From 1a26802ee02e7ba67343e1d6736fd81f33804bd6 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Wed, 1 Nov 2023 09:36:44 -0400 Subject: [PATCH 1/2] Modify ReferenceManifestValidator to incorporate necessary methods from SwidtagValidator --- .../utils/rim/ReferenceManifestValidator.java | 253 ++++++++++++++++-- 1 file changed, 232 insertions(+), 21 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java index 2f3bcc14..f9ecee08 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java @@ -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,12 @@ 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.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 +27,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 +38,14 @@ 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.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 +70,9 @@ public class ReferenceManifestValidator { private PublicKey publicKey; private Schema schema; private String subjectKeyIdentifier; + private String rimEventLog; + private String trustStoreFile; + private List trustStore; private boolean signatureValid, supportRimValid; /** @@ -125,6 +127,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 +185,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( @@ -260,7 +279,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 +311,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 +524,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 parseCertificatesFromPem(String filename) { + List 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) 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. * From 0236a6b9c698afac445de999108cfe29f7f30fd6 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:20:24 -0400 Subject: [PATCH 2/2] Add methods to ReferenceManifestValidator to facilitate swidtag XML validation. Modify rimtool classes to reference ReferenceManifestValidator. Modify build.gradle to build a fat jar. --- .../utils/rim/ReferenceManifestValidator.java | 105 ++++ tools/tcg_rim_tool/build.gradle | 17 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../java/hirs/swid/BaseReferenceManifest.java | 360 ------------ .../main/java/hirs/swid/DigestAlgorithm.java | 66 --- .../src/main/java/hirs/swid/Main.java | 9 +- .../java/hirs/swid/ReferenceManifest.java | 165 ------ .../src/main/java/hirs/swid/SwidResource.java | 83 --- .../main/java/hirs/swid/SwidTagValidator.java | 516 ------------------ .../java/hirs/swid/TestSwidTagGateway.java | 20 +- 10 files changed, 134 insertions(+), 1211 deletions(-) delete mode 100644 tools/tcg_rim_tool/src/main/java/hirs/swid/BaseReferenceManifest.java delete mode 100644 tools/tcg_rim_tool/src/main/java/hirs/swid/DigestAlgorithm.java delete mode 100644 tools/tcg_rim_tool/src/main/java/hirs/swid/ReferenceManifest.java delete mode 100644 tools/tcg_rim_tool/src/main/java/hirs/swid/SwidResource.java delete mode 100644 tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java diff --git a/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java index f9ecee08..3f07cf20 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/rim/ReferenceManifestValidator.java @@ -10,6 +10,7 @@ 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; @@ -39,6 +40,8 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; 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; @@ -91,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. * @@ -218,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. @@ -234,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. * diff --git a/tools/tcg_rim_tool/build.gradle b/tools/tcg_rim_tool/build.gradle index 89e43dfa..e68d74b0 100644 --- a/tools/tcg_rim_tool/build.gradle +++ b/tools/tcg_rim_tool/build.gradle @@ -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 diff --git a/tools/tcg_rim_tool/gradle/wrapper/gradle-wrapper.properties b/tools/tcg_rim_tool/gradle/wrapper/gradle-wrapper.properties index 7dee5e87..c30b486a 100644 --- a/tools/tcg_rim_tool/gradle/wrapper/gradle-wrapper.properties +++ b/tools/tcg_rim_tool/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/BaseReferenceManifest.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/BaseReferenceManifest.java deleted file mode 100644 index d9781c5e..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/BaseReferenceManifest.java +++ /dev/null @@ -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 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 getFileResources(final byte[] rimBytes) { - Element directoryTag = getDirectoryTag(new ByteArrayInputStream(rimBytes)); - List 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()); - } -} diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/DigestAlgorithm.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/DigestAlgorithm.java deleted file mode 100644 index 35111dc3..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/DigestAlgorithm.java +++ /dev/null @@ -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)); - } -} \ No newline at end of file diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java index 6356f0cd..b3185b22 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/Main.java @@ -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); diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/ReferenceManifest.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/ReferenceManifest.java deleted file mode 100644 index 325597f3..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/ReferenceManifest.java +++ /dev/null @@ -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); - } -} diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidResource.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidResource.java deleted file mode 100644 index b79f090d..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidResource.java +++ /dev/null @@ -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 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); - } -} \ No newline at end of file diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java deleted file mode 100644 index 10d83a91..00000000 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java +++ /dev/null @@ -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 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 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; - } -} diff --git a/tools/tcg_rim_tool/src/test/java/hirs/swid/TestSwidTagGateway.java b/tools/tcg_rim_tool/src/test/java/hirs/swid/TestSwidTagGateway.java index a1768ef7..29e3b71c 100644 --- a/tools/tcg_rim_tool/src/test/java/hirs/swid/TestSwidTagGateway.java +++ b/tools/tcg_rim_tool/src/test/java/hirs/swid/TestSwidTagGateway.java @@ -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 */ - 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)); } /**