Create a document signer certificate - AWS Private Certificate Authority

Create a document signer certificate

This Java sample shows how to use the BlankEndEntityCertificate_APIPassthrough/V1 template to create a ISO/IEC mDL standard-compliant document signer certificate. You must generate base64-encoded values for KeyUsage, IssuerAlternativeName, and CRLDistributionPoint and pass them through CustomExtensions.

The example calls the following AWS Private CA API action:

package com.amazonaws.samples.mdl; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; import java.util.Objects; import com.amazonaws.services.acmpca.AWSACMPCA; import com.amazonaws.services.acmpca.AWSACMPCAClientBuilder; import com.amazonaws.services.acmpca.model.ASN1Subject; import com.amazonaws.services.acmpca.model.ApiPassthrough; import com.amazonaws.services.acmpca.model.ExtendedKeyUsage; import com.amazonaws.services.acmpca.model.CustomExtension; import com.amazonaws.services.acmpca.model.Extensions; import com.amazonaws.services.acmpca.model.IssueCertificateRequest; import com.amazonaws.services.acmpca.model.IssueCertificateResult; import com.amazonaws.services.acmpca.model.SigningAlgorithm; import com.amazonaws.services.acmpca.model.Validity; import com.amazonaws.AmazonClientException; import com.amazonaws.services.acmpca.model.LimitExceededException; import com.amazonaws.services.acmpca.model.ResourceNotFoundException; import com.amazonaws.services.acmpca.model.InvalidStateException; import com.amazonaws.services.acmpca.model.InvalidArnException; import com.amazonaws.services.acmpca.model.InvalidArgsException; import com.amazonaws.services.acmpca.model.MalformedCSRException; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.DistributionPoint; import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.jce.X509KeyUsage; public class IssueDocumentSignerCertificate { public static ByteBuffer stringToByteBuffer(final String string) { if (Objects.isNull(string)) { return null; } byte[] bytes = string.getBytes(StandardCharsets.UTF_8); return ByteBuffer.wrap(bytes); } public static void main(String[] args) throws Exception { // Get your credentials from the C:\Users\name\.aws\credentials file // in Windows or the .aws/credentials file in Linux. AWSCredentials credentials = null; try { credentials = new ProfileCredentialsProvider("default").getCredentials(); } catch (Exception e) { throw new AmazonClientException("Cannot load your credentials from disk", e); } // Create a client that you can use to make requests. String endpointRegion = null; // Substitute your region here, e.g. "ap-southeast-2" if (endpointRegion == null) throw new Exception("Region cannot be null"); AWSACMPCA client = AWSACMPCAClientBuilder.standard() .withRegion(endpointRegion) .withCredentials(new AWSStaticCredentialsProvider(credentials)) .build(); // Create a certificate request: String caArn = null; if (caArn == null) throw new Exception("Certificate authority ARN cannot be null"); IssueCertificateRequest req = new IssueCertificateRequest() .withCertificateAuthorityArn(caArn) .withTemplateArn("arn:aws:acm-pca:::template/BlankEndEntityCertificate_APIPassthrough/V1") .withSigningAlgorithm(SigningAlgorithm.SHA256WITHECDSA) .withIdempotencyToken("1234"); // Specify the certificate signing request (CSR) for the certificate to be signed and issued. // Format: "-----BEGIN CERTIFICATE REQUEST-----\n" + // "base64-encoded certificate\n" + // "-----END CERTIFICATE REQUEST-----\n"; String strCSR = null; if (strCSR == null) throw new Exception("CSR string cannot be null"); ByteBuffer csrByteBuffer = stringToByteBuffer(strCSR); req.setCsr(csrByteBuffer); // Set the validity period for the certificate to be issued. Validity validity = new Validity() .withValue(365L) .withType("DAYS"); req.setValidity(validity); // Define a cert subject. ASN1Subject subject = new ASN1Subject() .withCountry("US") // mDL spec requires ISO 3166-1-alpha-2 country code e.g. "US" .withCommonName("mDL Test DS"); ApiPassthrough apiPassthrough = new ApiPassthrough() .withSubject(subject); // Generate base64 encoded extension value for KeyUsage KeyUsage keyUsage = new KeyUsage(X509KeyUsage.digitalSignature); byte[] kuBytes = keyUsage.getEncoded(); String base64EncodedKUValue = Base64.getEncoder().encodeToString(kuBytes); CustomExtension customKeyUsageExtension = new CustomExtension() .withObjectIdentifier("2.5.29.15") // KeyUsage Extension OID .withValue(base64EncodedKUValue) .withCritical(true); // Generate base64 encoded extension value for IssuerAlternativeName GeneralNames issuerAlternativeName = new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, "https://issuer-alternative-name.com")); String base64EncodedIANValue = Base64.getEncoder().encodeToString(issuerAlternativeName.getEncoded()); CustomExtension ianCustomExtension = new CustomExtension() .withValue(base64EncodedIANValue) .withObjectIdentifier("2.5.29.18"); // IssuerAlternativeName Extension OID // Generate base64 encoded extension value for CRLDistributionPoint CRLDistPoint crlDistPoint = new CRLDistPoint(new DistributionPoint[]{new DistributionPoint(new DistributionPointName( new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, "dummycrl.crl"))), null, null)}); String base64EncodedCDPValue = Base64.getEncoder().encodeToString(crlDistPoint.getEncoded()); CustomExtension cdpCustomExtension = new CustomExtension() .withValue(base64EncodedCDPValue) .withObjectIdentifier("2.5.29.31"); // CRLDistributionPoint Extension OID // Generate EKU ExtendedKeyUsage eku = new ExtendedKeyUsage() .withExtendedKeyUsageObjectIdentifier("1.0.18013.5.1.2"); // EKU value reserved for mDL DS // Set KeyUsage, ExtendedKeyUsage, IssuerAlternativeName, CRL Distribution Point extensions to api-passthrough Extensions extensions = new Extensions() .withCustomExtensions(Arrays.asList(customKeyUsageExtension, ianCustomExtension, cdpCustomExtension)) .withExtendedKeyUsage(Arrays.asList(eku)); apiPassthrough.setExtensions(extensions); req.setApiPassthrough(apiPassthrough); // Issue the certificate. IssueCertificateResult result = null; try { result = client.issueCertificate(req); } catch (LimitExceededException ex) { throw ex; } catch (ResourceNotFoundException ex) { throw ex; } catch (InvalidStateException ex) { throw ex; } catch (InvalidArnException ex) { throw ex; } catch (InvalidArgsException ex) { throw ex; } catch (MalformedCSRException ex) { throw ex; } // Get and display the certificate ARN. String arn = result.getCertificateArn(); System.out.println("mDL DS Certificate Arn: " + arn); } }