mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-01-18 10:46:39 +00:00
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:
parent
7bb9d8698d
commit
962ca45bb7
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user