mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-18 20:47:58 +00:00
Merge pull request #624 from nsacyber/v3_issue-607
[#607] Rimtool uses ReferenceManifestValidator for validation
This commit is contained in:
commit
6aba9b9c5d
@ -1,5 +1,6 @@
|
||||
package hirs.utils.rim;
|
||||
|
||||
import hirs.utils.swid.SwidTagConstants;
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.UnmarshalException;
|
||||
@ -7,10 +8,13 @@ import jakarta.xml.bind.Unmarshaller;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.crypto.AlgorithmMethod;
|
||||
import javax.xml.crypto.KeySelector;
|
||||
@ -24,6 +28,7 @@ import javax.xml.crypto.dsig.XMLSignatureException;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyValue;
|
||||
import javax.xml.crypto.dsig.keyinfo.X509Data;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
@ -34,19 +39,16 @@ import javax.xml.transform.dom.DOMResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.Key;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class handles validation functions of RIM files.
|
||||
@ -71,6 +73,9 @@ public class ReferenceManifestValidator {
|
||||
private PublicKey publicKey;
|
||||
private Schema schema;
|
||||
private String subjectKeyIdentifier;
|
||||
private String rimEventLog;
|
||||
private String trustStoreFile;
|
||||
private List<X509Certificate> trustStore;
|
||||
private boolean signatureValid, supportRimValid;
|
||||
|
||||
/**
|
||||
@ -89,6 +94,22 @@ public class ReferenceManifestValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the swidtag XML to be validated. The XML is passed in via a filepath
|
||||
* and converted into a Document for processing.
|
||||
*
|
||||
* @param path String filepath
|
||||
*/
|
||||
public void setRim(final String path) {
|
||||
File swidtagFile = new File(path);
|
||||
try {
|
||||
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(swidtagFile)));
|
||||
this.rim = doc;
|
||||
} catch (IOException e) {
|
||||
log.error("Error while unmarshalling rim bytes: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for signatureValid.
|
||||
*
|
||||
@ -125,6 +146,22 @@ public class ReferenceManifestValidator {
|
||||
return subjectKeyIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the truststore file path.
|
||||
* @param trustStoreFile the truststore
|
||||
*/
|
||||
public void setTrustStoreFile(String trustStoreFile) {
|
||||
this.trustStoreFile = trustStoreFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for rimel file path.
|
||||
* @param rimEventLog the rimel file
|
||||
*/
|
||||
public void setRimEventLog(String rimEventLog) {
|
||||
this.rimEventLog = rimEventLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* This default constructor creates the Schema object from SCHEMA_URL immediately to save
|
||||
* time during validation calls later.
|
||||
@ -167,6 +204,7 @@ public class ReferenceManifestValidator {
|
||||
log.error("Cannot validate RIM, signature element not found!");
|
||||
return false;
|
||||
}
|
||||
trustStore = parseCertificatesFromPem(trustStoreFile);
|
||||
NodeList certElement = rim.getElementsByTagName("X509Certificate");
|
||||
if (certElement.getLength() > 0) {
|
||||
X509Certificate embeddedCert = parseCertFromPEMString(
|
||||
@ -199,6 +237,32 @@ public class ReferenceManifestValidator {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a signed swidtag XML file.
|
||||
* @param path to the swidtag XML
|
||||
*/
|
||||
public boolean validateSwidtagFile(String path) {
|
||||
Element fileElement = (Element) rim.getElementsByTagName("File").item(0);
|
||||
X509Certificate signingCert = null;
|
||||
try {
|
||||
signingCert = getCertFromTruststore();
|
||||
} catch (IOException e) {
|
||||
log.warn("Error while parsing signing cert from truststore: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
String subjectKeyIdentifier = "";
|
||||
try {
|
||||
subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(signingCert);
|
||||
} catch (IOException e) {
|
||||
log.warn("Error while parsing certificate data: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return validateXmlSignature(signingCert.getPublicKey(),
|
||||
subjectKeyIdentifier,
|
||||
signingCert.getPublicKey().getEncoded())
|
||||
&& validateFile(fileElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the SHA256 hash of the input byte array and compares it against
|
||||
* the value passed in.
|
||||
@ -215,6 +279,66 @@ public class ReferenceManifestValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a hirs.swid.xjc.File from an indirect payload
|
||||
*/
|
||||
private boolean validateFile(final Element file) {
|
||||
String filepath;
|
||||
if (!rimEventLog.isEmpty()) {
|
||||
filepath = rimEventLog;
|
||||
} else {
|
||||
filepath = file.getAttribute(SwidTagConstants.NAME);
|
||||
}
|
||||
System.out.println("Support rim found at " + filepath);
|
||||
if (getHashValue(filepath, "SHA256").equals(
|
||||
file.getAttribute(SwidTagConstants._SHA256_HASH.getPrefix() + ":" +
|
||||
SwidTagConstants._SHA256_HASH.getLocalPart()))) {
|
||||
System.out.println("Support RIM hash verified!" + System.lineSeparator());
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("Support RIM hash does not match Base RIM!" + System.lineSeparator());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method pulls the signing certificate from the truststore based on the
|
||||
* SKID parsed from this instance's swidtag XML.
|
||||
*
|
||||
* @return X509Certificate signing cert
|
||||
*/
|
||||
private X509Certificate getCertFromTruststore() throws IOException {
|
||||
String subjectKeyIdentifier = getKeyName(rim);
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
String trustedSubjectKeyIdentifier = getCertificateSubjectKeyIdentifier(trustedCert);
|
||||
if (subjectKeyIdentifier.equals(trustedSubjectKeyIdentifier)) {
|
||||
return trustedCert;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the digest of the file at filepath based on algorithm sha
|
||||
*
|
||||
* @param filepath the file to hash
|
||||
* @param sha the algorithm to use
|
||||
* @return String digest
|
||||
*/
|
||||
private String getHashValue(final String filepath, final String sha) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(sha);
|
||||
byte[] bytes = md.digest(Files.readAllBytes(Paths.get(filepath)));
|
||||
return getHashValue(bytes, sha);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.warn(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.warn("Error reading " + filepath + " for hashing: " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* This method calculates the digest of a byte array based on the hashing algorithm passed in.
|
||||
*
|
||||
@ -260,7 +384,9 @@ public class ReferenceManifestValidator {
|
||||
* It is passed as a parameter to a DOMValidateContext that uses it to validate
|
||||
* an XML signature.
|
||||
*/
|
||||
public static class X509KeySelector extends KeySelector {
|
||||
public class X509KeySelector extends KeySelector {
|
||||
PublicKey publicKey;
|
||||
X509Certificate signingCert;
|
||||
/**
|
||||
* This method selects a public key for validation.
|
||||
* PKs are parsed preferentially from the following elements:
|
||||
@ -290,28 +416,172 @@ public class ReferenceManifestValidator {
|
||||
while (dataItr.hasNext()) {
|
||||
Object object = dataItr.next();
|
||||
if (object instanceof X509Certificate) {
|
||||
final PublicKey publicKey = ((X509Certificate) object).getPublicKey();
|
||||
if (areAlgorithmsEqual(algorithm.getAlgorithm(),
|
||||
publicKey.getAlgorithm())) {
|
||||
return new ReferenceManifestValidator.X509KeySelector
|
||||
.RIMKeySelectorResult(publicKey);
|
||||
X509Certificate embeddedCert = (X509Certificate) object;
|
||||
try {
|
||||
if (isCertChainValid(embeddedCert)) {
|
||||
publicKey = ((X509Certificate) embeddedCert).getPublicKey();
|
||||
signingCert = embeddedCert;
|
||||
System.out.println("Certificate chain validity: true");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Certificate chain invalid: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (element instanceof KeyValue) {
|
||||
try {
|
||||
PublicKey pk = ((KeyValue) element).getPublicKey();
|
||||
if (isPublicKeyTrusted(pk)) {
|
||||
publicKey = pk;
|
||||
try {
|
||||
System.out.println("Certificate chain validity: "
|
||||
+ isCertChainValid(signingCert));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Certificate chain invalid: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (KeyException e) {
|
||||
System.out.println("Unable to convert KeyValue data to PK.");
|
||||
}
|
||||
}
|
||||
if (publicKey != null) {
|
||||
if (areAlgorithmsEqual(algorithm.getAlgorithm(), publicKey.getAlgorithm())) {
|
||||
return new ReferenceManifestValidator.X509KeySelector
|
||||
.RIMKeySelectorResult(publicKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new KeySelectorException("No key found!");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if two strings refer to the same algorithm.
|
||||
*
|
||||
* @param uri string 1
|
||||
* @param name string 2
|
||||
* @return true if equal, false if not
|
||||
* This method checks that the signature and public key algorithms match.
|
||||
* @param uri to match the signature algorithm
|
||||
* @param name to match the public key algorithm
|
||||
* @return true if both match, false otherwise
|
||||
*/
|
||||
public boolean areAlgorithmsEqual(final String uri, final String name) {
|
||||
return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA");
|
||||
public boolean areAlgorithmsEqual(String uri, String name) {
|
||||
return uri.equals(SwidTagConstants.SIGNATURE_ALGORITHM_RSA_SHA256)
|
||||
&& name.equalsIgnoreCase("RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the cert chain for a given certificate. The truststore is iterated
|
||||
* over until a root CA is found, otherwise an error is returned.
|
||||
* @param cert the certificate at the start of the chain
|
||||
* @return true if the chain is valid
|
||||
* @throws Exception if a valid chain is not found in the truststore
|
||||
*/
|
||||
private boolean isCertChainValid(final X509Certificate cert)
|
||||
throws Exception {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new Exception("Null certificate or truststore received");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new Exception("Truststore is empty");
|
||||
}
|
||||
|
||||
final String INT_CA_ERROR = "Intermediate CA found, searching for root CA";
|
||||
String errorMessage = "";
|
||||
X509Certificate startOfChain = cert;
|
||||
do {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
boolean isIssuer = areYouMyIssuer(startOfChain, trustedCert);
|
||||
boolean isSigner = areYouMySigner(startOfChain, trustedCert);
|
||||
if (isIssuer && isSigner) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
return true;
|
||||
} else {
|
||||
startOfChain = trustedCert;
|
||||
errorMessage = INT_CA_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isIssuer) {
|
||||
errorMessage = "Issuer cert not found";
|
||||
} else if (!isSigner) {
|
||||
errorMessage = "Signing cert not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (errorMessage.equals(INT_CA_ERROR));
|
||||
|
||||
throw new Exception("Error while validating cert chain: " + errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's issuerDN matches issuer's subjectDN.
|
||||
* @param cert the signed certificate
|
||||
* @param issuer the signing certificate
|
||||
* @return true if they match, false if not
|
||||
* @throws Exception if either argument is null
|
||||
*/
|
||||
private boolean areYouMyIssuer(final X509Certificate cert, final X509Certificate issuer)
|
||||
throws Exception {
|
||||
if (cert == null || issuer == null) {
|
||||
throw new Exception("Cannot verify issuer, null certificate received");
|
||||
}
|
||||
X500Principal issuerDN = new X500Principal(cert.getIssuerX500Principal().getName());
|
||||
return issuer.getSubjectX500Principal().equals(issuerDN);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's signature matches signer's public key.
|
||||
* @param cert the signed certificate
|
||||
* @param signer the signing certificate
|
||||
* @return true if they match
|
||||
* @throws Exception if an error occurs or there is no match
|
||||
*/
|
||||
private boolean areYouMySigner(final X509Certificate cert, final X509Certificate signer)
|
||||
throws Exception {
|
||||
if (cert == null || signer == null) {
|
||||
throw new Exception("Cannot verify signature, null certificate received");
|
||||
}
|
||||
try {
|
||||
cert.verify(signer.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("Signing algorithm in signing cert not supported");
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new Exception("Signing certificate key does not match signature");
|
||||
} catch (NoSuchProviderException e) {
|
||||
throw new Exception("Error with BouncyCastleProvider: " + e.getMessage());
|
||||
} catch (SignatureException e) {
|
||||
String error = "Error with signature: " + e.getMessage()
|
||||
+ System.lineSeparator()
|
||||
+ "Certificate needed for verification is missing: "
|
||||
+ signer.getSubjectX500Principal().getName();
|
||||
throw new Exception(error);
|
||||
} catch (CertificateException e) {
|
||||
throw new Exception("Encoding error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a given certificate is self signed or not.
|
||||
* @param cert the cert to check
|
||||
* @return true if self signed, false if not
|
||||
*/
|
||||
private boolean isSelfSigned(final X509Certificate cert) {
|
||||
return cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method compares a public key against those in the truststore.
|
||||
* @param pk a public key
|
||||
* @return true if pk is found in the trust store, false otherwise
|
||||
*/
|
||||
private boolean isPublicKeyTrusted(final PublicKey pk) {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
if (Arrays.equals(trustedCert.getPublicKey().getEncoded(),
|
||||
pk.getEncoded())) {
|
||||
signingCert = trustedCert;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,6 +629,52 @@ public class ReferenceManifestValidator {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the X509Certificate found in a PEM file.
|
||||
* Unchecked type case warnings are suppressed because the CertificateFactory
|
||||
* implements X509Certificate objects explicitly.
|
||||
* @param filename pem file
|
||||
* @return a list containing all X509Certificates extracted
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<X509Certificate> parseCertificatesFromPem(String filename) {
|
||||
List<X509Certificate> certificates = null;
|
||||
FileInputStream fis = null;
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
fis = new FileInputStream(filename);
|
||||
bis = new BufferedInputStream(fis);
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
|
||||
while (bis.available() > 0) {
|
||||
certificates =
|
||||
(List<X509Certificate>) certificateFactory.generateCertificates(bis);
|
||||
}
|
||||
|
||||
if (certificates.size() < 1) {
|
||||
System.out.println("ERROR: No certificates parsed from " + filename);
|
||||
}
|
||||
bis.close();
|
||||
} catch (CertificateException e) {
|
||||
System.out.println("Error in certificate factory: " + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error reading from input stream: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
if (bis != null) {
|
||||
bis.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error closing input stream: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the subjectKeyIdentifier from a given X509Certificate.
|
||||
*
|
||||
|
@ -21,10 +21,12 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':HIRS_Utils')
|
||||
|
||||
implementation libs.bouncycastle
|
||||
implementation libs.glassfish.json
|
||||
implementation libs.glassfish.jaxb.runtime
|
||||
implementation libs.jcommander
|
||||
implementation libs.glassfish.json
|
||||
implementation libs.glassfish.jaxb.runtime
|
||||
implementation libs.jcommander
|
||||
implementation libs.jakarta.api
|
||||
implementation libs.jakarta.xml
|
||||
implementation libs.commons.codec
|
||||
@ -51,9 +53,12 @@ jar {
|
||||
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
manifest {
|
||||
attributes("Main-Class": "hirs.swid.Main",
|
||||
'Class-Path':configurations.runtimeClasspath.files.collect { it.getName() }.join(' ')
|
||||
)
|
||||
attributes "Main-Class": "hirs.swid.Main"
|
||||
|
||||
}
|
||||
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
//jar name format: [archiveBaseName]-[archiveAppendix]-[archiveVersion]-[archiveClassifier].[archiveExtension]
|
||||
archiveVersion = jarVersion
|
||||
|
@ -1,6 +1,6 @@
|
||||
#Thu Sep 13 15:33:27 EDT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
|
@ -1,360 +0,0 @@
|
||||
package hirs.swid;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import hirs.swid.ReferenceManifest;
|
||||
import hirs.swid.SwidResource;
|
||||
import hirs.swid.SwidTagConstants;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.UnmarshalException;
|
||||
import jakarta.xml.bind.Unmarshaller;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Log4j2
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Entity
|
||||
public class BaseReferenceManifest extends ReferenceManifest {
|
||||
/**
|
||||
* Holds the name of the 'base64Hash' field.
|
||||
*/
|
||||
public static final String BASE_64_HASH_FIELD = "base64Hash";
|
||||
|
||||
private static JAXBContext jaxbContext;
|
||||
|
||||
@Column
|
||||
@JsonIgnore
|
||||
private String base64Hash = "";
|
||||
@Column
|
||||
private String swidName = null;
|
||||
@Column
|
||||
private int swidCorpus = 0;
|
||||
@Column
|
||||
private String colloquialVersion = null;
|
||||
@Column
|
||||
private String product = null;
|
||||
@Column
|
||||
private String revision = null;
|
||||
@Column
|
||||
private String edition = null;
|
||||
@Column
|
||||
private String rimLinkHash = null;
|
||||
@Column
|
||||
private String bindingSpec = null;
|
||||
@Column
|
||||
private String bindingSpecVersion = null;
|
||||
@Column
|
||||
private String platformVersion = null;
|
||||
@Column
|
||||
private String payloadType = null;
|
||||
@Column
|
||||
private String pcURIGlobal = null;
|
||||
@Column
|
||||
private String pcURILocal = null;
|
||||
|
||||
private String entityName = null;
|
||||
private String entityRegId = null;
|
||||
private String entityRole = null;
|
||||
private String entityThumbprint = null;
|
||||
private String linkHref = null;
|
||||
private String linkRel = null;
|
||||
|
||||
/**
|
||||
* Support constructor for the RIM object.
|
||||
*
|
||||
* @param rimBytes - the file content of the uploaded file.
|
||||
* @throws IOException - thrown if the file is invalid.
|
||||
*/
|
||||
public BaseReferenceManifest(final byte[] rimBytes) throws IOException {
|
||||
this("", rimBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor for the RIM object. This takes in a byte array of a
|
||||
* valid swidtag file and parses the information.
|
||||
*
|
||||
* @param fileName - string representation of the uploaded file.
|
||||
* @param rimBytes byte array representation of the RIM
|
||||
* @throws IOException if unable to unmarshal the string
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:AvoidInlineConditionals")
|
||||
public BaseReferenceManifest(final String fileName, final byte[] rimBytes) throws IOException {
|
||||
super(rimBytes);
|
||||
this.setRimType(BASE_RIM);
|
||||
this.setFileName(fileName);
|
||||
Document document = unmarshallSwidTag(new ByteArrayInputStream(rimBytes));
|
||||
Element softwareIdentity;
|
||||
Element meta;
|
||||
Element entity;
|
||||
Element link;
|
||||
|
||||
MessageDigest digest = null;
|
||||
this.base64Hash = "";
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
this.base64Hash = Base64.getEncoder().encodeToString(
|
||||
digest.digest(rimBytes));
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
log.error(noSaEx);
|
||||
}
|
||||
|
||||
// begin parsing valid swid tag
|
||||
if (document != null) {
|
||||
softwareIdentity = (Element) document.getElementsByTagName(SwidTagConstants.SOFTWARE_IDENTITY).item(0);
|
||||
entity = (Element) document.getElementsByTagName(SwidTagConstants.ENTITY).item(0);
|
||||
link = (Element) document.getElementsByTagName(SwidTagConstants.LINK).item(0);
|
||||
meta = (Element) document.getElementsByTagName(SwidTagConstants.META).item(0);
|
||||
setTagId(softwareIdentity.getAttribute(SwidTagConstants.TAGID));
|
||||
this.swidName = softwareIdentity.getAttribute(SwidTagConstants.NAME);
|
||||
this.swidCorpus = Boolean.parseBoolean(softwareIdentity.getAttribute(SwidTagConstants.CORPUS)) ? 1 : 0;
|
||||
this.setSwidPatch(Boolean.parseBoolean(softwareIdentity.getAttribute(SwidTagConstants.PATCH)));
|
||||
this.setSwidSupplemental(Boolean.parseBoolean(softwareIdentity.getAttribute(SwidTagConstants.SUPPLEMENTAL)));
|
||||
this.setSwidVersion(softwareIdentity.getAttribute(SwidTagConstants.VERSION));
|
||||
this.setSwidTagVersion(softwareIdentity.getAttribute(SwidTagConstants.TAGVERSION));
|
||||
|
||||
parseSoftwareMeta(meta);
|
||||
parseEntity(entity);
|
||||
parseLink(link);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method that parses the SoftwareMeta tag and stores the
|
||||
* information in the class fields.
|
||||
*
|
||||
* @param softwareMeta The object to parse.
|
||||
*/
|
||||
private void parseSoftwareMeta(final Element softwareMeta) {
|
||||
if (softwareMeta != null) {
|
||||
this.colloquialVersion = softwareMeta.getAttribute(SwidTagConstants.COLLOQUIAL_VERSION);
|
||||
this.product = softwareMeta.getAttribute(SwidTagConstants.PRODUCT);
|
||||
this.revision = softwareMeta.getAttribute(SwidTagConstants.REVISION);
|
||||
this.edition = softwareMeta.getAttribute(SwidTagConstants.EDITION);
|
||||
this.rimLinkHash = softwareMeta.getAttribute(SwidTagConstants.RIM_LINK_HASH);
|
||||
this.bindingSpec = softwareMeta.getAttribute(SwidTagConstants.BINDING_SPEC);
|
||||
this.bindingSpecVersion = softwareMeta.getAttribute(SwidTagConstants.BINDING_SPEC_VERSION);
|
||||
this.setPlatformManufacturerId(softwareMeta.getAttribute(SwidTagConstants.PLATFORM_MANUFACTURER_ID));
|
||||
this.setPlatformManufacturer(softwareMeta.getAttribute(SwidTagConstants.PLATFORM_MANUFACTURER_STR));
|
||||
this.setPlatformModel(softwareMeta.getAttribute(SwidTagConstants.PLATFORM_MODEL));
|
||||
this.platformVersion = softwareMeta.getAttribute(SwidTagConstants.PLATFORM_VERSION);
|
||||
this.payloadType = softwareMeta.getAttribute(SwidTagConstants.PAYLOAD_TYPE);
|
||||
this.pcURIGlobal = softwareMeta.getAttribute(SwidTagConstants.PC_URI_GLOBAL);
|
||||
this.pcURILocal = softwareMeta.getAttribute(SwidTagConstants.PC_URI_LOCAL);
|
||||
} else {
|
||||
log.warn("SoftwareMeta Tag not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method that parses the Entity tag and stores the
|
||||
* information in the class fields.
|
||||
*
|
||||
* @param entity The object to parse.
|
||||
*/
|
||||
private void parseEntity(final Element entity) {
|
||||
if (entity != null) {
|
||||
this.entityName = entity.getAttribute(SwidTagConstants.NAME);
|
||||
this.entityRegId = entity.getAttribute(SwidTagConstants.REGID);
|
||||
this.entityRole = entity.getAttribute(SwidTagConstants.ROLE);
|
||||
this.entityThumbprint = entity.getAttribute(SwidTagConstants.THUMBPRINT);
|
||||
} else {
|
||||
log.warn("Entity Tag not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method that parses the Link tag and stores the
|
||||
* information in the class fields.
|
||||
*
|
||||
* @param link The object to parse.
|
||||
*/
|
||||
private void parseLink(final Element link) {
|
||||
if (link != null) {
|
||||
this.linkHref = link.getAttribute(SwidTagConstants.HREF);
|
||||
this.linkRel = link.getAttribute(SwidTagConstants.REL);
|
||||
} else {
|
||||
log.warn("Link Tag not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the .swidtag file at the given filepath against the
|
||||
* schema. A successful validation results in the output of the tag's name
|
||||
* and tagId attributes, otherwise a generic error message is printed.
|
||||
*
|
||||
*/
|
||||
private Element getDirectoryTag() {
|
||||
return getDirectoryTag(new ByteArrayInputStream(getRimBytes()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the .swidtag file at the given filepath against the
|
||||
* schema. A successful validation results in the output of the tag's name
|
||||
* and tagId attributes, otherwise a generic error message is printed.
|
||||
*
|
||||
* @param byteArrayInputStream the location of the file to be validated
|
||||
*/
|
||||
private Element getDirectoryTag(final ByteArrayInputStream byteArrayInputStream) {
|
||||
Document document = unmarshallSwidTag(byteArrayInputStream);
|
||||
Element softwareIdentity =
|
||||
(Element) document.getElementsByTagName("SoftwareIdentity").item(0);
|
||||
if (softwareIdentity != null) {
|
||||
Element directory = (Element) document.getElementsByTagName("Directory").item(0);
|
||||
|
||||
return directory;
|
||||
} else {
|
||||
log.error("Invalid xml for validation, please verify ");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method iterates over the list of File elements under the directory. *
|
||||
*/
|
||||
public List<SwidResource> getFileResources() {
|
||||
return getFileResources(getRimBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method iterates over the list of File elements under the directory.
|
||||
*
|
||||
* @param rimBytes the bytes to find the files
|
||||
*
|
||||
*/
|
||||
public List<SwidResource> getFileResources(final byte[] rimBytes) {
|
||||
Element directoryTag = getDirectoryTag(new ByteArrayInputStream(rimBytes));
|
||||
List<SwidResource> validHashes = new ArrayList<>();
|
||||
NodeList fileNodeList = directoryTag.getChildNodes();
|
||||
Element file = null;
|
||||
SwidResource swidResource = null;
|
||||
for (int i = 0; i < fileNodeList.getLength(); i++) {
|
||||
file = (Element) fileNodeList.item(i);
|
||||
swidResource = new SwidResource();
|
||||
swidResource.setName(file.getAttribute(SwidTagConstants.NAME));
|
||||
swidResource.setSize(file.getAttribute(SwidTagConstants.SIZE));
|
||||
swidResource.setHashValue(file.getAttribute(SwidTagConstants._SHA256_HASH.getPrefix() + ":"
|
||||
+ SwidTagConstants._SHA256_HASH.getLocalPart()));
|
||||
validHashes.add(swidResource);
|
||||
}
|
||||
|
||||
return validHashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method unmarshalls the swidtag found at [path] into a Document object
|
||||
* and validates it according to the schema.
|
||||
*
|
||||
* @param byteArrayInputStream to the input swidtag
|
||||
* @return the Document element at the root of the swidtag
|
||||
*/
|
||||
private Document unmarshallSwidTag(final ByteArrayInputStream byteArrayInputStream) {
|
||||
InputStream is = null;
|
||||
Document document = null;
|
||||
Unmarshaller unmarshaller = null;
|
||||
try {
|
||||
document = removeXMLWhitespace(byteArrayInputStream);
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
|
||||
is = getClass().getClassLoader().getResourceAsStream(SwidTagConstants.SCHEMA_URL);
|
||||
Schema schema = schemaFactory.newSchema(new StreamSource(is));
|
||||
if (jaxbContext == null) {
|
||||
jaxbContext = JAXBContext.newInstance(SCHEMA_PACKAGE);
|
||||
}
|
||||
unmarshaller = jaxbContext.createUnmarshaller();
|
||||
unmarshaller.setSchema(schema);
|
||||
unmarshaller.unmarshal(document);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
} catch (SAXException e) {
|
||||
log.error("Error setting schema for validation!");
|
||||
} catch (UnmarshalException e) {
|
||||
log.error("Error validating swidtag file!");
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("Input file empty.");
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error closing input stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method strips all whitespace from an xml file, including indents and spaces
|
||||
* added for human-readability.
|
||||
*
|
||||
* @param byteArrayInputStream to the xml file
|
||||
* @return Document object without whitespace
|
||||
*/
|
||||
private Document removeXMLWhitespace(final ByteArrayInputStream byteArrayInputStream) throws IOException {
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Source source = new StreamSource(
|
||||
getClass().getClassLoader().getResourceAsStream("identity_transform.xslt"));
|
||||
Document document = null;
|
||||
if (byteArrayInputStream.available() > 0) {
|
||||
try {
|
||||
Transformer transformer = tf.newTransformer(source);
|
||||
DOMResult result = new DOMResult();
|
||||
transformer.transform(new StreamSource(byteArrayInputStream), result);
|
||||
document = (Document) result.getNode();
|
||||
} catch (TransformerConfigurationException tcEx) {
|
||||
log.error("Error configuring transformer!");
|
||||
} catch (TransformerException tEx) {
|
||||
log.error("Error transforming input!");
|
||||
}
|
||||
} else {
|
||||
throw new IOException("Input file is empty!");
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ReferenceManifest{swidName=%s,"
|
||||
+ "platformManufacturer=%s,"
|
||||
+ " platformModel=%s,"
|
||||
+ "tagId=%s, base64Hash=%s}",
|
||||
swidName, this.getPlatformManufacturer(),
|
||||
this.getPlatformModel(), getTagId(), this.getBase64Hash());
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package hirs.swid;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Enum of digest algorithms. The enum values also provide a standardized
|
||||
* algorithm name. The standardized algorithm name is a String of the algorithm
|
||||
* name as defined by Java.
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DigestAlgorithm {
|
||||
/**
|
||||
* MD2 digest algorithm.
|
||||
*/
|
||||
MD2("MD2", 16),
|
||||
/**
|
||||
* MD5 digest algorithm.
|
||||
*/
|
||||
MD5("MD5", 16),
|
||||
/**
|
||||
* SHA-1 digest algorithm.
|
||||
*/
|
||||
SHA1("SHA-1", 20),
|
||||
/**
|
||||
* SHA-256 digest algorithm.
|
||||
*/
|
||||
SHA256("SHA-256", 32),
|
||||
/**
|
||||
* SHA-384 digest algorithm.
|
||||
*/
|
||||
SHA384("SHA-384", 48),
|
||||
/**
|
||||
* SHA-512 digest algorithm.
|
||||
*/
|
||||
SHA512("SHA-512", 64),
|
||||
/**
|
||||
* Condition used when an algorithm is not specified and
|
||||
* the size doesn't match known digests.
|
||||
*/
|
||||
UNSPECIFIED("NOT SPECIFIED", Integer.BYTES);
|
||||
|
||||
private final String standardAlgorithmName;
|
||||
private final int lengthInBytes;
|
||||
|
||||
/**
|
||||
* Returns a DigestAlgorithm object given a String. The String is expected to be one of the
|
||||
* options for standardAlgorithmName. Throws an IllegalArgumentException if no Enum exists with
|
||||
* that value.
|
||||
*
|
||||
* @param standardAlgorithmName
|
||||
* String value of the Enum
|
||||
* @return DigestAlgorithm object
|
||||
*/
|
||||
public static DigestAlgorithm findByString(final String standardAlgorithmName) {
|
||||
for (DigestAlgorithm algorithm: DigestAlgorithm.values()) {
|
||||
if (algorithm.getStandardAlgorithmName().equals(standardAlgorithmName)) {
|
||||
return algorithm;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("No constant with text \"%s\" found",
|
||||
standardAlgorithmName));
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package hirs.swid;
|
||||
|
||||
import hirs.swid.utils.Commander;
|
||||
import com.beust.jcommander.JCommander;
|
||||
import hirs.swid.utils.TimestampArgumentValidator;
|
||||
import hirs.utils.rim.ReferenceManifestValidator;
|
||||
import com.beust.jcommander.JCommander;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -13,14 +14,14 @@ public class Main {
|
||||
JCommander jc = JCommander.newBuilder().addObject(commander).build();
|
||||
jc.parse(args);
|
||||
SwidTagGateway gateway;
|
||||
SwidTagValidator validator;
|
||||
ReferenceManifestValidator validator;
|
||||
|
||||
if (commander.isHelp()) {
|
||||
jc.usage();
|
||||
System.out.println(commander.printHelpExamples());
|
||||
} else {
|
||||
if (!commander.getVerifyFile().isEmpty()) {
|
||||
validator = new SwidTagValidator();
|
||||
validator = new ReferenceManifestValidator();
|
||||
System.out.println(commander.toString());
|
||||
String verifyFile = commander.getVerifyFile();
|
||||
String rimel = commander.getRimEventLog();
|
||||
@ -37,7 +38,7 @@ public class Main {
|
||||
System.out.println("A single cert cannot be used for verification. " +
|
||||
"The signing cert will be searched for in the trust store.");
|
||||
}
|
||||
validator.validateSwidTag(verifyFile);
|
||||
validator.validateSwidtagFile(verifyFile);
|
||||
} else {
|
||||
System.out.println("Need a RIM file to validate!");
|
||||
System.exit(1);
|
||||
|
@ -1,165 +0,0 @@
|
||||
package hirs.swid;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.common.base.Preconditions;
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class represents the Reference Integrity Manifest object that will be
|
||||
* loaded into the DB and displayed in the ACA.
|
||||
*/
|
||||
@Getter @Setter @ToString
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
|
||||
@Log4j2
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
@Table(name = "ReferenceManifest")
|
||||
@Access(AccessType.FIELD)
|
||||
public class ReferenceManifest {
|
||||
|
||||
/**
|
||||
* Holds the name of the 'hexDecHash' field.
|
||||
*/
|
||||
public static final String HEX_DEC_HASH_FIELD = "hexDecHash";
|
||||
/**
|
||||
* String for display of a Base RIM.
|
||||
*/
|
||||
public static final String BASE_RIM = "Base";
|
||||
/**
|
||||
* String for display of a Support RIM.
|
||||
*/
|
||||
public static final String SUPPORT_RIM = "Support";
|
||||
/**
|
||||
* String for display of a Support RIM.
|
||||
*/
|
||||
public static final String MEASUREMENT_RIM = "Measurement";
|
||||
|
||||
/**
|
||||
* String for the xml schema ios standard.
|
||||
*/
|
||||
public static final String SCHEMA_STATEMENT = "ISO/IEC 19770-2:2015 Schema (XSD 1.0) "
|
||||
+ "- September 2015, see http://standards.iso.org/iso/19770/-2/2015/schema.xsd";
|
||||
/**
|
||||
* String for the xml schema URL file name.
|
||||
*/
|
||||
public static final String SCHEMA_URL = "swid_schema.xsd";
|
||||
/**
|
||||
* String for the language type for the xml schema.
|
||||
*/
|
||||
public static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
|
||||
/**
|
||||
* String for the package location of the xml generated java files.
|
||||
*/
|
||||
public static final String SCHEMA_PACKAGE = "hirs.utils.xjc";
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
@Column(columnDefinition = "mediumblob", nullable = false)
|
||||
private byte[] rimBytes;
|
||||
@EqualsAndHashCode.Include
|
||||
@Column(nullable = false)
|
||||
private String rimType = "Base";
|
||||
@Column
|
||||
private String tagId = null;
|
||||
@Column
|
||||
private boolean swidPatch = false;
|
||||
@Column
|
||||
private boolean swidSupplemental = false;
|
||||
@Column
|
||||
private String platformManufacturer = null;
|
||||
@Column
|
||||
private String platformManufacturerId = null;
|
||||
@Column
|
||||
private String swidTagVersion = null;
|
||||
@Column
|
||||
private String swidVersion = null;
|
||||
@Column
|
||||
private String platformModel = null;
|
||||
@Column(nullable = false)
|
||||
private String fileName = null;
|
||||
@JdbcTypeCode(java.sql.Types.VARCHAR)
|
||||
@Column
|
||||
private UUID associatedRim;
|
||||
@Column
|
||||
private String deviceName;
|
||||
@Column
|
||||
private String hexDecHash = "";
|
||||
@Column
|
||||
private String eventLogHash = "";
|
||||
|
||||
/**
|
||||
* Default constructor necessary for Hibernate.
|
||||
*/
|
||||
protected ReferenceManifest() {
|
||||
super();
|
||||
this.rimBytes = null;
|
||||
this.rimType = null;
|
||||
this.platformManufacturer = null;
|
||||
this.platformManufacturerId = null;
|
||||
this.platformModel = null;
|
||||
this.fileName = BASE_RIM;
|
||||
this.tagId = null;
|
||||
this.associatedRim = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for ingesting the bytes of the file content.
|
||||
* @param rimBytes - file contents.
|
||||
*/
|
||||
public ReferenceManifest(final byte[] rimBytes) {
|
||||
Preconditions.checkArgument(rimBytes != null,
|
||||
"Cannot construct a RIM from a null byte array");
|
||||
|
||||
Preconditions.checkArgument(rimBytes.length > 0,
|
||||
"Cannot construct a RIM from an empty byte array");
|
||||
|
||||
this.rimBytes = rimBytes.clone();
|
||||
MessageDigest digest = null;
|
||||
this.hexDecHash = "";
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
this.hexDecHash = Hex.encodeHexString(
|
||||
digest.digest(rimBytes));
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
log.error(noSaEx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the Reference Integrity Manifest as a byte array.
|
||||
*
|
||||
* @return array of bytes
|
||||
*/
|
||||
@JsonIgnore
|
||||
public byte[] getRimBytes() {
|
||||
if (this.rimBytes != null) {
|
||||
return this.rimBytes.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isBase() {
|
||||
return rimType.equals(BASE_RIM);
|
||||
}
|
||||
|
||||
public boolean isSupport() {
|
||||
return rimType.equals(SUPPORT_RIM);
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
package hirs.swid;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import hirs.swid.DigestAlgorithm;
|
||||
import hirs.swid.xjc.File;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This object is used to represent the content of a Swid Tags Directory
|
||||
* section.
|
||||
*/
|
||||
@ToString
|
||||
public class SwidResource {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String name, size, hashValue;
|
||||
@Getter
|
||||
private String rimFormat, rimType, rimUriGlobal;
|
||||
private DigestAlgorithm digest = DigestAlgorithm.SHA1;
|
||||
@Getter
|
||||
private boolean validFileSize = false;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SwidResource() {
|
||||
name = null;
|
||||
size = null;
|
||||
rimFormat = null;
|
||||
rimType = null;
|
||||
rimUriGlobal = null;
|
||||
hashValue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main constructor that processes a {@code hirs.utils.xjc.File}.
|
||||
*
|
||||
* @param file {@link File}
|
||||
* @param digest algorithm associated with pcr values
|
||||
*/
|
||||
public SwidResource(final File file, final DigestAlgorithm digest) {
|
||||
Preconditions.checkArgument(file != null,
|
||||
"Cannot construct a RIM Resource from a null File object");
|
||||
|
||||
this.name = file.getName();
|
||||
// at this time, there is a possibility to get an object with
|
||||
// no size even though it is required.
|
||||
if (file.getSize() != null) {
|
||||
this.size = file.getSize().toString();
|
||||
} else {
|
||||
this.size = BigInteger.ZERO.toString();
|
||||
}
|
||||
|
||||
for (Map.Entry<QName, String> entry
|
||||
: file.getOtherAttributes().entrySet()) {
|
||||
switch (entry.getKey().getLocalPart()) {
|
||||
case "supportRIMFormat":
|
||||
this.rimFormat = entry.getValue();
|
||||
break;
|
||||
case "supportRIMType":
|
||||
this.rimType = entry.getValue();
|
||||
break;
|
||||
case "supportRIMURIGlobal":
|
||||
this.rimUriGlobal = entry.getValue();
|
||||
break;
|
||||
case "hash":
|
||||
this.hashValue = entry.getValue();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
this.digest = digest;
|
||||
// tpmWhiteList = new TpmWhiteListBaseline(this.name);
|
||||
}
|
||||
}
|
@ -1,516 +0,0 @@
|
||||
package hirs.swid;
|
||||
|
||||
import hirs.swid.utils.HashSwid;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.UnmarshalException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.crypto.AlgorithmMethod;
|
||||
import javax.xml.crypto.KeySelector;
|
||||
import javax.xml.crypto.KeySelectorException;
|
||||
import javax.xml.crypto.KeySelectorResult;
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.XMLCryptoContext;
|
||||
import javax.xml.crypto.XMLStructure;
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
import javax.xml.crypto.dsig.XMLSignatureException;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyValue;
|
||||
import javax.xml.crypto.dsig.keyinfo.X509Data;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class handles validating base Reference Integrity Manifest files.
|
||||
*/
|
||||
public class SwidTagValidator {
|
||||
private Unmarshaller unmarshaller;
|
||||
private String rimEventLog;
|
||||
private String certificateFile;
|
||||
private String trustStoreFile;
|
||||
private List<X509Certificate> trustStore;
|
||||
|
||||
/**
|
||||
* Ensure that BouncyCastle is configured as a javax.security.Security provider, as this
|
||||
* class expects it to be available.
|
||||
*/
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for rimel file path.
|
||||
* @param rimEventLog the rimel file
|
||||
*/
|
||||
public void setRimEventLog(String rimEventLog) {
|
||||
this.rimEventLog = rimEventLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the truststore file path.
|
||||
* @param trustStoreFile the truststore
|
||||
*/
|
||||
public void setTrustStoreFile(String trustStoreFile) {
|
||||
this.trustStoreFile = trustStoreFile;
|
||||
}
|
||||
|
||||
public SwidTagValidator() {
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(SwidTagConstants.SCHEMA_PACKAGE);
|
||||
unmarshaller = jaxbContext.createUnmarshaller();
|
||||
rimEventLog = "";
|
||||
certificateFile = "";
|
||||
trustStoreFile = SwidTagConstants.DEFAULT_KEYSTORE_FILE;
|
||||
} catch (JAXBException e) {
|
||||
System.out.println("Error initializing JAXBContext: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method validates the .swidtag file at the given filepath against the
|
||||
* schema. A successful validation results in the output of the tag's name
|
||||
* and tagId attributes, otherwise a generic error message is printed.
|
||||
*
|
||||
* @param path the location of the file to be validated
|
||||
*/
|
||||
public boolean validateSwidTag(String path) {
|
||||
Document document = unmarshallSwidTag(path);
|
||||
Element softwareIdentity = (Element) document.getElementsByTagName("SoftwareIdentity").item(0);
|
||||
StringBuilder si = new StringBuilder("Base RIM detected:\n");
|
||||
si.append("SoftwareIdentity name: " + softwareIdentity.getAttribute("name") + "\n");
|
||||
si.append("SoftwareIdentity tagId: " + softwareIdentity.getAttribute("tagId") + "\n");
|
||||
System.out.println(si.toString());
|
||||
Element file = (Element) document.getElementsByTagName("File").item(0);
|
||||
try {
|
||||
validateFile(file);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
System.out.println("Signature core validity: " + validateSignedXMLDocument(document));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a hirs.swid.xjc.File from an indirect payload
|
||||
*/
|
||||
private boolean validateFile(Element file) throws Exception {
|
||||
String filepath;
|
||||
if (!rimEventLog.isEmpty()) {
|
||||
filepath = rimEventLog;
|
||||
} else {
|
||||
filepath = file.getAttribute(SwidTagConstants.NAME);
|
||||
}
|
||||
System.out.println("Support rim found at " + filepath);
|
||||
if (HashSwid.get256Hash(filepath).equals(
|
||||
file.getAttribute(SwidTagConstants._SHA256_HASH.getPrefix() + ":" +
|
||||
SwidTagConstants._SHA256_HASH.getLocalPart()))) {
|
||||
System.out.println("Support RIM hash verified!" + System.lineSeparator());
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("Support RIM hash does not match Base RIM!" + System.lineSeparator());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates a signed XML document.
|
||||
* First, the signing certificate is either parsed from the embedded X509Certificate element or
|
||||
* generated from the Modulus and Exponent elements.
|
||||
* Next, the signature is inspected for two things:
|
||||
* 1. valid signature
|
||||
* 2. valid certificate chain
|
||||
* @param doc XML document
|
||||
* @return true if both the signature and cert chain are valid; false otherwise
|
||||
*/
|
||||
private boolean validateSignedXMLDocument(Document doc) {
|
||||
try {
|
||||
DOMValidateContext context;
|
||||
CredentialParser cp = new CredentialParser();
|
||||
X509Certificate signingCert = null;
|
||||
trustStore = cp.parseCertsFromPEM(trustStoreFile);
|
||||
X509KeySelector keySelector = new X509KeySelector();
|
||||
NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
|
||||
if (nodes.getLength() == 0) {
|
||||
throw new Exception("Signature element not found!");
|
||||
} else {
|
||||
context = new DOMValidateContext(keySelector, nodes.item(0));
|
||||
}
|
||||
NodeList keyName = doc.getElementsByTagName("KeyName");
|
||||
if (keyName.getLength() > 0) {
|
||||
String skId = keyName.item(0).getTextContent();
|
||||
if (skId != null && !skId.isEmpty()) {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
String trustedSkId = cp.getCertificateSubjectKeyIdentifier(trustedCert);
|
||||
if (skId.equals(trustedSkId)) {
|
||||
signingCert = trustedCert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (signingCert != null) {
|
||||
context = new DOMValidateContext(signingCert.getPublicKey(),
|
||||
nodes.item(0));
|
||||
} else {
|
||||
System.out.println("Issuer certificate with subject key identifier = "
|
||||
+ skId + " not found");
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Base RIM must have a non-empty, non-null " +
|
||||
"Subject Key Identifier (SKID) in the <KeyName> element");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
|
||||
XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
|
||||
boolean signatureIsValid = signature.validate(context);
|
||||
System.out.println("Signature validity: " + signatureIsValid);
|
||||
if (signingCert == null) {
|
||||
signingCert = keySelector.getSigningCert();
|
||||
}
|
||||
cp.setCertificate(signingCert);
|
||||
System.out.println(System.lineSeparator() + cp.getCertificateAuthorityInfoAccess());
|
||||
return signatureIsValid;
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("Error parsing truststore: " + e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.out.println("Error instantiating a KeyFactory to generate pk: "
|
||||
+ e.getMessage());
|
||||
} catch (InvalidKeySpecException e) {
|
||||
System.out.println("Failed to generate a pk from swidtag: " + e.getMessage());
|
||||
} catch (MarshalException | XMLSignatureException e) {
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal class handles parsing the public key from a KeyInfo element.
|
||||
*/
|
||||
public class X509KeySelector extends KeySelector {
|
||||
PublicKey publicKey;
|
||||
X509Certificate signingCert;
|
||||
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public X509Certificate getSigningCert() {
|
||||
return signingCert;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method extracts a public key from either an X509Certificate element
|
||||
* or a KeyValue element. If the public key's algorithm matches the declared
|
||||
* algorithm it is returned in a KeySelecctorResult.
|
||||
* @param keyinfo the KeyInfo element
|
||||
* @param purpose
|
||||
* @param algorithm the encapsulating signature's declared signing algorithm
|
||||
* @param context
|
||||
* @return a KeySelectorResult if the public key's algorithm matches the declared algorithm
|
||||
* @throws KeySelectorException if the algorithms do not match
|
||||
*/
|
||||
public KeySelectorResult select(final KeyInfo keyinfo,
|
||||
final KeySelector.Purpose purpose,
|
||||
final AlgorithmMethod algorithm,
|
||||
final XMLCryptoContext context)
|
||||
throws KeySelectorException {
|
||||
Iterator keyinfoItr = keyinfo.getContent().iterator();
|
||||
while(keyinfoItr.hasNext()) {
|
||||
XMLStructure element = (XMLStructure) keyinfoItr.next();
|
||||
if (element instanceof X509Data) {
|
||||
X509Data data = (X509Data) element;
|
||||
Iterator dataItr = data.getContent().iterator();
|
||||
while (dataItr.hasNext()) {
|
||||
Object object = dataItr.next();
|
||||
if (object instanceof X509Certificate) {
|
||||
X509Certificate embeddedCert = (X509Certificate) object;
|
||||
try {
|
||||
if (isCertChainValid(embeddedCert)) {
|
||||
publicKey = ((X509Certificate) embeddedCert).getPublicKey();
|
||||
signingCert = embeddedCert;
|
||||
System.out.println("Certificate chain validity: true");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Certificate chain invalid: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (element instanceof KeyValue) {
|
||||
try {
|
||||
PublicKey pk = ((KeyValue) element).getPublicKey();
|
||||
if (isPublicKeyTrusted(pk)) {
|
||||
publicKey = pk;
|
||||
try {
|
||||
System.out.println("Certificate chain validity: "
|
||||
+ isCertChainValid(signingCert));
|
||||
} catch (Exception e) {
|
||||
System.out.println("Certificate chain invalid: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (KeyException e) {
|
||||
System.out.println("Unable to convert KeyValue data to PK.");
|
||||
}
|
||||
}
|
||||
if (publicKey != null) {
|
||||
if (areAlgorithmsEqual(algorithm.getAlgorithm(), publicKey.getAlgorithm())) {
|
||||
return new
|
||||
SwidTagValidator.X509KeySelector.RIMKeySelectorResult(publicKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new KeySelectorException("No key found!");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks that the signature and public key algorithms match.
|
||||
* @param uri to match the signature algorithm
|
||||
* @param name to match the public key algorithm
|
||||
* @return true if both match, false otherwise
|
||||
*/
|
||||
public boolean areAlgorithmsEqual(String uri, String name) {
|
||||
return uri.equals(SwidTagConstants.SIGNATURE_ALGORITHM_RSA_SHA256)
|
||||
&& name.equalsIgnoreCase("RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the cert chain for a given certificate. The truststore is iterated
|
||||
* over until a root CA is found, otherwise an error is returned.
|
||||
* @param cert the certificate at the start of the chain
|
||||
* @return true if the chain is valid
|
||||
* @throws Exception if a valid chain is not found in the truststore
|
||||
*/
|
||||
private boolean isCertChainValid(final X509Certificate cert)
|
||||
throws Exception {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new Exception("Null certificate or truststore received");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new Exception("Truststore is empty");
|
||||
}
|
||||
|
||||
final String INT_CA_ERROR = "Intermediate CA found, searching for root CA";
|
||||
String errorMessage = "";
|
||||
X509Certificate startOfChain = cert;
|
||||
do {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
boolean isIssuer = areYouMyIssuer(startOfChain, trustedCert);
|
||||
boolean isSigner = areYouMySigner(startOfChain, trustedCert);
|
||||
if (isIssuer && isSigner) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
return true;
|
||||
} else {
|
||||
startOfChain = trustedCert;
|
||||
errorMessage = INT_CA_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isIssuer) {
|
||||
errorMessage = "Issuer cert not found";
|
||||
} else if (!isSigner) {
|
||||
errorMessage = "Signing cert not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (errorMessage.equals(INT_CA_ERROR));
|
||||
|
||||
throw new Exception("Error while validating cert chain: " + errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's issuerDN matches issuer's subjectDN.
|
||||
* @param cert the signed certificate
|
||||
* @param issuer the signing certificate
|
||||
* @return true if they match, false if not
|
||||
* @throws Exception if either argument is null
|
||||
*/
|
||||
private boolean areYouMyIssuer(final X509Certificate cert, final X509Certificate issuer)
|
||||
throws Exception {
|
||||
if (cert == null || issuer == null) {
|
||||
throw new Exception("Cannot verify issuer, null certificate received");
|
||||
}
|
||||
X500Principal issuerDN = new X500Principal(cert.getIssuerX500Principal().getName());
|
||||
return issuer.getSubjectX500Principal().equals(issuerDN);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's signature matches signer's public key.
|
||||
* @param cert the signed certificate
|
||||
* @param signer the signing certificate
|
||||
* @return true if they match
|
||||
* @throws Exception if an error occurs or there is no match
|
||||
*/
|
||||
private boolean areYouMySigner(final X509Certificate cert, final X509Certificate signer)
|
||||
throws Exception {
|
||||
if (cert == null || signer == null) {
|
||||
throw new Exception("Cannot verify signature, null certificate received");
|
||||
}
|
||||
try {
|
||||
cert.verify(signer.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("Signing algorithm in signing cert not supported");
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new Exception("Signing certificate key does not match signature");
|
||||
} catch (NoSuchProviderException e) {
|
||||
throw new Exception("Error with BouncyCastleProvider: " + e.getMessage());
|
||||
} catch (SignatureException e) {
|
||||
String error = "Error with signature: " + e.getMessage()
|
||||
+ System.lineSeparator()
|
||||
+ "Certificate needed for verification is missing: "
|
||||
+ signer.getSubjectX500Principal().getName();
|
||||
throw new Exception(error);
|
||||
} catch (CertificateException e) {
|
||||
throw new Exception("Encoding error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a given certificate is self signed or not.
|
||||
* @param cert the cert to check
|
||||
* @return true if self signed, false if not
|
||||
*/
|
||||
private boolean isSelfSigned(final X509Certificate cert) {
|
||||
return cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method compares a public key against those in the truststore.
|
||||
* @param pk a public key
|
||||
* @return true if pk is found in the trust store, false otherwise
|
||||
*/
|
||||
private boolean isPublicKeyTrusted(final PublicKey pk) {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
if (Arrays.equals(trustedCert.getPublicKey().getEncoded(),
|
||||
pk.getEncoded())) {
|
||||
signingCert = trustedCert;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class RIMKeySelectorResult implements KeySelectorResult {
|
||||
private Key key;
|
||||
|
||||
public RIMKeySelectorResult(Key key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Key getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method unmarshalls the swidtag found at [path] into a Document object
|
||||
* and validates it according to the schema.
|
||||
*
|
||||
* @param path to the input swidtag
|
||||
* @return the SoftwareIdentity element at the root of the swidtag
|
||||
* @throws IOException if the swidtag cannot be unmarshalled or validated
|
||||
*/
|
||||
private Document unmarshallSwidTag(String path) {
|
||||
InputStream is = null;
|
||||
Document document = null;
|
||||
try {
|
||||
document = removeXMLWhitespace(path);
|
||||
is = SwidTagGateway.class.getClassLoader().getResourceAsStream(SwidTagConstants.SCHEMA_URL);
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(SwidTagConstants.SCHEMA_LANGUAGE);
|
||||
Schema schema = schemaFactory.newSchema(new StreamSource(is));
|
||||
unmarshaller.setSchema(schema);
|
||||
unmarshaller.unmarshal(document);
|
||||
} catch (IOException e) {
|
||||
System.out.println(e.getMessage());
|
||||
} catch (SAXException e) {
|
||||
System.out.println("Error setting schema for validation!");
|
||||
} catch (UnmarshalException e) {
|
||||
System.out.println("Error validating swidtag file!");
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.out.println("Input file empty.");
|
||||
} catch (JAXBException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error closing input stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method strips all whitespace from an xml file, including indents and spaces
|
||||
* added for human-readability.
|
||||
* @param path to the xml file
|
||||
* @return Document object without whitespace
|
||||
*/
|
||||
private Document removeXMLWhitespace(String path) throws IOException {
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Source source = new StreamSource(
|
||||
SwidTagGateway.class.getClassLoader().getResourceAsStream("identity_transform.xslt"));
|
||||
Document document = null;
|
||||
File input = new File(path);
|
||||
if (input.length() > 0) {
|
||||
try {
|
||||
Transformer transformer = tf.newTransformer(source);
|
||||
DOMResult result = new DOMResult();
|
||||
transformer.transform(new StreamSource(input), result);
|
||||
document = (Document) result.getNode();
|
||||
} catch (TransformerConfigurationException e) {
|
||||
System.out.println("Error configuring transformer!");
|
||||
e.printStackTrace();
|
||||
} catch (TransformerException e) {
|
||||
System.out.println("Error transforming input!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
throw new IOException("Input file is empty!");
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package hirs.swid;
|
||||
|
||||
import hirs.utils.rim.ReferenceManifestValidator;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@ -12,7 +14,7 @@ import org.testng.annotations.Test;
|
||||
|
||||
public class TestSwidTagGateway {
|
||||
private SwidTagGateway gateway;
|
||||
private SwidTagValidator validator;
|
||||
private ReferenceManifestValidator validator;
|
||||
private final String DEFAULT_OUTPUT = "generated_swidTag.swidtag";
|
||||
private final String BASE_USER_CERT = "generated_user_cert.swidtag";
|
||||
private final String BASE_USER_CERT_EMBED = "generated_user_cert_embed.swidtag";
|
||||
@ -40,7 +42,7 @@ public class TestSwidTagGateway {
|
||||
gateway = new SwidTagGateway();
|
||||
gateway.setRimEventLog(SUPPORT_RIM_FILE);
|
||||
gateway.setAttributesFile(ATTRIBUTES_FILE);
|
||||
validator = new SwidTagValidator();
|
||||
validator = new ReferenceManifestValidator();
|
||||
validator.setRimEventLog(SUPPORT_RIM_FILE);
|
||||
validator.setTrustStoreFile(CA_CHAIN_FILE);
|
||||
}
|
||||
@ -67,7 +69,7 @@ public class TestSwidTagGateway {
|
||||
expectedFile = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResourceAsStream(BASE_USER_CERT);
|
||||
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(DEFAULT_OUTPUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +88,7 @@ public class TestSwidTagGateway {
|
||||
expectedFile = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResourceAsStream(BASE_USER_CERT_EMBED);
|
||||
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(DEFAULT_OUTPUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +103,7 @@ public class TestSwidTagGateway {
|
||||
expectedFile = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResourceAsStream(BASE_DEFAULT_CERT);
|
||||
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(DEFAULT_OUTPUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,7 +120,7 @@ public class TestSwidTagGateway {
|
||||
expectedFile = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResourceAsStream(BASE_RFC3339_TIMESTAMP);
|
||||
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(DEFAULT_OUTPUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +137,7 @@ public class TestSwidTagGateway {
|
||||
expectedFile = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResourceAsStream(BASE_RFC3852_TIMESTAMP);
|
||||
Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(DEFAULT_OUTPUT));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,11 +145,11 @@ public class TestSwidTagGateway {
|
||||
* -v <path>
|
||||
*/
|
||||
|
||||
public void testValidateSwidTag() {
|
||||
public void testvalidateSwidtagFile() {
|
||||
String filepath = TestSwidTagGateway.class.getClassLoader()
|
||||
.getResource(BASE_USER_CERT).getPath();
|
||||
System.out.println("Validating file at " + filepath);
|
||||
Assert.assertTrue(validator.validateSwidTag(filepath));
|
||||
Assert.assertTrue(validator.validateSwidtagFile(filepath));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user