Activate an issuing authority certificate authority (IACA) certificate
This Java sample shows how to use the BlankRootCACertificate_PathLen0_APIPassthrough/V1 definition template to create and
install an ISO/IEC mDL
standardKeyUsage
,
IssuerAlternativeName
, and CRLDistributionPoint
, and pass them
through CustomExtensions
.
Note
The IACA link certificate establishes a trust path from the old IACA root certificate
to the new IACA root certificate. The issuing authority can generate and distribute an
IACA link certificate during the IACA re-key process. You cannot issue an IACA link
certificate by using an IACA root certificate with pathLen=0
set.
The example calls the following Amazon Private CA API actions:
package com.amazonaws.samples.mdl; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; 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.CertificateAuthorityConfiguration; import com.amazonaws.services.acmpca.model.CertificateAuthorityType; import com.amazonaws.services.acmpca.model.CreateCertificateAuthorityResult; import com.amazonaws.services.acmpca.model.CreateCertificateAuthorityRequest; import com.amazonaws.services.acmpca.model.CustomExtension; import com.amazonaws.services.acmpca.model.Extensions; import com.amazonaws.services.acmpca.model.KeyAlgorithm; import com.amazonaws.services.acmpca.model.SigningAlgorithm; 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.model.GetCertificateAuthorityCsrRequest; import com.amazonaws.services.acmpca.model.GetCertificateAuthorityCsrResult; import com.amazonaws.services.acmpca.model.GetCertificateRequest; import com.amazonaws.services.acmpca.model.GetCertificateResult; import com.amazonaws.services.acmpca.model.ImportCertificateAuthorityCertificateRequest; import com.amazonaws.services.acmpca.model.IssueCertificateRequest; import com.amazonaws.services.acmpca.model.IssueCertificateResult; import com.amazonaws.services.acmpca.model.Validity; import com.amazonaws.AmazonClientException; import com.amazonaws.services.acmpca.model.CertificateMismatchException; import com.amazonaws.services.acmpca.model.ConcurrentModificationException; import com.amazonaws.services.acmpca.model.LimitExceededException; import com.amazonaws.services.acmpca.model.InvalidArgsException; import com.amazonaws.services.acmpca.model.InvalidArnException; import com.amazonaws.services.acmpca.model.InvalidPolicyException; import com.amazonaws.services.acmpca.model.InvalidStateException; import com.amazonaws.services.acmpca.model.MalformedCertificateException; import com.amazonaws.services.acmpca.model.MalformedCSRException; import com.amazonaws.services.acmpca.model.RequestFailedException; import com.amazonaws.services.acmpca.model.RequestInProgressException; import com.amazonaws.services.acmpca.model.ResourceNotFoundException; import com.amazonaws.services.acmpca.model.AWSACMPCAException; import com.amazonaws.waiters.Waiter; import com.amazonaws.waiters.WaiterParameters; import com.amazonaws.waiters.WaiterTimedOutException; import com.amazonaws.waiters.WaiterUnrecoverableException; 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; import lombok.SneakyThrows; public class IssuingAuthorityCertificateAuthorityActivation { public static void main(String[] args) throws Exception { // Define the endpoint region for your sample. String endpointRegion = null; // Substitute your region here, e.g. "ap-southeast-2" if (endpointRegion == null) throw new Exception("Region cannot be null"); // Define a CA subject. ASN1Subject subject = new ASN1Subject() .withCountry("US") // mDL spec requires ISO 3166-1-alpha-2 country code e.g. "US" .withCommonName("mDL Test IACA"); // Define the CA configuration. CertificateAuthorityConfiguration configCA = new CertificateAuthorityConfiguration() .withKeyAlgorithm(KeyAlgorithm.EC_prime256v1) .withSigningAlgorithm(SigningAlgorithm.SHA256WITHECDSA) .withSubject(subject); // Define a certificate authority type CertificateAuthorityType CAType = CertificateAuthorityType.ROOT; // Execute core code samples for Root CA activation in sequence AWSACMPCA client = buildClient(endpointRegion); String rootCAArn = createCertificateAuthority(configCA, CAType, client); String csr = getCertificateAuthorityCsr(rootCAArn, client); String rootCertificateArn = issueCertificate(rootCAArn, csr, client); String rootCertificate = getCertificate(rootCertificateArn, rootCAArn, client); importCertificateAuthorityCertificate(rootCertificate, rootCAArn, client); } private static AWSACMPCA buildClient(String endpointRegion) { // 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. AWSACMPCA client = AWSACMPCAClientBuilder.standard() .withRegion(endpointRegion) .withCredentials(new AWSStaticCredentialsProvider(credentials)) .build(); return client; } private static String createCertificateAuthority(CertificateAuthorityConfiguration configCA, CertificateAuthorityType CAtype, AWSACMPCA client) { // Create the request object. CreateCertificateAuthorityRequest createCARequest = new CreateCertificateAuthorityRequest() .withCertificateAuthorityConfiguration(configCA) .withIdempotencyToken("123987") .withCertificateAuthorityType(CAtype); // Create the private CA. CreateCertificateAuthorityResult createCAResult = null; try { createCAResult = client.createCertificateAuthority(createCARequest); } catch (InvalidArgsException ex) { throw ex; } catch (InvalidPolicyException ex) { throw ex; } catch (LimitExceededException ex) { throw ex; } // Get the ARN of the private CA. String rootCAArn = createCAResult.getCertificateAuthorityArn(); System.out.println("Issuing Authority Certificate Authority (IACA) Arn: " + rootCAArn); return rootCAArn; } private static String getCertificateAuthorityCsr(String rootCAArn, AWSACMPCA client) { // Create the CSR request object and set the CA ARN. GetCertificateAuthorityCsrRequest csrRequest = new GetCertificateAuthorityCsrRequest() .withCertificateAuthorityArn(rootCAArn); // Create waiter to wait on successful creation of the CSR file. Waiter<GetCertificateAuthorityCsrRequest> getCSRWaiter = client.waiters().certificateAuthorityCSRCreated(); try { getCSRWaiter.run(new WaiterParameters<>(csrRequest)); } catch (WaiterUnrecoverableException e) { // Explicit short circuit when the recourse transitions into // an undesired state. } catch (WaiterTimedOutException e) { // Failed to transition into desired state even after polling. } catch (AWSACMPCAException e) { // Unexpected service exception. } // Get the CSR. GetCertificateAuthorityCsrResult csrResult = null; try { csrResult = client.getCertificateAuthorityCsr(csrRequest); } catch (RequestInProgressException ex) { throw ex; } catch (ResourceNotFoundException ex) { throw ex; } catch (InvalidArnException ex) { throw ex; } catch (RequestFailedException ex) { throw ex; } // Get and display the CSR; String csr = csrResult.getCsr(); System.out.println("CSR:"); System.out.println(csr); return csr; } @SneakyThrows private static String issueCertificate(String rootCAArn, String csr, AWSACMPCA client) { IssueCertificateRequest issueRequest = new IssueCertificateRequest() .withCertificateAuthorityArn(rootCAArn) .withTemplateArn("arn:aws:acm-pca:::template/BlankRootCACertificate_PathLen0_APIPassthrough/V1") .withSigningAlgorithm(SigningAlgorithm.SHA256WITHECDSA) .withIdempotencyToken("1234"); // Set the CSR. ByteBuffer csrByteBuffer = stringToByteBuffer(csr); issueRequest.setCsr(csrByteBuffer); // Set the validity period for the certificate to be issued. Validity validity = new Validity() .withValue(3650L) .withType("DAYS"); issueRequest.setValidity(validity); // Generate base64 encoded extension value for KeyUsage KeyUsage keyUsage = new KeyUsage(X509KeyUsage.keyCertSign + X509KeyUsage.cRLSign); byte[] kuBytes = keyUsage.getEncoded(); String base64EncodedKUValue = Base64.getEncoder().encodeToString(kuBytes); CustomExtension keyUsageCustomExtension = 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 // Add custom extension to api-passthrough Extensions extensions = new Extensions() .withCustomExtensions(Arrays.asList(keyUsageCustomExtension, ianCustomExtension, cdpCustomExtension)); ApiPassthrough apiPassthrough = new ApiPassthrough() .withExtensions(extensions); issueRequest.setApiPassthrough(apiPassthrough); // Issue the certificate. IssueCertificateResult issueResult = null; try { issueResult = client.issueCertificate(issueRequest); } 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 rootCertificateArn = issueResult.getCertificateArn(); System.out.println("mDL IACA Certificate Arn: " + rootCertificateArn); return rootCertificateArn; } private static String getCertificate(String rootCertificateArn, String rootCAArn, AWSACMPCA client) { // Create a request object. GetCertificateRequest certificateRequest = new GetCertificateRequest() .withCertificateArn(rootCertificateArn) .withCertificateAuthorityArn(rootCAArn); // Create waiter to wait on successful creation of the certificate file. Waiter<GetCertificateRequest> getCertificateWaiter = client.waiters().certificateIssued(); try { getCertificateWaiter.run(new WaiterParameters<>(certificateRequest)); } catch (WaiterUnrecoverableException e) { // Explicit short circuit when the recourse transitions into // an undesired state. } catch (WaiterTimedOutException e) { // Failed to transition into desired state even after polling. } catch (AWSACMPCAException e) { // Unexpected service exception. } // Get the certificate and certificate chain. GetCertificateResult certificateResult = null; try { certificateResult = client.getCertificate(certificateRequest); } catch (RequestInProgressException ex) { throw ex; } catch (RequestFailedException ex) { throw ex; } catch (ResourceNotFoundException ex) { throw ex; } catch (InvalidArnException ex) { throw ex; } catch (InvalidStateException ex) { throw ex; } // Get the certificate and certificate chain and display the result. String rootCertificate = certificateResult.getCertificate(); System.out.println(rootCertificate); return rootCertificate; } private static void importCertificateAuthorityCertificate(String rootCertificate, String rootCAArn, AWSACMPCA client) { // Create the request object and set the signed certificate, chain and CA ARN. ImportCertificateAuthorityCertificateRequest importRequest = new ImportCertificateAuthorityCertificateRequest() .withCertificateChain(null) .withCertificateAuthorityArn(rootCAArn); ByteBuffer certByteBuffer = stringToByteBuffer(rootCertificate); importRequest.setCertificate(certByteBuffer); // Import the certificate. try { client.importCertificateAuthorityCertificate(importRequest); } catch (CertificateMismatchException ex) { throw ex; } catch (MalformedCertificateException ex) { throw ex; } catch (InvalidArnException ex) { throw ex; } catch (ResourceNotFoundException ex) { throw ex; } catch (RequestInProgressException ex) { throw ex; } catch (ConcurrentModificationException ex) { throw ex; } catch (RequestFailedException ex) { throw ex; } System.out.println("Root CA certificate successfully imported."); System.out.println("Root CA activated successfully."); } private static ByteBuffer stringToByteBuffer(final String string) { if (Objects.isNull(string)) { return null; } byte[] bytes = string.getBytes(StandardCharsets.UTF_8); return ByteBuffer.wrap(bytes); } }