diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java index ee4f5a7a..baf5cfe1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java @@ -125,14 +125,14 @@ public interface CertificateRepository extends JpaRepository /** * Query that retrieves a list of issued attestation certificates using the provided device id, - * isLDevID value and sort value. + * ldevID value and sort value. * * @param deviceId device id - * @param isLDevID is it a LDevId + * @param ldevID is it a LDevId * @param sort sort * @return a list of issued attestation certificates */ - List findByDeviceIdAndIsLDevID(UUID deviceId, boolean isLDevID, Sort sort); + List findByDeviceIdAndLdevID(UUID deviceId, boolean ldevID, Sort sort); /** * Query that retrieves a certificates using the provided certificate hash. @@ -142,3 +142,4 @@ public interface CertificateRepository extends JpaRepository */ Certificate findByCertificateHash(int certificateHash); } + diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java index 891bf6b1..3ecdf827 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java @@ -29,7 +29,7 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { public static final String AIC_TYPE_LABEL = "TCPA Trusted Platform Identity"; @Column - private boolean isLDevID; + private boolean ldevID; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "ek_id") @@ -45,18 +45,18 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { * @param certificateBytes the issued certificate bytes * @param endorsementCredential the endorsement credential * @param platformCredentials the platform credentials - * @param isLDevID is LDevId + * @param ldevID is LDevID * @throws IOException if there is a problem extracting information from the certificate */ public IssuedAttestationCertificate(final byte[] certificateBytes, final EndorsementCredential endorsementCredential, final List platformCredentials, - final boolean isLDevID) + final boolean ldevID) throws IOException { super(certificateBytes); this.endorsementCredential = endorsementCredential; this.platformCredentials = new ArrayList<>(platformCredentials); - this.isLDevID = isLDevID; + this.ldevID = ldevID; } /** @@ -65,14 +65,14 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { * @param certificatePath path to certificate * @param endorsementCredential the endorsement credential * @param platformCredentials the platform credentials - * @param isLDevID is it an LDev ID + * @param ldevID is it an LDevID * @throws IOException if there is a problem extracting information from the certificate */ public IssuedAttestationCertificate(final Path certificatePath, final EndorsementCredential endorsementCredential, final List platformCredentials, - final boolean isLDevID) + final boolean ldevID) throws IOException { - this(readBytes(certificatePath), endorsementCredential, platformCredentials, isLDevID); + this(readBytes(certificatePath), endorsementCredential, platformCredentials, ldevID); } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java index 809e283d..087f4be1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java @@ -19,7 +19,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.log4j.Log4j2; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -85,7 +87,7 @@ public class AbstractProcessor { expiry.add(Calendar.DAY_OF_YEAR, getValidDays()); X500Name issuer = - new X500Name(acaCertificate.getSubjectX500Principal().getName()); + new X509CertificateHolder(acaCertificate.getEncoded()).getSubject(); Date notBefore = new Date(); Date notAfter = expiry.getTime(); BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); @@ -103,7 +105,7 @@ public class AbstractProcessor { endorsementCredential, platformCredentials, deviceName); Extension authKeyIdentifier = IssuedCertificateAttributeHelper - .buildAuthorityKeyIdentifier(endorsementCredential); + .buildAuthorityKeyIdentifier(acaCertificate); builder.addExtension(subjectAlternativeName); if (authKeyIdentifier != null) { @@ -118,6 +120,20 @@ public class AbstractProcessor { + "Unable to issue certificates"); } + // Add signing extension + builder.addExtension( + Extension.keyUsage, + true, + new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment) + ); + + // Basic constraints + builder.addExtension( + Extension.basicConstraints, + true, + new BasicConstraints(false) + ); + ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA") .setProvider("BC").build(getPrivateKey()); X509CertificateHolder holder = builder.build(signer); @@ -238,7 +254,7 @@ public class AbstractProcessor { * @param endorsementCredential the endorsement credential used to generate the AC * @param platformCredentials the platform credentials used to generate the AC * @param device the device to which the attestation certificate is tied - * @param isLDevID whether the certificate is a ldevid + * @param ldevID whether the certificate is a ldevid * @return whether the certificate was saved successfully * @throws {@link CertificateProcessingException} if error occurs in persisting the Attestation * Certificate @@ -248,7 +264,7 @@ public class AbstractProcessor { final EndorsementCredential endorsementCredential, final List platformCredentials, final Device device, - final boolean isLDevID) { + final boolean ldevID) { List issuedAc; boolean generateCertificate = true; PolicyRepository scp = getPolicyRepository(); @@ -258,27 +274,27 @@ public class AbstractProcessor { try { // save issued certificate IssuedAttestationCertificate attCert = new IssuedAttestationCertificate( - derEncodedAttestationCertificate, endorsementCredential, platformCredentials, isLDevID); + derEncodedAttestationCertificate, endorsementCredential, platformCredentials, ldevID); if (scp != null) { policySettings = scp.findByName("Default"); Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity"); - issuedAc = certificateRepository.findByDeviceIdAndIsLDevID(device.getId(), isLDevID, + issuedAc = certificateRepository.findByDeviceIdAndLdevID(device.getId(), ldevID, sortCriteria); - generateCertificate = isLDevID ? policySettings.isIssueDevIdCertificate() + generateCertificate = ldevID ? policySettings.isIssueDevIdCertificate() : policySettings.isIssueAttestationCertificate(); if (issuedAc != null && issuedAc.size() > 0 - && (isLDevID ? policySettings.isDevIdExpirationFlag() + && (ldevID ? policySettings.isDevIdExpirationFlag() : policySettings.isGenerateOnExpiration())) { if (issuedAc.get(0).getEndValidity().after(currentDate)) { // so the issued AC is not expired // however are we within the threshold days = ProvisionUtils.daysBetween(currentDate, issuedAc.get(0).getEndValidity()); generateCertificate = - days < Integer.parseInt(isLDevID ? policySettings.getDevIdReissueThreshold() + days < Integer.parseInt(ldevID ? policySettings.getDevIdReissueThreshold() : policySettings.getReissueThreshold()); } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/IssuedCertificateAttributeHelper.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/IssuedCertificateAttributeHelper.java index 6154d5f9..34e7db42 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/IssuedCertificateAttributeHelper.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/IssuedCertificateAttributeHelper.java @@ -21,6 +21,7 @@ import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.GeneralNamesBuilder; import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.TBSCertificate; import java.io.IOException; @@ -69,26 +70,28 @@ public final class IssuedCertificateAttributeHelper { * This method builds the AKI extension that will be stored in the generated * Attestation Issued Certificate. * - * @param endorsementCredential EK object to pull AKI from. + * @param acaCertificate ACA certificate to pull SKI from, that will be used to build matching AKI. * @return the AKI extension. - * @throws IOException on bad get instance for AKI. + * @throws IOException on bad get instance for SKI. */ public static Extension buildAuthorityKeyIdentifier( - final EndorsementCredential endorsementCredential) throws IOException { - if (endorsementCredential == null || endorsementCredential.getX509Certificate() == null) { + final X509Certificate acaCertificate) throws IOException { + if (acaCertificate == null) { return null; } - byte[] extValue = endorsementCredential.getX509Certificate() - .getExtensionValue(Extension.authorityKeyIdentifier.getId()); + byte[] extValue = acaCertificate + .getExtensionValue(Extension.subjectKeyIdentifier.getId()); if (extValue == null) { return null; } byte[] authExtension = ASN1OctetString.getInstance(extValue).getOctets(); - AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(authExtension); + SubjectKeyIdentifier ski = SubjectKeyIdentifier.getInstance(authExtension); - return new Extension(Extension.authorityKeyIdentifier, true, aki.getEncoded()); + AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(ski.getKeyIdentifier()); + + return new Extension(Extension.authorityKeyIdentifier, false, aki.getEncoded()); } /** diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java index c0deaec3..b72dec75 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java @@ -19,7 +19,12 @@ import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.TBSCertificate; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; @@ -33,7 +38,6 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.OAEPParameterSpec; import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; -import javax.security.auth.x500.X500Principal; import java.io.IOException; import java.math.BigInteger; import java.net.URISyntaxException; @@ -53,6 +57,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.security.spec.MGF1ParameterSpec; import java.util.Calendar; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -61,6 +66,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -376,6 +382,37 @@ public class AttestationCertificateAuthorityTest { verifyNoMoreInteractions(certificate, symmetricKey); } + /** + * Creates a self-signed X.509 public-key certificate. + * + * @param pair KeyPair to create the cert for + * @return self-signed X509Certificate + */ + private static X509Certificate createSelfSignedCertificate(final KeyPair pair) { + Security.addProvider(new BouncyCastleProvider()); + final int timeRange = 10000; + X509Certificate cert = null; + try { + + X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); + X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); + BigInteger serialNumber = BigInteger.ONE; + Date notBefore = new Date(System.currentTimeMillis() - timeRange); + Date notAfter = new Date(System.currentTimeMillis() + timeRange); + X509v3CertificateBuilder builder = + new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, + subjectName, pair.getPublic()); + ContentSigner signer = + new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( + pair.getPrivate()); + return new JcaX509CertificateConverter().setProvider("BC").getCertificate( + builder.build(signer)); + } catch (Exception e) { + fail("Exception occurred while creating a cert", e); + } + return cert; + } + /** * Tests {@link AttestationCertificateAuthority# * AttestationCertificateAuthority(SupplyChainValidationService, PrivateKey, @@ -390,14 +427,13 @@ public class AttestationCertificateAuthorityTest { final String identityProofLabelString = "label"; byte[] identityProofLabel = identityProofLabelString.getBytes(StandardCharsets.UTF_8); byte[] modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray(); - X500Principal principal = new X500Principal("CN=TEST, OU=TEST, O=TEST, C=TEST"); int validDays = 1; // create mocks for testing IdentityProof identityProof = mock(IdentityProof.class); AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class); StorePubKey storePubKey = mock(StorePubKey.class); - X509Certificate acaCertificate = mock(X509Certificate.class); + X509Certificate acaCertificate = createSelfSignedCertificate(keyPair); // assign ACA fields ReflectionTestUtils.setField(aca, "validDays", validDays); @@ -406,10 +442,6 @@ public class AttestationCertificateAuthorityTest { // prepare identity proof interactions when(identityProof.getLabel()).thenReturn(identityProofLabel); - // prepare other mocks - when(acaCertificate.getSubjectX500Principal()).thenReturn(principal); - when(acaCertificate.getIssuerX500Principal()).thenReturn(principal); - // perform the test X509Certificate certificate = abstractProcessor.accessGenerateCredential(keyPair.getPublic(), null, @@ -453,8 +485,7 @@ public class AttestationCertificateAuthorityTest { assertEquals(tomorrow.get(Calendar.DATE), afterDate.get(Calendar.DATE)); // validate mock interactions - verify(acaCertificate).getSubjectX500Principal(); - verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey, acaCertificate); + verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey); } /** diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java index 36ce7d06..3f4d85eb 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java @@ -74,7 +74,7 @@ public class PersistenceJPAConfig implements WebMvcConfigurer { @Value("${server.ssl.key-store-password:''}") private String keyStorePassword; - @Value("${server.ssl.key-alias}") + @Value("${aca.certificates.signing-key-alias}") private String keyAlias; @Autowired diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.properties b/HIRS_AttestationCAPortal/src/main/resources/application.properties index 6ea4a85e..5a35bcbc 100644 --- a/HIRS_AttestationCAPortal/src/main/resources/application.properties +++ b/HIRS_AttestationCAPortal/src/main/resources/application.properties @@ -33,6 +33,7 @@ server.ssl.key-alias=hirs_aca_tls_rsa_3k_sha384 server.ssl.enabled-protocols=TLSv1.2, TLSv1.3 server.ssl.ciphers=TLS_AES_256_GCM_SHA384, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-GCM-SHA384, DHE-RSA-AES256-GCM-SHA384, AES256-GCM-SHA384 # ACA specific default properties +aca.certificates.signing-key-alias=HIRS_leaf_ca3_rsa_3k_sha384 aca.certificates.validity=3652 # Compression settings server.compression.enabled=true diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp index c82a00ca..32890436 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp @@ -53,7 +53,7 @@ } }, { - data: 'isLDevID', + data: 'ldevID', searchable:false, render: function (data, type, full, meta) { if (data === true) { @@ -134,4 +134,4 @@ - \ No newline at end of file +