mirror of https://github.com/shred/acme4j
Workaround for ssl.com metadata bug
ssl.com requires EAB for account creation, but the metadata's "externalAccountRequired" property gives "false", indicating that no EAB is used. This fix patches the read directory's metadata if the ssl.com provider is used.pull/168/head
parent
081e53f137
commit
908e11b152
|
@ -16,10 +16,14 @@ package org.shredzone.acme4j.provider.sslcom;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.exception.AcmeException;
|
||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||
import org.shredzone.acme4j.provider.AbstractAcmeProvider;
|
||||
import org.shredzone.acme4j.provider.AcmeProvider;
|
||||
import org.shredzone.acme4j.toolbox.JSON;
|
||||
|
||||
/**
|
||||
* An {@link AcmeProvider} for <em>SSL.com</em>.
|
||||
|
@ -68,4 +72,21 @@ public class SslComAcmeProvider extends AbstractAcmeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSON directory(Session session, URI serverUri) throws AcmeException {
|
||||
// This is a workaround for a bug at SSL.com. It requires account registration
|
||||
// by EAB, but the "externalAccountRequired" flag in the directory is set to
|
||||
// false. This patch reads the directory and forcefully sets the flag to true.
|
||||
// The entire method can be removed once it is fixed on SSL.com side.
|
||||
var directory = super.directory(session, serverUri).toMap();
|
||||
var meta = directory.get("meta");
|
||||
if (meta instanceof Map) {
|
||||
var metaMap = ((Map<String, Object>) meta);
|
||||
metaMap.remove("externalAccountRequired");
|
||||
metaMap.put("externalAccountRequired", true);
|
||||
}
|
||||
return JSON.fromMap(directory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -118,6 +118,21 @@ public final class JSON implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON object from a map.
|
||||
* <p>
|
||||
* The map's content is deeply copied. Changes to the map won't reflect in the created
|
||||
* JSON structure.
|
||||
*
|
||||
* @param data
|
||||
* Map structure
|
||||
* @return {@link JSON} of the map's content.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public static JSON fromMap(Map<String, Object> data) {
|
||||
return JSON.parse(JsonUtil.toJson(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link JSON} of an empty document.
|
||||
*
|
||||
|
|
|
@ -74,26 +74,33 @@ public class ProviderIT {
|
|||
var sessionEcc = new Session("acme://ssl.com/ecc");
|
||||
assertThat(sessionEcc.getMetadata().getWebsite()).hasValue(new URL("https://www.ssl.com"));
|
||||
assertThatNoException().isThrownBy(() -> sessionEcc.resourceUrl(Resource.NEW_ACCOUNT));
|
||||
assertThat(sessionEcc.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
assertThat(sessionEcc.getMetadata().isExternalAccountRequired()).isTrue();
|
||||
assertThat(sessionEcc.getMetadata().isAutoRenewalEnabled()).isFalse();
|
||||
|
||||
var sessionRsa = new Session("acme://ssl.com/rsa");
|
||||
assertThat(sessionRsa.getMetadata().getWebsite()).hasValue(new URL("https://www.ssl.com"));
|
||||
assertThatNoException().isThrownBy(() -> sessionRsa.resourceUrl(Resource.NEW_ACCOUNT));
|
||||
assertThat(sessionRsa.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
assertThat(sessionRsa.getMetadata().isExternalAccountRequired()).isTrue();
|
||||
assertThat(sessionRsa.getMetadata().isAutoRenewalEnabled()).isFalse();
|
||||
|
||||
var sessionEccStage = new Session("acme://ssl.com/staging/ecc");
|
||||
assertThat(sessionEccStage.getMetadata().getWebsite()).hasValue(new URL("https://www.ssl.com"));
|
||||
assertThatNoException().isThrownBy(() -> sessionEccStage.resourceUrl(Resource.NEW_ACCOUNT));
|
||||
assertThat(sessionEccStage.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
assertThat(sessionEccStage.getMetadata().isExternalAccountRequired()).isTrue();
|
||||
assertThat(sessionEccStage.getMetadata().isAutoRenewalEnabled()).isFalse();
|
||||
|
||||
var sessionRsaStage = new Session("acme://ssl.com/staging/rsa");
|
||||
assertThat(sessionRsaStage.getMetadata().getWebsite()).hasValue(new URL("https://www.ssl.com"));
|
||||
assertThatNoException().isThrownBy(() -> sessionRsaStage.resourceUrl(Resource.NEW_ACCOUNT));
|
||||
assertThat(sessionRsaStage.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
assertThat(sessionRsaStage.getMetadata().isExternalAccountRequired()).isTrue();
|
||||
assertThat(sessionRsaStage.getMetadata().isAutoRenewalEnabled()).isFalse();
|
||||
|
||||
// If these tests fail, the metadata have been fixed on server side. Then remove
|
||||
// the patch at ZeroSSLAcmeProvider, and update the documentation.
|
||||
var sessionEABCheck = new Session("https://acme.ssl.com/sslcom-dv-ecc");
|
||||
assertThat(sessionEABCheck.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
var sessionEABCheckStage = new Session("https://acme-try.ssl.com/sslcom-dv-ecc");
|
||||
assertThat(sessionEABCheckStage.getMetadata().isExternalAccountRequired()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,7 @@ Available since acme4j 3.2.0
|
|||
|
||||
## Note
|
||||
|
||||
* This CA requires [External Account Binding (EAB)](../usage/account.md#external-account-binding) for account creation. However `Metadata.isExternalAccountRequired()` returns `false` due to an error in the CA's directory resource.
|
||||
* This CA requires [External Account Binding (EAB)](../usage/account.md#external-account-binding) for account creation. However, the CA's directory resource returns `externalAccountRequired` as `false`, which is incorrect. If you use one of the `acme:` URIs above, _acme4j_ will patch the metadata transparently. If you directly connect to SSL.com via `https:` URI though, `Metadata.isExternalAccountRequired()` could return a wrong value. (As of February 2024)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
|
Loading…
Reference in New Issue