mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-18 12:46:30 +00:00
Merge pull request #878 from nsacyber/v3_issue-877
[#877] Fix ACA-issued certificates not verifying in OpenSSL
This commit is contained in:
commit
accacbd4db
@ -125,14 +125,14 @@ public interface CertificateRepository extends JpaRepository<Certificate, UUID>
|
||||
|
||||
/**
|
||||
* 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<IssuedAttestationCertificate> findByDeviceIdAndIsLDevID(UUID deviceId, boolean isLDevID, Sort sort);
|
||||
List<IssuedAttestationCertificate> 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, UUID>
|
||||
*/
|
||||
Certificate findByCertificateHash(int certificateHash);
|
||||
}
|
||||
|
||||
|
@ -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<PlatformCredential> 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<PlatformCredential> platformCredentials,
|
||||
final boolean isLDevID)
|
||||
final boolean ldevID)
|
||||
throws IOException {
|
||||
this(readBytes(certificatePath), endorsementCredential, platformCredentials, isLDevID);
|
||||
this(readBytes(certificatePath), endorsementCredential, platformCredentials, ldevID);
|
||||
}
|
||||
}
|
||||
|
@ -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<PlatformCredential> platformCredentials,
|
||||
final Device device,
|
||||
final boolean isLDevID) {
|
||||
final boolean ldevID) {
|
||||
List<IssuedAttestationCertificate> 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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -53,7 +53,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'isLDevID',
|
||||
data: 'ldevID',
|
||||
searchable:false,
|
||||
render: function (data, type, full, meta) {
|
||||
if (data === true) {
|
||||
@ -134,4 +134,4 @@
|
||||
</script>
|
||||
</jsp:body>
|
||||
|
||||
</my:page>
|
||||
</my:page>
|
||||
|
Loading…
Reference in New Issue
Block a user