Modify ACA RIM validation to search for a signing cert if the base RIM does not have an embedded cert. Validate the ca chain of the found signing cert.

This commit is contained in:
chubtub 2021-10-21 18:45:46 -04:00
parent 7bb9d8698d
commit 962ca45bb7
3 changed files with 193 additions and 152 deletions

View File

@ -46,15 +46,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -95,7 +92,8 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
/** /**
* Constructor to set just the CertificateManager, so that cert chain validating * Constructor to set just the CertificateManager, so that cert chain validating
* methods can be called from outside classes. * methods can be called from outside classes.
* @param certificateManager the cert manager *
* @param certificateManager the cert manager
*/ */
public SupplyChainValidationServiceImpl(final CertificateManager certificateManager) { public SupplyChainValidationServiceImpl(final CertificateManager certificateManager) {
this.certificateManager = certificateManager; this.certificateManager = certificateManager;
@ -135,6 +133,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
/** /**
* Allows other service access to the policy information. * Allows other service access to the policy information.
*
* @return supply chain policy * @return supply chain policy
*/ */
public SupplyChainPolicy getPolicy() { public SupplyChainPolicy getPolicy() {
@ -243,7 +242,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} else { } else {
validations.add(new SupplyChainValidation(platformType, validations.add(new SupplyChainValidation(platformType,
AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage)); AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage));
} }
} }
} }
@ -425,33 +424,20 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
// verify signatures // verify signatures
ReferenceManifestValidator referenceManifestValidator = ReferenceManifestValidator referenceManifestValidator =
new ReferenceManifestValidator( new ReferenceManifestValidator();
new ByteArrayInputStream(baseReferenceManifest.getRimBytes())); referenceManifestValidator.setRim(baseReferenceManifest);
for (SwidResource swidRes : resources) {
supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager)
.byHexDecHash(swidRes.getHashValue()).getRIM();
if (supportReferenceManifest != null
&& swidRes.getName().equals(supportReferenceManifest.getFileName())) {
referenceManifestValidator.validateSupportRimHash(
supportReferenceManifest.getRimBytes(), swidRes.getHashValue());
} else {
supportReferenceManifest = null;
}
}
//Validate signing cert //Validate signing cert
Set<CertificateAuthorityCredential> allCerts = Set<CertificateAuthorityCredential> allCerts =
CertificateAuthorityCredential.select(certificateManager).getCertificates(); CertificateAuthorityCredential.select(certificateManager).getCertificates();
CertificateAuthorityCredential signingCert = null; CertificateAuthorityCredential signingCert = null;
for (CertificateAuthorityCredential cert : allCerts) { for (CertificateAuthorityCredential cert : allCerts) {
if (Arrays.equals(cert.getEncodedPublicKey(), signingCert = cert;
referenceManifestValidator.getPublicKey().getEncoded())) { KeyStore keyStore = getCaChain(signingCert);
signingCert = cert; if (referenceManifestValidator.validateXmlSignature(signingCert)) {
KeyStore keyStore = getCaChain(signingCert);
try { try {
X509Certificate x509Cert = signingCert.getX509Certificate(); if (!SupplyChainCredentialValidator.verifyCertificate(
if (!SupplyChainCredentialValidator.verifyCertificate(x509Cert, keyStore)) { signingCert.getX509Certificate(), keyStore)) {
passed = false; passed = false;
fwStatus = new AppraisalStatus(FAIL, fwStatus = new AppraisalStatus(FAIL,
"Firmware validation failed: invalid certificate path."); "Firmware validation failed: invalid certificate path.");
@ -467,6 +453,18 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
} }
} }
for (SwidResource swidRes : resources) {
supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager)
.byHexDecHash(swidRes.getHashValue()).getRIM();
if (supportReferenceManifest != null
&& swidRes.getName().equals(supportReferenceManifest.getFileName())) {
referenceManifestValidator.validateSupportRimHash(
supportReferenceManifest.getRimBytes(), swidRes.getHashValue());
} else {
supportReferenceManifest = null;
}
}
if (signingCert == null) { if (signingCert == null) {
passed = false; passed = false;
fwStatus = new AppraisalStatus(FAIL, fwStatus = new AppraisalStatus(FAIL,

View File

@ -31,12 +31,10 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -68,9 +66,9 @@ public class ReferenceManifestDetailsPageController
* Constructor providing the Page's display and routing specification. * Constructor providing the Page's display and routing specification.
* *
* @param referenceManifestManager the reference manifest manager. * @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager. * @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager. * @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager. * @param certificateManager the certificate manager.
*/ */
@Autowired @Autowired
public ReferenceManifestDetailsPageController( public ReferenceManifestDetailsPageController(
@ -144,19 +142,19 @@ public class ReferenceManifestDetailsPageController
* *
* @param uuid database reference for the requested RIM. * @param uuid database reference for the requested RIM.
* @param referenceManifestManager the reference manifest manager. * @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager. * @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager. * @param referenceEventManager the reference event manager.
* @param certificateManager the certificate manager. * @param certificateManager the certificate manager.
* @return mapping of the RIM information from the database. * @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes. * @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
* @throws CertificateException if a certificate doesn't parse. * @throws CertificateException if a certificate doesn't parse.
*/ */
public static HashMap<String, Object> getRimDetailInfo(final UUID uuid, public static HashMap<String, Object> getRimDetailInfo(final UUID uuid,
final ReferenceManifestManager referenceManifestManager, final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager, final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager, final ReferenceEventManager referenceEventManager,
final CertificateManager certificateManager) throws IOException, final CertificateManager certificateManager) throws IOException,
CertificateException, NoSuchAlgorithmException { CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>(); HashMap<String, Object> data = new HashMap<>();
@ -189,9 +187,9 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder. * This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays. * Gathers all information and returns it for displays.
* *
* @param baseRim established ReferenceManifest Type. * @param baseRim established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager. * @param referenceManifestManager the reference manifest manager.
* @param certificateManager the certificate manager. * @param certificateManager the certificate manager.
* @return mapping of the RIM information from the database. * @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes. * @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
@ -284,6 +282,7 @@ public class ReferenceManifestDetailsPageController
} }
// going to have to pull the filename and grab that from the DB // going to have to pull the filename and grab that from the DB
// to get the id to make the link // to get the id to make the link
RIM_VALIDATOR.setRim(baseRim);
for (SwidResource swidRes : resources) { for (SwidResource swidRes : resources) {
if (support != null && swidRes.getHashValue() if (support != null && swidRes.getHashValue()
.equalsIgnoreCase(support.getHexDecHash())) { .equalsIgnoreCase(support.getHexDecHash())) {
@ -305,29 +304,25 @@ public class ReferenceManifestDetailsPageController
data.put("pcrList", support.getExpectedPCRList()); data.put("pcrList", support.getExpectedPCRList());
} }
RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes())); // RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes()));
Set<CertificateAuthorityCredential> certificates = Set<CertificateAuthorityCredential> certificates =
CertificateAuthorityCredential.select(certificateManager) CertificateAuthorityCredential.select(certificateManager)
.getCertificates(); .getCertificates();
//Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid //Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid
data.put("signatureValid", false); data.put("signatureValid", false);
if (RIM_VALIDATOR.isSignatureValid()) { for (CertificateAuthorityCredential cert : certificates) {
for (CertificateAuthorityCredential cert : certificates) { SupplyChainValidationServiceImpl scvsImpl =
if (Arrays.equals(cert.getEncodedPublicKey(), new SupplyChainValidationServiceImpl(certificateManager);
RIM_VALIDATOR.getPublicKey().getEncoded())) { KeyStore keystore = scvsImpl.getCaChain(cert);
SupplyChainValidationServiceImpl scvsImpl = if (RIM_VALIDATOR.validateXmlSignature(cert)) {
new SupplyChainValidationServiceImpl(certificateManager); try {
KeyStore keystore = scvsImpl.getCaChain(cert); if (SupplyChainCredentialValidator.verifyCertificate(
X509Certificate signingCert = cert.getX509Certificate(); cert.getX509Certificate(), keystore)) {
try { data.replace("signatureValid", true);
if (SupplyChainCredentialValidator.verifyCertificate(signingCert, break;
keystore)) {
data.replace("signatureValid", true);
}
} catch (SupplyChainValidatorException e) {
LOGGER.error("Error verifying cert chain: " + e.getMessage());
} }
break; } catch (SupplyChainValidatorException e) {
LOGGER.error("Error verifying cert chain: " + e.getMessage());
} }
} }
} }
@ -349,7 +344,7 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder. * This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays. * Gathers all information and returns it for displays.
* *
* @param support established ReferenceManifest Type. * @param support established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager. * @param referenceManifestManager the reference manifest manager.
* @return mapping of the RIM information from the database. * @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes. * @throws java.io.IOException error for reading file bytes.
@ -414,8 +409,8 @@ public class ReferenceManifestDetailsPageController
digestMap.put(tpe.getEventDigestStr(), tpe); digestMap.put(tpe.getEventDigestStr(), tpe);
if (!support.isSwidSupplemental() if (!support.isSwidSupplemental()
&& !tpe.eventCompare( && !tpe.eventCompare(
measurementsProcess.getEventByNumber( measurementsProcess.getEventByNumber(
tpe.getEventNumber()))) { tpe.getEventNumber()))) {
tpe.setError(true); tpe.setError(true);
} }
tpmPcrEvents.add(tpe); tpmPcrEvents.add(tpe);
@ -509,10 +504,10 @@ public class ReferenceManifestDetailsPageController
* This method takes the place of an entire class for a string builder. * This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays. * Gathers all information and returns it for displays.
* *
* @param measurements established ReferenceManifest Type. * @param measurements established ReferenceManifest Type.
* @param referenceManifestManager the reference manifest manager. * @param referenceManifestManager the reference manifest manager.
* @param referenceDigestManager the reference digest manager. * @param referenceDigestManager the reference digest manager.
* @param referenceEventManager the reference event manager. * @param referenceEventManager the reference event manager.
* @return mapping of the RIM information from the database. * @return mapping of the RIM information from the database.
* @throws java.io.IOException error for reading file bytes. * @throws java.io.IOException error for reading file bytes.
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.

View File

@ -1,7 +1,11 @@
package hirs.utils; package hirs.utils;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -23,7 +27,6 @@ import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data; import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.transform.Source; import javax.xml.transform.Source;
import javax.xml.transform.Transformer; import javax.xml.transform.Transformer;
@ -34,14 +37,17 @@ import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema; import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.Key; import java.security.Key;
import java.security.KeyException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
@ -52,7 +58,7 @@ import java.util.Iterator;
*/ */
public class ReferenceManifestValidator { public class ReferenceManifestValidator {
private static final String SIGNATURE_ALGORITHM_RSA_SHA256 = private static final String SIGNATURE_ALGORITHM_RSA_SHA256 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
private static final String SCHEMA_PACKAGE = "hirs.utils.xjc"; private static final String SCHEMA_PACKAGE = "hirs.utils.xjc";
private static final String SCHEMA_URL = "swid_schema.xsd"; private static final String SCHEMA_URL = "swid_schema.xsd";
private static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI; private static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
@ -63,12 +69,29 @@ public class ReferenceManifestValidator {
private static final int RADIX = 16; private static final int RADIX = 16;
private static final Logger LOGGER = LogManager.getLogger(ReferenceManifestValidator.class); private static final Logger LOGGER = LogManager.getLogger(ReferenceManifestValidator.class);
private Document rim;
private Unmarshaller unmarshaller; private Unmarshaller unmarshaller;
private PublicKey publicKey; private PublicKey publicKey;
private Schema schema; private Schema schema;
private String subjectKeyIdentifier; private String subjectKeyIdentifier;
private boolean signatureValid, supportRimValid; private boolean signatureValid, supportRimValid;
/**
* Setter for the RIM to be validated. The ReferenceManifest object is converted into a
* Document for processing.
*
* @param rim ReferenceManifest object
*/
public void setRim(ReferenceManifest rim) {
try {
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(
new ByteArrayInputStream(rim.getRimBytes()))));
this.rim = doc;
} catch (IOException e) {
LOGGER.error("Error while unmarshalling rim bytes: " + e.getMessage());
}
}
/** /**
* Getter for signatureValid. * Getter for signatureValid.
* *
@ -98,6 +121,7 @@ public class ReferenceManifestValidator {
/** /**
* Getter for subjectKeyIdentifier. * Getter for subjectKeyIdentifier.
*
* @return subjectKeyIdentifier * @return subjectKeyIdentifier
*/ */
public String getSubjectKeyIdentifier() { public String getSubjectKeyIdentifier() {
@ -114,43 +138,16 @@ public class ReferenceManifestValidator {
.getClassLoader().getResourceAsStream(SCHEMA_URL); .getClassLoader().getResourceAsStream(SCHEMA_URL);
SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE); SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
schema = schemaFactory.newSchema(new StreamSource(is)); schema = schemaFactory.newSchema(new StreamSource(is));
rim = null;
signatureValid = false; signatureValid = false;
supportRimValid = false; supportRimValid = false;
publicKey = null; publicKey = null;
subjectKeyIdentifier = ""; subjectKeyIdentifier = "(not found)";
} catch (SAXException e) { } catch (SAXException e) {
LOGGER.warn("Error setting schema for validation!"); LOGGER.warn("Error setting schema for validation!");
} }
} }
/**
* This constructor is used for a quick signature check which bypasses the loading of
* the schema url into memory. As a result the full stream is not validated against the schema.
*
* @param input xml data byte array.
*/
public ReferenceManifestValidator(final InputStream input) {
try {
signatureValid = validateSignedXMLDocument(
removeXMLWhitespace(new StreamSource(input)));
} catch (IOException e) {
LOGGER.warn("Error during unmarshal: " + e.getMessage());
}
}
/**
* This method calculates the SHA256 hash of the input byte array and compares it against
* the value passed in.
*
* @param input byte array to hash.
* @param expected value to compare against.
*/
public void validateSupportRimHash(final byte[] input, final String expected) {
String calculatedHash = getHashValue(input, SHA256);
LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected);
supportRimValid = calculatedHash.equals(expected);
}
/** /**
* This method validates the xml signature in the stream and stores the * This method validates the xml signature in the stream and stores the
* result for public access. * result for public access.
@ -160,16 +157,67 @@ public class ReferenceManifestValidator {
public void validateXmlSignature(final InputStream input) { public void validateXmlSignature(final InputStream input) {
try { try {
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(input))); Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(input)));
signatureValid = validateSignedXMLDocument(doc); //signatureValid = validateSignedXMLDocument(doc);
} catch (IOException e) { } catch (IOException e) {
LOGGER.warn("Error during unmarshal: " + e.getMessage()); LOGGER.warn("Error during unmarshal: " + e.getMessage());
} }
} }
public boolean validateXmlSignature(final CertificateAuthorityCredential cert) {
DOMValidateContext context = null;
try {
NodeList nodes = rim.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) {
LOGGER.error("Cannot validate RIM, signature element not found!");
return false;
}
NodeList certElement = rim.getElementsByTagName("X509Certificate");
if (certElement.getLength() > 0) {
X509Certificate embeddedCert = parseCertFromPEMString(
certElement.item(0).getTextContent());
subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert);
if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(),
cert.getEncodedPublicKey())) {
context = new DOMValidateContext(new X509KeySelector(), nodes.item(0));
}
} else {
subjectKeyIdentifier = getKeyName(rim);
if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString())) {
context = new DOMValidateContext(cert.getX509Certificate().getPublicKey(),
nodes.item(0));
}
}
if (context != null) {
publicKey = cert.getX509Certificate().getPublicKey();
return validateSignedXMLDocument(context);
}
} catch (CertificateException e) {
LOGGER.warn("Error parsing embedded certificate from RIM: " + e.getMessage());
} catch (IOException e) {
LOGGER.warn("Error while parsing certificate data: " + e.getMessage());
}
return false;
}
/**
* This method calculates the SHA256 hash of the input byte array and compares it against
* the value passed in.
*
* @param input byte array to hash.
* @param expected value to compare against.
*/
public void validateSupportRimHash(final byte[] input, final String expected) {
String calculatedHash = getHashValue(input, SHA256);
LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected);
supportRimValid = calculatedHash.equals(expected);
}
/** /**
* This method calculates the digest of a byte array based on the hashing algorithm passed in. * This method calculates the digest of a byte array based on the hashing algorithm passed in.
*
* @param input byte array. * @param input byte array.
* @param sha hash algorithm. * @param sha hash algorithm.
* @return String digest. * @return String digest.
*/ */
private String getHashValue(final byte[] input, final String sha) { private String getHashValue(final byte[] input, final String sha) {
@ -181,7 +229,7 @@ public class ReferenceManifestValidator {
for (int i = 0; i < bytes.length; i++) { for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & EIGHT_BIT_MASK) sb.append(Integer.toString((bytes[i] & EIGHT_BIT_MASK)
+ LEFT_SHIFT, RADIX).substring(1)); + LEFT_SHIFT, RADIX).substring(1));
} }
resultString = sb.toString(); resultString = sb.toString();
} catch (NoSuchAlgorithmException grex) { } catch (NoSuchAlgorithmException grex) {
@ -191,37 +239,18 @@ public class ReferenceManifestValidator {
return resultString; return resultString;
} }
/** private boolean validateSignedXMLDocument(final DOMValidateContext context) {
* This method validates a Document with a signature element.
*
* @param doc
*/
private boolean validateSignedXMLDocument(final Document doc) {
DOMValidateContext context;
boolean isValid = false;
try { try {
NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) {
throw new Exception("Signature element not found!");
}
X509KeySelector keySelector = new ReferenceManifestValidator.X509KeySelector();
context = new DOMValidateContext(keySelector, nodes.item(0));
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = sigFactory.unmarshalXMLSignature(context); XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
isValid = signature.validate(context); return signature.validate(context);
publicKey = keySelector.getPublicKey();
subjectKeyIdentifier = getKeyName(doc);
} catch (MarshalException e) { } catch (MarshalException e) {
LOGGER.warn("Error while unmarshalling XML signature: " + e.getMessage()); LOGGER.warn("Error while unmarshalling XML signature: " + e.getMessage());
} catch (XMLSignatureException e) { } catch (XMLSignatureException e) {
LOGGER.warn("Error while validating XML signature: " + e.getMessage()); LOGGER.warn("Error while validating XML signature: " + e.getMessage());
} catch (KeySelectorException e) {
LOGGER.warn("Public key not found in XML signature: " + e.getMessage());
} catch (Exception e) {
LOGGER.warn(e.getMessage());
} }
return isValid; return false;
} }
/** /**
@ -230,8 +259,6 @@ public class ReferenceManifestValidator {
* an XML signature. * an XML signature.
*/ */
public static class X509KeySelector extends KeySelector { public static class X509KeySelector extends KeySelector {
private PublicKey publicKey;
/** /**
* This method selects a public key for validation. * This method selects a public key for validation.
* PKs are parsed preferentially from the following elements: * PKs are parsed preferentially from the following elements:
@ -240,10 +267,10 @@ public class ReferenceManifestValidator {
* The parsed PK is then verified based on the provided algorithm before * The parsed PK is then verified based on the provided algorithm before
* being returned in a KeySelectorResult. * being returned in a KeySelectorResult.
* *
* @param keyinfo object containing the cert. * @param keyinfo object containing the cert.
* @param purpose purpose. * @param purpose purpose.
* @param algorithm algorithm. * @param algorithm algorithm.
* @param context XMLCryptoContext. * @param context XMLCryptoContext.
* @return KeySelectorResult holding the PublicKey. * @return KeySelectorResult holding the PublicKey.
* @throws KeySelectorException exception. * @throws KeySelectorException exception.
*/ */
@ -251,7 +278,7 @@ public class ReferenceManifestValidator {
final KeySelector.Purpose purpose, final KeySelector.Purpose purpose,
final AlgorithmMethod algorithm, final AlgorithmMethod algorithm,
final XMLCryptoContext context) final XMLCryptoContext context)
throws KeySelectorException { throws KeySelectorException {
Iterator keyinfoItr = keyinfo.getContent().iterator(); Iterator keyinfoItr = keyinfo.getContent().iterator();
while (keyinfoItr.hasNext()) { while (keyinfoItr.hasNext()) {
XMLStructure element = (XMLStructure) keyinfoItr.next(); XMLStructure element = (XMLStructure) keyinfoItr.next();
@ -261,32 +288,22 @@ public class ReferenceManifestValidator {
while (dataItr.hasNext()) { while (dataItr.hasNext()) {
Object object = dataItr.next(); Object object = dataItr.next();
if (object instanceof X509Certificate) { if (object instanceof X509Certificate) {
publicKey = ((X509Certificate) object).getPublicKey(); final PublicKey publicKey = ((X509Certificate) object).getPublicKey();
break; if (areAlgorithmsEqual(algorithm.getAlgorithm(), publicKey.getAlgorithm())) {
return new ReferenceManifestValidator.X509KeySelector
.RIMKeySelectorResult(publicKey);
}
} }
} }
} else if (element instanceof KeyValue) {
try {
publicKey = ((KeyValue) element).getPublicKey();
} catch (KeyException e) {
LOGGER.warn("KeyException thrown while getting PK from KeyValue: "
+ e.getMessage());
}
} }
} }
if (areAlgorithmsEqual(algorithm.getAlgorithm(),
publicKey.getAlgorithm())) {
return new ReferenceManifestValidator.X509KeySelector
.RIMKeySelectorResult(publicKey);
}
throw new KeySelectorException("No key found!"); throw new KeySelectorException("No key found!");
} }
/** /**
* This method checks if two strings refer to the same algorithm. * This method checks if two strings refer to the same algorithm.
* *
* @param uri string 1 * @param uri string 1
* @param name string 2 * @param name string 2
* @return true if equal, false if not * @return true if equal, false if not
*/ */
@ -294,19 +311,10 @@ public class ReferenceManifestValidator {
return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA"); return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA");
} }
/**
* Getter for the public key that is parsed in the select() method above.
*
* @return PublicKey encoded in the X509 cert.
*/
public PublicKey getPublicKey() {
return publicKey;
}
/** /**
* This internal class creates a KeySelectorResult from the public key. * This internal class creates a KeySelectorResult from the public key.
*/ */
private static class RIMKeySelectorResult implements KeySelectorResult { private class RIMKeySelectorResult implements KeySelectorResult {
private Key key; private Key key;
RIMKeySelectorResult(final Key key) { RIMKeySelectorResult(final Key key) {
@ -319,6 +327,46 @@ public class ReferenceManifestValidator {
} }
} }
/**
* This method extracts certificate bytes from a string. The bytes are assumed to be
* PEM format, and a header and footer are concatenated with the input string to
* facilitate proper parsing.
*
* @param pemString the input string
* @return an X509Certificate created from the string
* @throws CertificateException if instantiating the CertificateFactory errors
*/
public X509Certificate parseCertFromPEMString(String pemString) throws CertificateException {
String CERTIFICATE_HEADER = "-----BEGIN CERTIFICATE-----";
String CERTIFICATE_FOOTER = "-----END CERTIFICATE-----";
try {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
InputStream inputStream = new ByteArrayInputStream((CERTIFICATE_HEADER
+ System.lineSeparator()
+ pemString
+ System.lineSeparator()
+ CERTIFICATE_FOOTER).getBytes());
return (X509Certificate) factory.generateCertificate(inputStream);
} catch (CertificateException e) {
throw e;
}
}
/**
* This method returns the subjectKeyIdentifier from a given X509Certificate.
* @param certificate the cert to pull the subjectKeyIdentifier from
* @return the String representation of the subjectKeyIdentifier
* @throws IOException
*/
private String getCertificateSubjectKeyIdentifier(X509Certificate certificate) throws IOException {
String decodedValue = null;
byte[] extension = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId());
if (extension != null && extension.length > 0) {
decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString();
}
return decodedValue.substring(1);//Drop the # at the beginning of the string
}
/** /**
* This method parses the subject key identifier from the KeyName element of a signature. * This method parses the subject key identifier from the KeyName element of a signature.
* *
@ -368,7 +416,7 @@ public class ReferenceManifestValidator {
TransformerFactory tf = TransformerFactory.newInstance(); TransformerFactory tf = TransformerFactory.newInstance();
Source identitySource = new StreamSource( Source identitySource = new StreamSource(
ReferenceManifestValidator.class.getClassLoader() ReferenceManifestValidator.class.getClassLoader()
.getResourceAsStream(IDENTITY_TRANSFORM)); .getResourceAsStream(IDENTITY_TRANSFORM));
Document doc = null; Document doc = null;
try { try {
Transformer transformer = tf.newTransformer(identitySource); Transformer transformer = tf.newTransformer(identitySource);