Create certificates with custom extensions - AWS Private Certificate Authority

Create certificates with custom extensions

The CustomExtension object allows administrators to set custom X.509 extensions in private certificates. Customized certificates must be created using one of the ApiPassthrough templates. For more information about templates, see Template varieties. For more information about using custom extensions, see Issuing private end-entity certificates.

Activate a subordinate CA with the NameConstraints extension

package com.amazonaws.samples; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; import com.amazonaws.auth.AWSStaticCredentialsProvider; import java.io.IOException; 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.IssueCertificateRequest; import com.amazonaws.services.acmpca.model.IssueCertificateResult; import com.amazonaws.services.acmpca.model.KeyAlgorithm; import com.amazonaws.services.acmpca.model.SigningAlgorithm; import com.amazonaws.services.acmpca.model.Validity; import com.amazonaws.waiters.Waiter; import com.amazonaws.waiters.WaiterParameters; import com.amazonaws.waiters.WaiterTimedOutException; import com.amazonaws.waiters.WaiterUnrecoverableException; import com.amazonaws.AmazonClientException; import com.amazonaws.services.acmpca.model.LimitExceededException; import com.amazonaws.services.acmpca.model.ResourceNotFoundException; import com.amazonaws.services.acmpca.model.RevocationConfiguration; import com.amazonaws.services.acmpca.model.InvalidStateException; import com.amazonaws.services.acmpca.model.InvalidArnException; import com.amazonaws.services.acmpca.model.InvalidPolicyException; import com.amazonaws.services.acmpca.model.ASN1Subject; import com.amazonaws.services.acmpca.model.AWSACMPCAException; 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.CertificateMismatchException; import com.amazonaws.services.acmpca.model.ConcurrentModificationException; import com.amazonaws.services.acmpca.model.CreateCertificateAuthorityRequest; import com.amazonaws.services.acmpca.model.CreateCertificateAuthorityResult; import com.amazonaws.services.acmpca.model.CrlConfiguration; import com.amazonaws.services.acmpca.model.CustomExtension; import com.amazonaws.services.acmpca.model.Extensions; import com.amazonaws.services.acmpca.model.GetCertificateAuthorityCertificateRequest; import com.amazonaws.services.acmpca.model.GetCertificateAuthorityCertificateResult; 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.InvalidArgsException; import com.amazonaws.services.acmpca.model.MalformedCSRException; import com.amazonaws.services.acmpca.model.MalformedCertificateException; import com.amazonaws.services.acmpca.model.RequestFailedException; import com.amazonaws.services.acmpca.model.RequestInProgressException; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralSubtree; import org.bouncycastle.asn1.x509.NameConstraints; import lombok.SneakyThrows; public class SubordinateCAActivationWithNameConstraints { public static void main(String[] args) throws Exception { // Place your own Root CA ARN here. String rootCAArn = "arn:aws:acm-pca:region:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012"; // Define the endpoint region for your sample. String endpointRegion = "us-west-2"; // Substitute your region here, e.g. "us-west-2" // Define a CA subject. ASN1Subject subject = new ASN1Subject(); subject.setOrganization("Example Organization"); subject.setOrganizationalUnit("Example"); subject.setCountry("US"); subject.setState("Virginia"); subject.setLocality("Arlington"); subject.setCommonName("SubordinateCA"); // Define the CA configuration. CertificateAuthorityConfiguration configCA = new CertificateAuthorityConfiguration(); configCA.withKeyAlgorithm(KeyAlgorithm.RSA_2048); configCA.withSigningAlgorithm(SigningAlgorithm.SHA256WITHRSA); configCA.withSubject(subject); // Define a certificate revocation list configuration. CrlConfiguration crlConfigure = new CrlConfiguration(); crlConfigure.withEnabled(true); crlConfigure.withExpirationInDays(365); crlConfigure.withCustomCname(null); crlConfigure.withS3BucketName("your-bucket-name"); // Define a certificate authority type CertificateAuthorityType caType = CertificateAuthorityType.SUBORDINATE; // ** Execute core code samples for Subordinate CA activation in sequence ** AWSACMPCA client = ClientBuilder(endpointRegion); String rootCertificate = GetCertificateAuthorityCertificate(rootCAArn, client); String subordinateCAArn = CreateCertificateAuthority(configCA, crlConfigure, caType, client); String csr = GetCertificateAuthorityCsr(subordinateCAArn, client); String subordinateCertificateArn = IssueCertificate(rootCAArn, csr, client); String subordinateCertificate = GetCertificate(subordinateCertificateArn, rootCAArn, client); ImportCertificateAuthorityCertificate(subordinateCertificate, rootCertificate, subordinateCAArn, client); } private static AWSACMPCA ClientBuilder(String endpointRegion) { // Retrieve 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 the credentials from the credential profiles file. " + "Please make sure that your credentials file is at the correct " + "location (C:\\Users\\joneps\\.aws\\credentials), and is in valid format.", e); } String endpointProtocol = "https://acm-pca." + endpointRegion + ".amazonaws.com/"; EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(endpointProtocol, endpointRegion); // Create a client that you can use to make requests. AWSACMPCA client = AWSACMPCAClientBuilder.standard() .withEndpointConfiguration(endpoint) .withCredentials(new AWSStaticCredentialsProvider(credentials)) .build(); return client; } private static String GetCertificateAuthorityCertificate(String rootCAArn, AWSACMPCA client) { // ** GetCertificateAuthorityCertificate ** // Create a request object and set the certificate authority ARN, GetCertificateAuthorityCertificateRequest getCACertificateRequest = new GetCertificateAuthorityCertificateRequest(); getCACertificateRequest.withCertificateAuthorityArn(rootCAArn); // Create a result object. GetCertificateAuthorityCertificateResult getCACertificateResult = null; try { getCACertificateResult = client.getCertificateAuthorityCertificate(getCACertificateRequest); } catch (ResourceNotFoundException ex) { throw ex; } catch (InvalidStateException ex) { throw ex; } catch (InvalidArnException ex) { throw ex; } // Retrieve and display the certificate information. String rootCertificate = getCACertificateResult.getCertificate(); System.out.println("Root CA Certificate / Certificate Chain:"); System.out.println(rootCertificate); return rootCertificate; } private static String CreateCertificateAuthority(CertificateAuthorityConfiguration configCA, CrlConfiguration crlConfigure, CertificateAuthorityType caType, AWSACMPCA client) { RevocationConfiguration revokeConfig = new RevocationConfiguration(); revokeConfig.setCrlConfiguration(crlConfigure); // Create the request object. CreateCertificateAuthorityRequest createCARequest = new CreateCertificateAuthorityRequest(); createCARequest.withCertificateAuthorityConfiguration(configCA); createCARequest.withRevocationConfiguration(revokeConfig); createCARequest.withIdempotencyToken("1234"); createCARequest.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; } // Retrieve the ARN of the private CA. String subordinateCAArn = createCAResult.getCertificateAuthorityArn(); System.out.println("Subordinate CA Arn: " + subordinateCAArn); return subordinateCAArn; } private static String GetCertificateAuthorityCsr(String subordinateCAArn, AWSACMPCA client) { // Create the CSR request object and set the CA ARN. GetCertificateAuthorityCsrRequest csrRequest = new GetCertificateAuthorityCsrRequest(); csrRequest.withCertificateAuthorityArn(subordinateCAArn); // 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. } // Retrieve 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; } // Retrieve and display the CSR; String csr = csrResult.getCsr(); System.out.println("Subordinate CSR:"); System.out.println(csr); return csr; } private static String IssueCertificate(String rootCAArn, String csr, AWSACMPCA client) { // Create a certificate request: IssueCertificateRequest issueRequest = new IssueCertificateRequest(); // Set the issuing CA ARN. issueRequest.withCertificateAuthorityArn(rootCAArn); // Set the template ARN. issueRequest.withTemplateArn("arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0_APIPassthrough/V1"); ByteBuffer csrByteBuffer = stringToByteBuffer(csr); issueRequest.setCsr(csrByteBuffer); // Set the signing algorithm. issueRequest.withSigningAlgorithm(SigningAlgorithm.SHA256WITHRSA); // Set the validity period for the certificate to be issued. Validity validity = new Validity(); validity.withValue(100L); validity.withType("DAYS"); issueRequest.withValidity(validity); // Set the idempotency token. issueRequest.setIdempotencyToken("1234"); // Generate Base64 encoded Nameconstraints extension value String base64EncodedExtValue = getNameConstraintExtensionValue(); // Generate custom extension CustomExtension customExtension = new CustomExtension(); customExtension.setCritical(true); customExtension.setObjectIdentifier("2.5.29.30"); // NameConstraints Extension OID customExtension.setValue(base64EncodedExtValue); // Add custom extension to api-passthrough ApiPassthrough apiPassthrough = new ApiPassthrough(); Extensions extensions = new Extensions(); extensions.setCustomExtensions(Arrays.asList(customExtension)); apiPassthrough.setExtensions(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; } // Retrieve and display the certificate ARN. String subordinateCertificateArn = issueResult.getCertificateArn(); System.out.println("Subordinate Certificate Arn: " + subordinateCertificateArn); return subordinateCertificateArn; } @SneakyThrows private static String getNameConstraintExtensionValue() { // Generate Base64 encoded Nameconstraints extension value GeneralSubtree dnsPrivate = new GeneralSubtree(new GeneralName(GeneralName.dNSName, ".private")); GeneralSubtree dnsLocal = new GeneralSubtree(new GeneralName(GeneralName.dNSName, ".local")); GeneralSubtree dnsCorp = new GeneralSubtree(new GeneralName(GeneralName.dNSName, ".corp")); GeneralSubtree dnsSecretCorp = new GeneralSubtree(new GeneralName(GeneralName.dNSName, ".secret.corp")); GeneralSubtree dnsExample = new GeneralSubtree(new GeneralName(GeneralName.dNSName, ".example.com")); GeneralSubtree[] permittedSubTree = new GeneralSubtree[] { dnsPrivate, dnsLocal, dnsCorp }; GeneralSubtree[] excludedSubTree = new GeneralSubtree[] { dnsSecretCorp, dnsExample }; NameConstraints nameConstraints = new NameConstraints(permittedSubTree, excludedSubTree); return new String(Base64.getEncoder().encode(nameConstraints.getEncoded())); } private static String GetCertificate(String subordinateCertificateArn, String rootCAArn, AWSACMPCA client) { // Create a request object. GetCertificateRequest certificateRequest = new GetCertificateRequest(); // Set the certificate ARN. certificateRequest.withCertificateArn(subordinateCertificateArn); // Set the certificate authority ARN. certificateRequest.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. } // Retrieve 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 subordinateCertificate = certificateResult.getCertificate(); System.out.println("Subordinate CA Certificate:"); System.out.println(subordinateCertificate); return subordinateCertificate; } private static void ImportCertificateAuthorityCertificate(String subordinateCertificate, String rootCertificate, String subordinateCAArn, AWSACMPCA client) { // Create the request object and set the signed certificate, chain and CA ARN. ImportCertificateAuthorityCertificateRequest importRequest = new ImportCertificateAuthorityCertificateRequest(); ByteBuffer certByteBuffer = stringToByteBuffer(subordinateCertificate); importRequest.setCertificate(certByteBuffer); ByteBuffer rootCACertByteBuffer = stringToByteBuffer(rootCertificate); importRequest.setCertificateChain(rootCACertByteBuffer); // Set the certificate authority ARN. importRequest.withCertificateAuthorityArn(subordinateCAArn); // 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("Subordinate CA certificate successfully imported."); System.out.println("Subordinate 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); } }

Issue a certificate with the QC statement extension

package com.amazonaws.samples; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; 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.ApiPassthrough; 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.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.x509.qualified.ETSIQCObjectIdentifiers; import org.bouncycastle.asn1.x509.qualified.QCStatement; import lombok.SneakyThrows; public class IssueCertificateWithQCStatement { private static ByteBuffer stringToByteBuffer(final String string) { if (Objects.isNull(string)) { return null; } byte[] bytes = string.getBytes(StandardCharsets.UTF_8); return ByteBuffer.wrap(bytes); } @SneakyThrows private static String generateQCStatementBase64ExtValue() { DERSequence qcTypeSeq = new DERSequence(ETSIQCObjectIdentifiers.id_etsi_qct_web); QCStatement qcType = new QCStatement(ETSIQCObjectIdentifiers.id_etsi_qcs_QcType, qcTypeSeq); ASN1EncodableVector pspAIVector = new ASN1EncodableVector(2); pspAIVector.add(new ASN1ObjectIdentifier("0.4.0.19495.1.3")); pspAIVector.add(new DERUTF8String("PSP_AI")); DERSequence pspAISeq = new DERSequence(pspAIVector); ASN1EncodableVector pspASVector = new ASN1EncodableVector(2); pspASVector.add(new ASN1ObjectIdentifier("0.4.0.19495.1.1")); pspASVector.add(new DERUTF8String("PSP_AS")); DERSequence pspASSeq = new DERSequence(pspASVector); ASN1EncodableVector pspPIVector = new ASN1EncodableVector(2); pspPIVector.add(new ASN1ObjectIdentifier("0.4.0.19495.1.2")); pspPIVector.add(new DERUTF8String("PSP_PI")); DERSequence pspPISeq = new DERSequence(pspPIVector); ASN1EncodableVector pspICVector = new ASN1EncodableVector(2); pspICVector.add(new ASN1ObjectIdentifier("0.4.0.19495.1.4")); pspICVector.add(new DERUTF8String("PSP_IC")); DERSequence pspICSeq = new DERSequence(pspICVector); ASN1EncodableVector pspSeqVector = new ASN1EncodableVector(4); pspSeqVector.add(pspPISeq); pspSeqVector.add(pspICSeq); pspSeqVector.add(pspASSeq); pspSeqVector.add(pspAISeq); DERSequence pspSeq = new DERSequence(pspSeqVector); ASN1EncodableVector pspVector = new ASN1EncodableVector(3); pspVector.add(pspSeq); pspVector.add(new DERUTF8String("Your Financial Authority")); pspVector.add(new DERUTF8String("AB-CD")); DERSequence psp = new DERSequence(pspVector); QCStatement qcPSP = new QCStatement(new ASN1ObjectIdentifier("0.4.0.19495.2"), psp); DERSequence qcSeq = new DERSequence(new QCStatement[] { qcType, qcPSP }); byte[] qcExtValueInBytes = qcSeq.getEncoded(); return Base64.getEncoder().encodeToString(qcExtValueInBytes); } public static void main(String[] args) throws Exception { // Retrieve 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); } // Define the endpoint for your sample. String endpointRegion = "us-west-2"; // Substitute your region here, e.g. "us-west-2" String endpointProtocol = "https://acm-pca." + endpointRegion + ".amazonaws.com/"; EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(endpointProtocol, endpointRegion); // Create a client that you can use to make requests. AWSACMPCA client = AWSACMPCAClientBuilder.standard() .withEndpointConfiguration(endpoint) .withCredentials(new AWSStaticCredentialsProvider(credentials)) .build(); // Create a certificate request: IssueCertificateRequest req = new IssueCertificateRequest(); // Set the CA ARN. req.withCertificateAuthorityArn("arn:aws:acm-pca:region:account:" + "certificate-authority/12345678-1234-1234-1234-123456789012"); // Specify the certificate signing request (CSR) for the certificate to be signed and issued. String strCSR = "-----BEGIN CERTIFICATE REQUEST-----\n" + "base64-encoded CSR\n" + "-----END CERTIFICATE REQUEST-----\n"; ByteBuffer csrByteBuffer = stringToByteBuffer(strCSR); req.setCsr(csrByteBuffer); // Specify the template for the issued certificate. req.withTemplateArn("arn:aws:acm-pca:::template/EndEntityCertificate_APIPassthrough/V1"); // Set the signing algorithm. req.withSigningAlgorithm(SigningAlgorithm.SHA256WITHRSA); // Set the validity period for the certificate to be issued. Validity validity = new Validity(); validity.withValue(30L); validity.withType("DAYS"); req.withValidity(validity); // Set the idempotency token. req.setIdempotencyToken("1234"); // Generate Base64 encoded extension value for QC Statement String base64EncodedExtValue = generateQCStatementBase64ExtValue(); // Generate custom extension CustomExtension customExtension = new CustomExtension(); customExtension.setObjectIdentifier("1.3.6.1.5.5.7.1.3"); // QC Statement Extension OID customExtension.setValue(base64EncodedExtValue); // Add custom extension to api-passthrough ApiPassthrough apiPassthrough = new ApiPassthrough(); Extensions extensions = new Extensions(); extensions.setCustomExtensions(Arrays.asList(customExtension)); 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; } // Retrieve and display the certificate ARN. String arn = result.getCertificateArn(); System.out.println(arn); } }