Merge pull request from nsacyber/certificate-failure-fidelity

Certificate Failure Fidelity
This commit is contained in:
Cyrus 2021-04-09 14:15:43 -04:00 committed by GitHub
commit c46aa2b48b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 88 deletions
HIRS_AttestationCAPortal/src/main
java/hirs/attestationca/portal/util
webapp/WEB-INF/jsp
HIRS_Utils/src
main/java/hirs
data/persist/certificate
validation
test/java/hirs

@ -103,20 +103,30 @@ public final class CertificateStringMapBuilder {
if (data.get("isSelfSigned").equals("false")) {
//Get the missing certificate chain for not self sign
Certificate missingCert = containsAllChain(certificate, certificateManager);
String issuerResult;
if (missingCert != null) {
data.put("missingChainIssuer", missingCert.getIssuer());
data.put("missingChainIssuer", String.format("Missing %s from the chain.",
missingCert.getIssuer()));
}
//Find all certificates that could be the issuer certificate based on subject name
for (Certificate issuerCert : CertificateAuthorityCredential
.select(certificateManager)
.bySubject(certificate.getIssuer())
.bySubjectSorted(certificate.getIssuerSorted())
.getCertificates()) {
try {
//Find the certificate that actually signed this cert
if (certificate.isIssuer(issuerCert)) {
issuerResult = certificate.isIssuer(issuerCert);
if (issuerResult.isEmpty()) {
data.put("issuerID", issuerCert.getId().toString());
break;
} else {
data.put("issuerID", issuerCert.getId().toString());
issuerResult = String.format("%s: %s", issuerResult,
issuerCert.getSubject());
data.put("missingChainIssuer", issuerResult);
break;
}
} catch (IOException e) {
LOGGER.error(e);
@ -139,6 +149,7 @@ public final class CertificateStringMapBuilder {
final CertificateManager certificateManager) {
Set<CertificateAuthorityCredential> issuerCertificates = new HashSet<>();
CertificateAuthorityCredential skiCA = null;
String issuerResult;
//Check if there is a subject organization
if (certificate.getAuthKeyId() != null
&& !certificate.getAuthKeyId().isEmpty()) {
@ -147,7 +158,7 @@ public final class CertificateStringMapBuilder {
.select(certificateManager)
.bySubjectKeyIdentifier(bytes).getCertificate();
} else {
LOGGER.info(String.format("Certificate (%s) for %s has no authority key identifier.",
LOGGER.error(String.format("Certificate (%s) for %s has no authority key identifier.",
certificate.getClass().toString(), certificate.getSubject()));
}
@ -171,7 +182,8 @@ public final class CertificateStringMapBuilder {
for (Certificate issuerCert : issuerCertificates) {
try {
// Find the certificate that actually signed this cert
if (certificate.isIssuer(issuerCert)) {
issuerResult = certificate.isIssuer(issuerCert);
if (issuerResult.isEmpty()) {
//Check if it's root certificate
if (BouncyCastleUtils.x500NameCompare(issuerCert.getIssuer(),
issuerCert.getSubject())) {

@ -88,7 +88,7 @@
</c:when>
<c:otherwise>
<img src="${icons}/ic_error_red_24dp.png"
title="Missing ${initialData.missingChainIssuer} from the chain.">
title="${initialData.missingChainIssuer}">
</c:otherwise>
</c:choose>
</span>

@ -8,31 +8,34 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.AttCertIssuer;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.V2Form;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AttCertIssuer;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.V2Form;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
import javax.persistence.Column;
@ -64,9 +67,6 @@ import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
/**
@ -753,8 +753,8 @@ public abstract class Certificate extends ArchivableEntity {
* @return whether or not the other certificate is the issuer for this certificate
* @throws IOException if there is an issue deserializing either certificate
*/
public boolean isIssuer(final Certificate issuer) throws IOException {
boolean isIssuer = false;
public String isIssuer(final Certificate issuer) throws IOException {
String isIssuer = "Certificate signature failed to verify";
// only run if of the correct type, otherwise false
if (issuer.getCertificateType() == CertificateType.X509_CERTIFICATE) {
X509Certificate issuerX509 = issuer.getX509Certificate();
@ -764,7 +764,7 @@ public abstract class Certificate extends ArchivableEntity {
X509Certificate certX509 = getX509Certificate();
try {
certX509.verify(issuerX509.getPublicKey());
isIssuer = true;
isIssuer = "";
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException
| NoSuchProviderException | SignatureException e) {
LOGGER.error(e);
@ -777,7 +777,9 @@ public abstract class Certificate extends ArchivableEntity {
Signature sig = Signature.getInstance(algorithm);
sig.initVerify(issuerX509.getPublicKey());
sig.update(attCert.getAcinfo().getEncoded());
isIssuer = sig.verify(attCert.getSignatureValue().getBytes());
if (sig.verify(attCert.getSignatureValue().getBytes())) {
isIssuer = "";
}
} catch (NoSuchAlgorithmException
| InvalidKeyException
| SignatureException e) {

@ -4,17 +4,21 @@ import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.info.ComponentInfo;
import hirs.data.persist.ArchivableEntity;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.info.HardwareInfo;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.data.persist.certificate.attributes.ComponentIdentifier;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.data.persist.info.ComponentInfo;
import hirs.data.persist.info.HardwareInfo;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.CertException;
@ -41,11 +45,14 @@ import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -55,13 +62,6 @@ import java.util.stream.Collectors;
import static hirs.data.persist.AppraisalStatus.Status.ERROR;
import static hirs.data.persist.AppraisalStatus.Status.FAIL;
import static hirs.data.persist.AppraisalStatus.Status.PASS;
import hirs.data.persist.ArchivableEntity;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import org.apache.logging.log4j.util.Strings;
/**
@ -196,6 +196,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
final boolean acceptExpired) {
final String baseErrorMessage = "Can't validate platform credential without ";
String message;
String certVerifyMsg;
if (pc == null) {
message = baseErrorMessage + "a platform credential\n";
LOGGER.error(message);
@ -233,19 +234,21 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
// verify cert against truststore
try {
if (verifyCertificate(attributeCert, trustStore)) {
certVerifyMsg = verifyCertificate(attributeCert, trustStore);
if (certVerifyMsg.isEmpty()) {
message = PLATFORM_VALID;
LOGGER.info(message);
return new AppraisalStatus(PASS, message);
} else {
message = "Platform credential failed verification";
message = String.format("Platform credential failed verification%n%s",
certVerifyMsg);
LOGGER.error(message);
return new AppraisalStatus(FAIL, message);
}
} catch (SupplyChainValidatorException e) {
} catch (SupplyChainValidatorException scvEx) {
message = "An error occurred indicating the credential is not valid";
LOGGER.warn(message, e);
return new AppraisalStatus(FAIL, message + " " + e.getMessage());
LOGGER.warn(message, scvEx);
return new AppraisalStatus(FAIL, message + " " + scvEx.getMessage());
}
}
@ -1244,14 +1247,14 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* @throws SupplyChainValidatorException
* if the verification is not successful
*/
public static boolean verifyCertificate(final X509AttributeCertificateHolder cert,
public static String verifyCertificate(final X509AttributeCertificateHolder cert,
final KeyStore trustStore) throws SupplyChainValidatorException {
if (cert == null || trustStore == null) {
throw new SupplyChainValidatorException("Certificate or trust store is null");
}
try {
Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
Set<X509Certificate> trustedCerts = new HashSet<>();
Enumeration<String> alias = trustStore.aliases();
@ -1259,15 +1262,14 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement()));
}
boolean certChainValidated = validateCertChain(cert, trustedCerts);
if (!certChainValidated) {
String certChainValidated = validateCertChain(cert, trustedCerts);
if (!certChainValidated.isEmpty()) {
LOGGER.error("Cert chain could not be validated");
}
return certChainValidated;
} catch (KeyStoreException e) {
throw new SupplyChainValidatorException("Error with the trust store", e);
}
}
/**
@ -1291,7 +1293,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
throw new SupplyChainValidatorException("Certificate or trust store is null");
}
try {
Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
Set<X509Certificate> trustedCerts = new HashSet<>();
Enumeration<String> alias = trustStore.aliases();
@ -1299,7 +1301,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement()));
}
return validateCertChain(cert, trustedCerts);
return validateCertChain(cert, trustedCerts).isEmpty();
} catch (KeyStoreException e) {
LOGGER.error("Error accessing keystore", e);
throw new SupplyChainValidatorException("Error with the trust store", e);
@ -1321,31 +1323,40 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* @return boolean indicating if the validation was successful
* @throws SupplyChainValidatorException tried to validate using null certificates
*/
public static boolean validateCertChain(final X509AttributeCertificateHolder cert,
public static String validateCertChain(final X509AttributeCertificateHolder cert,
final Set<X509Certificate> additionalCerts) throws SupplyChainValidatorException {
if (cert == null || additionalCerts == null) {
throw new SupplyChainValidatorException(
"Certificate or validation certificates are null");
}
boolean foundRootOfCertChain = false;
String foundRootOfCertChain = "";
Iterator<X509Certificate> certIterator = additionalCerts.iterator();
X509Certificate trustedCert;
boolean issuerMatchesSubject = false;
boolean signatureMatchesPublicKey = false;
while (!foundRootOfCertChain && certIterator.hasNext()) {
while (foundRootOfCertChain.isEmpty() && certIterator.hasNext()) {
trustedCert = certIterator.next();
if (issuerMatchesSubjectDN(cert, trustedCert)
&& signatureMatchesPublicKey(cert, trustedCert)) {
issuerMatchesSubject = issuerMatchesSubjectDN(cert, trustedCert);
signatureMatchesPublicKey = signatureMatchesPublicKey(cert, trustedCert);
if (issuerMatchesSubject && signatureMatchesPublicKey) {
if (isSelfSigned(trustedCert)) {
LOGGER.info("CA Root found.");
foundRootOfCertChain = true;
} else {
foundRootOfCertChain = validateCertChain(trustedCert, additionalCerts);
if (!foundRootOfCertChain) {
if (!foundRootOfCertChain.isEmpty()) {
LOGGER.error("Root of certificate chain not found. Check for CA Cert: "
+ cert.getIssuer().getNames()[0]);
}
}
} else {
if (!issuerMatchesSubject) {
foundRootOfCertChain = "Issuer DN does not match Subject DN";
}
if (!signatureMatchesPublicKey) {
foundRootOfCertChain = "Certificate signature failed to verify";
}
}
}
@ -1366,31 +1377,40 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
* @return boolean indicating if the validation was successful
* @throws SupplyChainValidatorException tried to validate using null certificates
*/
public static boolean validateCertChain(final X509Certificate cert,
public static String validateCertChain(final X509Certificate cert,
final Set<X509Certificate> additionalCerts) throws SupplyChainValidatorException {
if (cert == null || additionalCerts == null) {
throw new SupplyChainValidatorException(
"Certificate or validation certificates are null");
}
boolean foundRootOfCertChain = false;
String foundRootOfCertChain = "";
Iterator<X509Certificate> certIterator = additionalCerts.iterator();
X509Certificate trustedCert;
boolean issuerMatchesSubject = false;
boolean signatureMatchesPublicKey = false;
while (!foundRootOfCertChain && certIterator.hasNext()) {
while (foundRootOfCertChain.isEmpty() && certIterator.hasNext()) {
trustedCert = certIterator.next();
if (issuerMatchesSubjectDN(cert, trustedCert)
&& signatureMatchesPublicKey(cert, trustedCert)) {
issuerMatchesSubject = issuerMatchesSubjectDN(cert, trustedCert);
signatureMatchesPublicKey = signatureMatchesPublicKey(cert, trustedCert);
if (issuerMatchesSubject && signatureMatchesPublicKey) {
if (isSelfSigned(trustedCert)) {
LOGGER.info("CA Root found.");
foundRootOfCertChain = true;
} else if (!cert.equals(trustedCert)) {
foundRootOfCertChain = validateCertChain(trustedCert, additionalCerts);
if (!foundRootOfCertChain) {
if (!foundRootOfCertChain.isEmpty()) {
LOGGER.error("Root of certificate chain not found. Check for CA Cert: "
+ cert.getIssuerDN().getName());
}
}
} else {
if (!issuerMatchesSubject) {
foundRootOfCertChain = "Issuer DN does not match Subject DN";
}
if (!signatureMatchesPublicKey) {
foundRootOfCertChain = "Certificate signature failed to verify";
}
}
}
@ -1611,8 +1631,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator
*/
public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert,
final X509Certificate signingCert) throws SupplyChainValidatorException {
if (cert == null || signingCert == null) {
throw new SupplyChainValidatorException("Certificate or signing certificate is null");
if (signingCert == null) {
throw new SupplyChainValidatorException("Signing certificate is null");
}
return signatureMatchesPublicKey(cert, signingCert.getPublicKey());
}

@ -441,8 +441,8 @@ public class CertificateTest {
Certificate issuerCert = getTestCertificate(FAKE_ROOT_CA_FILE);
Certificate cert = getTestCertificate(INT_CA_CERT02);
Assert.assertFalse(issuerCert.isIssuer(cert));
Assert.assertTrue(cert.isIssuer(issuerCert));
Assert.assertFalse(!issuerCert.isIssuer(cert).isEmpty());
Assert.assertTrue(cert.isIssuer(issuerCert).isEmpty());
}
/**

@ -7,6 +7,7 @@ import hirs.data.persist.certificate.attributes.TBBSecurityAssertion;
import hirs.data.persist.certificate.attributes.URIReference;
import hirs.data.persist.certificate.attributes.V2.PlatformConfigurationV2;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.util.encoders.Base64;
import org.testng.Assert;
import org.testng.annotations.Test;
@ -19,8 +20,6 @@ import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import org.bouncycastle.util.encoders.Base64;
import static org.testng.Assert.fail;
/**
@ -405,7 +404,7 @@ public class PlatformCredentialTest {
Base64.decode(EXPECTED_CERT_SIGNATURE_FOR_CERT2_1));
//Check if issuer certificate issued the platform credential
Assert.assertTrue(platformCert.isIssuer(issuer));
Assert.assertTrue(platformCert.isIssuer(issuer).isEmpty());
}
/**

@ -895,16 +895,17 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
trustedCerts.add(intermediateCert);
boolean assertion = SupplyChainCredentialValidator.validateCertChain(attrCert,
trustedCerts).isEmpty();
Assert.assertTrue(SupplyChainCredentialValidator.validateCertChain(attrCert,
trustedCerts));
Assert.assertTrue(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);
keyStore.setCertificateEntry("Intermediate Cert", intermediateCert);
Assert.assertTrue(SupplyChainCredentialValidator.verifyCertificate(attrCert,
keyStore));
assertion = SupplyChainCredentialValidator.verifyCertificate(attrCert,
keyStore).isEmpty();
Assert.assertTrue(assertion);
} catch (Exception e) {
Assert.fail("Unexpected error occurred while verifying certificate", e);
}
@ -938,14 +939,16 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
Assert.assertFalse(SupplyChainCredentialValidator.validateCertChain(attrCert,
trustedCerts));
boolean assertion = SupplyChainCredentialValidator.validateCertChain(attrCert,
trustedCerts).isEmpty();
Assert.assertFalse(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);
Assert.assertFalse(SupplyChainCredentialValidator.verifyCertificate(attrCert,
keyStore));
assertion = SupplyChainCredentialValidator.verifyCertificate(attrCert,
keyStore).isEmpty();
Assert.assertFalse(assertion);
} catch (Exception e) {
Assert.fail("Unexpected error occurred while verifying certificate", e);
}
@ -971,12 +974,16 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
Assert.assertTrue(SupplyChainCredentialValidator.validateCertChain(attrCert, trustedCerts));
boolean assertion = SupplyChainCredentialValidator.validateCertChain(
attrCert, trustedCerts).isEmpty();
Assert.assertTrue(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);
Assert.assertTrue(SupplyChainCredentialValidator.verifyCertificate(attrCert, keyStore));
assertion = SupplyChainCredentialValidator.verifyCertificate(
attrCert, keyStore).isEmpty();
Assert.assertTrue(assertion);
} catch (Exception e) {
Assert.fail("Unexpected error occurred while verifying certificate", e);
}
@ -1008,8 +1015,9 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
trustedCerts.add(intermediateCert);
Assert.assertTrue(SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts));
boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts).isEmpty();
Assert.assertTrue(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);
@ -1046,8 +1054,9 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
Assert.assertFalse(SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts));
boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts).isEmpty();
Assert.assertFalse(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);
@ -1076,8 +1085,9 @@ public class SupplyChainCredentialValidatorTest {
trustedCerts.add(caCert);
Assert.assertTrue(SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts));
boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
trustedCerts).isEmpty();
Assert.assertTrue(assertion);
try {
keyStore.setCertificateEntry("CA cert", caCert);