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 07bc88a0..b1fe58bc 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 @@ -49,6 +49,7 @@ public class Main { String jksTruststoreFile = commander.getTruststoreFile(); String certificateFile = commander.getPublicCertificate(); String privateKeyFile = commander.getPrivateKeyFile(); + boolean embeddedCert = commander.isEmbedded(); boolean defaultKey = commander.isDefaultKey(); String rimEventLog = commander.getRimEventLog(); switch (createType) { @@ -63,6 +64,9 @@ public class Main { gateway.setDefaultCredentials(false); gateway.setPemCertificateFile(certificateFile); gateway.setPemPrivateKeyFile(privateKeyFile); + if (embeddedCert) { + gateway.setEmbeddedCert(true); + } } else if (defaultKey){ gateway.setDefaultCredentials(true); gateway.setJksTruststoreFile(SwidTagConstants.DEFAULT_KEYSTORE_FILE); diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagConstants.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagConstants.java index c5dd2af1..350e6b40 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagConstants.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagConstants.java @@ -15,7 +15,7 @@ public class SwidTagConstants { public static final String DEFAULT_KEYSTORE_FILE = "/opt/hirs/rimtool/keystore.jks"; public static final String DEFAULT_KEYSTORE_PASSWORD = "password"; public static final String DEFAULT_PRIVATE_KEY_ALIAS = "selfsigned"; - public static final String DEFAULT_ATTRIBUTES_FILE = "rim_fields.json"; + public static final String DEFAULT_ATTRIBUTES_FILE = "/opt/hirs/rimtool/rim_fields.json"; public static final String DEFAULT_ENGLISH = "en"; public static final String SIGNATURE_ALGORITHM_RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java index 55ca8416..d3c715c6 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagGateway.java @@ -53,6 +53,7 @@ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; +import java.security.KeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateException; @@ -76,6 +77,7 @@ public class SwidTagGateway { private String jksTruststoreFile; private String pemPrivateKeyFile; private String pemCertificateFile; + private boolean embeddedCert; private String rimEventLog; /** @@ -88,6 +90,7 @@ public class SwidTagGateway { attributesFile = SwidTagConstants.DEFAULT_ATTRIBUTES_FILE; defaultCredentials = true; pemCertificateFile = ""; + embeddedCert = false; rimEventLog = ""; } catch (JAXBException e) { System.out.println("Error initializing jaxbcontext: " + e.getMessage()); @@ -99,7 +102,7 @@ public class SwidTagGateway { * * @param attributesFile */ - public void setAttributesFile(String attributesFile) { + public void setAttributesFile(final String attributesFile) { this.attributesFile = attributesFile; } @@ -109,7 +112,7 @@ public class SwidTagGateway { * @param defaultCredentials * @return */ - public void setDefaultCredentials(boolean defaultCredentials) { + public void setDefaultCredentials(final boolean defaultCredentials) { this.defaultCredentials = defaultCredentials; } @@ -118,7 +121,7 @@ public class SwidTagGateway { * * @param jksTruststoreFile */ - public void setJksTruststoreFile(String jksTruststoreFile) { + public void setJksTruststoreFile(final String jksTruststoreFile) { this.jksTruststoreFile = jksTruststoreFile; } @@ -127,7 +130,7 @@ public class SwidTagGateway { * * @param pemPrivateKeyFile */ - public void setPemPrivateKeyFile(String pemPrivateKeyFile) { + public void setPemPrivateKeyFile(final String pemPrivateKeyFile) { this.pemPrivateKeyFile = pemPrivateKeyFile; } @@ -136,16 +139,25 @@ public class SwidTagGateway { * * @param pemCertificateFile */ - public void setPemCertificateFile(String pemCertificateFile) { + public void setPemCertificateFile(final String pemCertificateFile) { this.pemCertificateFile = pemCertificateFile; } + /** + * Setter to embed certificate file in signature block + * + * @param embeddedCert + */ + public void setEmbeddedCert(final boolean embeddedCert) { + this.embeddedCert = embeddedCert; + } + /** * Setter for event log support RIM * * @param rimEventLog */ - public void setRimEventLog(String rimEventLog) { + public void setRimEventLog(final String rimEventLog) { this.rimEventLog = rimEventLog; } @@ -210,7 +222,7 @@ public class SwidTagGateway { * * @param swidTag */ - public void writeSwidTagFile(Document swidTag, String output) { + public void writeSwidTagFile(final Document swidTag, final String output) { try { TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); @@ -238,7 +250,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return SoftwareIdentity object created from the properties */ - private SoftwareIdentity createSwidTag(JsonObject jsonObject) { + private SoftwareIdentity createSwidTag(final JsonObject jsonObject) { SoftwareIdentity swidTag = objectFactory.createSoftwareIdentity(); swidTag.setLang(SwidTagConstants.DEFAULT_ENGLISH); String name = jsonObject.getString(SwidTagConstants.NAME, ""); @@ -271,7 +283,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return Entity object created from the properties */ - private Entity createEntity(JsonObject jsonObject) { + private Entity createEntity(final JsonObject jsonObject) { boolean isTagCreator = false; Entity entity = objectFactory.createEntity(); String name = jsonObject.getString(SwidTagConstants.NAME, ""); @@ -309,7 +321,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return Link element created from the properties */ - private Link createLink(JsonObject jsonObject) { + private Link createLink(final JsonObject jsonObject) { Link link = objectFactory.createLink(); String href = jsonObject.getString(SwidTagConstants.HREF, ""); if (!href.isEmpty()) { @@ -330,7 +342,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return the Meta element created from the properties */ - private SoftwareMeta createSoftwareMeta(JsonObject jsonObject) { + private SoftwareMeta createSoftwareMeta(final JsonObject jsonObject) { SoftwareMeta softwareMeta = objectFactory.createSoftwareMeta(); Map attributes = softwareMeta.getOtherAttributes(); addNonNullAttribute(attributes, SwidTagConstants._COLLOQUIAL_VERSION, @@ -379,7 +391,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return the Payload object created */ - private ResourceCollection createPayload(JsonObject jsonObject) { + private ResourceCollection createPayload(final JsonObject jsonObject) { ResourceCollection payload = objectFactory.createResourceCollection(); Map attributes = payload.getOtherAttributes(); addNonNullAttribute(attributes, SwidTagConstants._N8060_ENVVARPREFIX, @@ -388,6 +400,12 @@ public class SwidTagGateway { jsonObject.getString(SwidTagConstants._N8060_ENVVARSUFFIX.getLocalPart(), "")); addNonNullAttribute(attributes, SwidTagConstants._N8060_PATHSEPARATOR, jsonObject.getString(SwidTagConstants._N8060_PATHSEPARATOR.getLocalPart(), "")); + addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_FORMAT, + jsonObject.getString(SwidTagConstants.SUPPORT_RIM_FORMAT, "")); + addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_TYPE, + jsonObject.getString(SwidTagConstants.SUPPORT_RIM_TYPE, "")); + addNonNullAttribute(attributes, SwidTagConstants._SUPPORT_RIM_URI_GLOBAL, + jsonObject.getString(SwidTagConstants.SUPPORT_RIM_URI_GLOBAL, "")); return payload; } @@ -398,7 +416,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return Directory object created from the properties */ - private Directory createDirectory(JsonObject jsonObject) { + private Directory createDirectory(final JsonObject jsonObject) { Directory directory = objectFactory.createDirectory(); directory.setName(jsonObject.getString(SwidTagConstants.NAME, "")); Map attributes = directory.getOtherAttributes(); @@ -428,7 +446,7 @@ public class SwidTagGateway { * @param jsonObject the Properties object containing parameters from file * @return File object created from the properties */ - private hirs.swid.xjc.File createFile(JsonObject jsonObject) { + private hirs.swid.xjc.File createFile(final JsonObject jsonObject) { hirs.swid.xjc.File file = objectFactory.createFile(); file.setName(jsonObject.getString(SwidTagConstants.NAME, "")); Map attributes = file.getOtherAttributes(); @@ -448,7 +466,8 @@ public class SwidTagGateway { jsonObject.getString(SwidTagConstants.SUPPORT_RIM_URI_GLOBAL, "")); File rimEventLogFile = new File(rimEventLog); file.setSize(new BigInteger(Long.toString(rimEventLogFile.length()))); - addNonNullAttribute(attributes, SwidTagConstants._SHA256_HASH, HashSwid.get256Hash(rimEventLog)); + addNonNullAttribute(attributes, SwidTagConstants._SHA256_HASH, + HashSwid.get256Hash(rimEventLog)); return file; } @@ -460,7 +479,8 @@ public class SwidTagGateway { * @param key * @param value */ - private void addNonNullAttribute(Map attributes, QName key, String value) { + private void addNonNullAttribute(final Map attributes, + final QName key, String value) { if (!value.isEmpty()) { attributes.put(key, value); } @@ -470,20 +490,23 @@ public class SwidTagGateway { * This method signs a SoftwareIdentity with an xmldsig in compatibility mode. * Current assumptions: digest method SHA256, signature method SHA256, enveloped signature */ - private Document signXMLDocument(JAXBElement swidTag) { + private Document signXMLDocument(final JAXBElement swidTag) { Document doc = null; try { XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); Reference reference = sigFactory.newReference( "", sigFactory.newDigestMethod(DigestMethod.SHA256, null), - Collections.singletonList(sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), + Collections.singletonList(sigFactory.newTransform(Transform.ENVELOPED, + (TransformParameterSpec) null)), null, null ); SignedInfo signedInfo = sigFactory.newSignedInfo( - sigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), - sigFactory.newSignatureMethod(SwidTagConstants.SIGNATURE_ALGORITHM_RSA_SHA256, null), + sigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, + (C14NMethodParameterSpec) null), + sigFactory.newSignatureMethod(SwidTagConstants.SIGNATURE_ALGORITHM_RSA_SHA256, + null), Collections.singletonList(reference) ); List keyInfoElements = new ArrayList(); @@ -500,11 +523,15 @@ public class SwidTagGateway { cp.parsePEMCredentials(pemCertificateFile, pemPrivateKeyFile); X509Certificate certificate = cp.getCertificate(); privateKey = cp.getPrivateKey(); - ArrayList x509Content = new ArrayList(); - x509Content.add(certificate.getSubjectX500Principal().getName()); - x509Content.add(certificate); - X509Data data = kiFactory.newX509Data(x509Content); - keyInfoElements.add(data); + if (embeddedCert) { + ArrayList x509Content = new ArrayList(); + x509Content.add(certificate.getSubjectX500Principal().getName()); + x509Content.add(certificate); + X509Data data = kiFactory.newX509Data(x509Content); + keyInfoElements.add(data); + } else { + keyInfoElements.add(kiFactory.newKeyValue(certificate.getPublicKey())); + } } KeyInfo keyinfo = kiFactory.newKeyInfo(keyInfoElements); @@ -526,6 +553,9 @@ public class SwidTagGateway { System.out.println("Error marshaling signed swidtag: " + e.getMessage()); } catch (MarshalException | XMLSignatureException e) { System.out.println("Error while signing SoftwareIdentity: " + e.getMessage()); + } catch (KeyException e) { + System.out.println("Public key algorithm not recognized or supported: " + + e.getMessage()); } return doc; 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 index e1313f0f..a07b4586 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/SwidTagValidator.java @@ -4,7 +4,6 @@ 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.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -25,6 +24,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; @@ -39,10 +39,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.Key; -import java.security.KeyFactory; +import java.security.KeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PublicKey; @@ -51,9 +50,7 @@ import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; -import java.security.spec.RSAPublicKeySpec; import java.util.Arrays; -import java.util.Base64; import java.util.Iterator; import java.util.List; @@ -65,6 +62,7 @@ public class SwidTagValidator { 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 @@ -117,7 +115,7 @@ public class SwidTagValidator { System.out.println(si.toString()); Element file = (Element) document.getElementsByTagName("File").item(0); validateFile(file); - System.out.println("Signature core validity: " + validateSignedXMLDocument(document)); + validateSignedXMLDocument(document); return true; } @@ -158,40 +156,49 @@ public class SwidTagValidator { DOMValidateContext context; CredentialParser cp = new CredentialParser(); X509Certificate signingCert = null; - List trustStore = cp.parseCertsFromPEM(trustStoreFile); + 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!"); - } - NodeList embeddedCert = doc.getElementsByTagName("X509Certificate"); - if (embeddedCert.getLength() > 0) { - context = new DOMValidateContext(new X509KeySelector(), nodes.item(0)); - signingCert = cp.parseCertFromPEMString(embeddedCert.item(0).getTextContent()); } else { - String skId = doc.getElementsByTagName("KeyName").item(0).getTextContent(); - for (X509Certificate trustedCert : trustStore) { - String trustedSkId = cp.getCertificateSubjectKeyIdentifier(trustedCert); - if (skId.equals(trustedSkId)) { - signingCert = trustedCert; - break; + 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) { - System.out.println("Issuer certificate with subject key identifier = " - + skId + " not found"); + 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); } - context = new DOMValidateContext(signingCert.getPublicKey(), nodes.item(0)); } - cp.setCertificate(signingCert); - System.out.println(cp.getCertificateAuthorityInfoAccess()); XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); XMLSignature signature = sigFactory.unmarshalXMLSignature(context); boolean signatureIsValid = signature.validate(context); - boolean certChainIsValid = validateCertChain(signingCert, trustStore); System.out.println("Signature validity: " + signatureIsValid); - System.out.println("Cert chain validity: " + certChainIsValid); - return signatureIsValid && certChainIsValid; + 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) { @@ -208,112 +215,21 @@ public class SwidTagValidator { return false; } - /** - * 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 - * @param trustStore from which to find the chain of intermediate and root CAs - * @return true if the chain is valid - * @throws Exception if a valid chain is not found in the truststore - */ - private boolean validateCertChain(final X509Certificate cert, - final List trustStore) - 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 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 @@ -339,12 +255,41 @@ public class SwidTagValidator { 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 SwidTagValidator.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 + SwidTagValidator.X509KeySelector.RIMKeySelectorResult(publicKey); + } } } throw new KeySelectorException("No key found!"); @@ -361,6 +306,123 @@ public class SwidTagValidator { && 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; diff --git a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java index 9af08b5d..a769409b 100644 --- a/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java +++ b/tools/tcg_rim_tool/src/main/java/hirs/swid/utils/Commander.java @@ -36,10 +36,13 @@ public class Commander { description = "The public key certificate to embed in the base RIM created by " + "this tool.") private String publicCertificate = ""; - @Parameter(names = {"-d", "--default-key"}, + @Parameter(names = {"-e", "--embed-cert"}, order = 7, + description = "Embed the provided certificate in the signed swidtag.") + private boolean embedded = false; + @Parameter(names = {"-d", "--default-key"}, order = 8, description = "Use default signing credentials.") private boolean defaultKey = false; - @Parameter(names = {"-l", "--rimel "}, order = 7, + @Parameter(names = {"-l", "--rimel "}, order = 9, description = "The TCG eventlog file to use as a support RIM.") private String rimEventLog = ""; @@ -73,6 +76,8 @@ public class Commander { return publicCertificate; } + public boolean isEmbedded() { return embedded; } + public boolean isDefaultKey() { return defaultKey; } public String getRimEventLog() { return rimEventLog; } @@ -82,11 +87,12 @@ public class Commander { sb.append("Create a base RIM using the values in attributes.json; " + "sign it with the default keystore; "); sb.append("and write the data to base_rim.swidtag:\n\n"); - sb.append("\t\t-c base -a attributes.json -d -l support_rim.bin -o base_rim.swidtag\n\n\n"); + sb.append("\t\t-c base -a attributes.json -d -l support_rim.bin -o base_rim.swidtag" + + "\n\n\n"); sb.append("Create a base RIM using the default attribute values; "); sb.append("sign it using privateKey.pem; embed cert.pem in the signature block; "); sb.append("and write the data to console output:\n\n"); - sb.append("\t\t-c base -l support_rim.bin -k privateKey.pem -p cert.pem\n\n\n"); + sb.append("\t\t-c base -l support_rim.bin -k privateKey.pem -p cert.pem -e\n\n\n"); sb.append("Validate a base RIM using an external support RIM to override the "); sb.append("payload file:\n\n"); sb.append("\t\t-v base_rim.swidtag -l support_rim.bin\n\n\n"); @@ -107,7 +113,9 @@ public class Commander { } else if (!this.getPrivateKeyFile().isEmpty() && !this.getPublicCertificate().isEmpty()) { sb.append("Private key file: " + this.getPrivateKeyFile() + System.lineSeparator()); - sb.append("Public certificate: " + this.getPublicCertificate() + System.lineSeparator()); + sb.append("Public certificate: " + this.getPublicCertificate() + + System.lineSeparator()); + sb.append("Embedded certificate: " + this.isEmbedded() + System.lineSeparator()); } else if (this.isDefaultKey()){ sb.append("Truststore file: default (" + SwidTagConstants.DEFAULT_KEYSTORE_FILE + ")" + System.lineSeparator()); 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 02826f0a..09b75977 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 @@ -14,13 +14,21 @@ public class TestSwidTagGateway { private SwidTagGateway gateway; private SwidTagValidator validator; private final String DEFAULT_OUTPUT = "generated_swidTag.swidtag"; - private final String DEFAULT_WITH_CERT = "generated_with_cert.swidtag"; - private final String DEFAULT_NO_CERT = "generated_no_cert.swidtag"; - private final String ATTRIBUTES_FILE = TestSwidTagGateway.class.getClassLoader().getResource("rim_fields.json").getPath(); - private final String JKS_KEYSTORE_FILE = TestSwidTagGateway.class.getClassLoader().getResource("keystore.jks").getPath(); - private final String SIGNING_CERT_FILE = TestSwidTagGateway.class.getClassLoader().getResource("RimSignCert.pem").getPath(); - private final String PRIVATE_KEY_FILE = TestSwidTagGateway.class.getClassLoader().getResource("privateRimKey.pem").getPath(); - private final String SUPPORT_RIM_FILE = TestSwidTagGateway.class.getClassLoader().getResource("TpmLog.bin").getPath(); + private final String BASE_USER_CERT = "generated_user_cert.swidtag"; + private final String BASE_USER_CERT_EMBED = "generated_user_cert_embed.swidtag"; + private final String BASE_DEFAULT_CERT = "generated_default_cert.swidtag"; + private final String ATTRIBUTES_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("rim_fields.json").getPath(); + private final String JKS_KEYSTORE_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("keystore.jks").getPath(); + private final String SIGNING_CERT_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("RimSignCert.pem").getPath(); + private final String PRIVATE_KEY_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("privateRimKey.pem").getPath(); + private final String CA_CHAIN_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("RimCertChain.pem").getPath(); + private final String SUPPORT_RIM_FILE = TestSwidTagGateway.class.getClassLoader() + .getResource("TpmLog.bin").getPath(); private InputStream expectedFile; @BeforeClass @@ -30,6 +38,7 @@ public class TestSwidTagGateway { gateway.setAttributesFile(ATTRIBUTES_FILE); validator = new SwidTagValidator(); validator.setRimEventLog(SUPPORT_RIM_FILE); + validator.setTrustStoreFile(CA_CHAIN_FILE); } @AfterClass @@ -45,13 +54,35 @@ public class TestSwidTagGateway { * where RimSignCert.pem has the AIA extension. */ @Test - public void testCreateBaseWithCert() { + public void testCreateBaseUserCertNotEmbedded() { gateway.setDefaultCredentials(false); gateway.setPemCertificateFile(SIGNING_CERT_FILE); gateway.setPemPrivateKeyFile(PRIVATE_KEY_FILE); + gateway.setEmbeddedCert(false); gateway.generateSwidTag(DEFAULT_OUTPUT); - expectedFile = TestSwidTagGateway.class.getClassLoader().getResourceAsStream(DEFAULT_WITH_CERT); + expectedFile = TestSwidTagGateway.class.getClassLoader() + .getResourceAsStream(BASE_USER_CERT); Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT)); + Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT)); + } + + /** + * This test creates the following base RIM: + * -c base -l TpmLog.bin -k privateRimKey.pem -p RimSignCert.pem -e + * And then validates it: + * -v [base RIM] -l TpmLog.bin -t RimCertChain.pem + */ + @Test + public void testCreateBaseUserCertEmbedded() { + gateway.setDefaultCredentials(false); + gateway.setPemCertificateFile(SIGNING_CERT_FILE); + gateway.setPemPrivateKeyFile(PRIVATE_KEY_FILE); + gateway.setEmbeddedCert(true); + gateway.generateSwidTag(DEFAULT_OUTPUT); + expectedFile = TestSwidTagGateway.class.getClassLoader() + .getResourceAsStream(BASE_USER_CERT_EMBED); + Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT)); + Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT)); } /** @@ -59,21 +90,24 @@ public class TestSwidTagGateway { * -c base -l TpmLog.bin */ @Test - public void testCreateBaseWithoutCert() { + public void testCreateBaseDefaultCert() { gateway.setDefaultCredentials(true); gateway.setJksTruststoreFile(JKS_KEYSTORE_FILE); gateway.generateSwidTag(DEFAULT_OUTPUT); - expectedFile = TestSwidTagGateway.class.getClassLoader().getResourceAsStream(DEFAULT_NO_CERT); + expectedFile = TestSwidTagGateway.class.getClassLoader() + .getResourceAsStream(BASE_DEFAULT_CERT); Assert.assertTrue(compareFileBytesToExpectedFile(DEFAULT_OUTPUT)); + Assert.assertTrue(validator.validateSwidTag(DEFAULT_OUTPUT)); } /** * This test corresponds to the arguments: * -v */ - @Test + public void testValidateSwidTag() { - String filepath = TestSwidTagGateway.class.getClassLoader().getResource(DEFAULT_WITH_CERT).getPath(); + String filepath = TestSwidTagGateway.class.getClassLoader() + .getResource(BASE_USER_CERT).getPath(); System.out.println("Validating file at " + filepath); Assert.assertTrue(validator.validateSwidTag(filepath)); } diff --git a/tools/tcg_rim_tool/src/test/resources/RimCertChain.pem b/tools/tcg_rim_tool/src/test/resources/RimCertChain.pem new file mode 100644 index 00000000..1580a790 --- /dev/null +++ b/tools/tcg_rim_tool/src/test/resources/RimCertChain.pem @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIDjDCCAnSgAwIBAgIJALEA1Q472tZoMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwI +UENDbGllbnQxEjAQBgNVBAMMCUV4YW1wbGVDQTAeFw0yMDAyMTAxNzI2MDdaFw0y +OTEyMTkxNzI2MDdaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UE +CgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxEjAQBgNVBAMMCUV4YW1wbGVD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPN0k+ULqFxdHZ14CCio +HAvn56T1Ca4t3ClmZoHSAiKsqzLV+rErk5SbMTIdi0vHQ+3sPYf9Opy0EeUXzh4J +g6CeGdDn247has1k135KBD9iJCaErJfZPnJ22CjKey8rvJM8fH3CAR7M/5uwYcPH +yRICwGAJMA/Qss4nsMRQpfZg4ReKVW+kAoa9eekG3q1sLu/QlCb0NC766X0ANP+8 +AuGuHJmNV22fjvwSNfWbsJElcMrLbK4kliPyy05YVs19p+cBM1ADxGw2fJqsNsUy +34SXL1ATqOp7VCslRR5TJBzhxfM56xZbszry7BaqTSFDRGn1FuMw/4+qtPMAB88u +eXECAwEAAaNjMGEwHQYDVR0OBBYEFEahuO3bpnFf0NLneoo8XW6aw5Y4MB8GA1Ud +IwQYMBaAFEahuO3bpnFf0NLneoo8XW6aw5Y4MA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQCwCUSV6VjOR+v85z18q5UX +bla0gEsfbc2mx0kGtNqi2im2Xt8UoSJDnfMXzfQq3IP3en943mqgIeYUl3f9UQBT +KgGfyHNbEfa0FzqfKpxJdT37C9ilSQ85GtThffc4I50QgBHaRXOvwBdrGpU2O11V +x35VLyYoycIlg+CizVywEX53aoMil1hEbv0TPtbNnFZGwM/fxvere65GeQld9gEP +9krGtSXYlMktvr66cqPzmG0ciA6dMBZN8dpTgUopmYNz8HVoHDq/KBmXYA7CMzrX +pVNx4kMW/KxA+XAHT82xE7PCiLIJx4z9uPn0O4PBDw0tQ0mxuDpeoi1i9PuBfe6Y +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgIJAIKly+6bklZlMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwI +UENDbGllbnQxEjAQBgNVBAMMCUV4YW1wbGVDQTAeFw0yMDA2MTExNjUzMDFaFw0z +MDA0MjAxNjUzMDFaMFwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UE +CgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxGzAZBgNVBAMMEmV4YW1wbGUu +UklNLnNpZ25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKd1lWGk +SRuxAAY2wHag2GVxUk1dZx2PTpfQOflvLeccAVwa8mQhlsRERq+QK8ilj8Xfqs44 +/nBaccZDOjdfIxIUCMfwhGXjxCaqZbgTucNsExDnu4arTGraoAwzHg0cVLiKT/Cx +j9NL4dcMgxRXsPdHfXb0923C7xYd2t2qfW05umgaj7qeQl6c68CFNsGX4JA8rWFQ +ZvvGx5DGlK4KTcjPuQQINs5fxasNKqLY2hq+z82x/rqwr2hmyizD6FpFSyIABPEM +PfB036GEhRwu1WEMkq8yIp2jgRUoFYke9pB3ph9pVow0Hh4mNFSKD4pP41VSKY1n +us83mdkuukPy5o0CAwEAAaNvMG0wHQYDVR0OBBYEFC/euOfQMKIgnaoBhhqWT+3s +8rzBMB8GA1UdIwQYMBaAFEahuO3bpnFf0NLneoo8XW6aw5Y4MAkGA1UdEwQCMAAw +CwYDVR0PBAQDAgbAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUA +A4IBAQC1mG0naE0W4E9vujPhygf7LXHMFkMPs5uWyvkxe4zWgTg0RHTClbOFJQJ+ +pGLOcthSG6vIC6xYJxT5EKtB9rzRlEYHOi4MxuwXz9rLWQhA2zdbSo54Fb/BPoca +5K9kxvAanRltEfqEFhCcRmqIX1i6mpOWiZsrdMs7IflHKBsylUTn+v636BAz3p2H +8/lpJbF4LUFUxFU5FWB3tLuasxYTsbeE6YyNAnQIS95ML7c5H8z2aEQs5TCNHZJD +yc0PZT2aPOuEj5lGv9oyBHbYDitszUWSVxF7z86uVGmYR/2oTIj6tqb+IwuvFtnO +wiXFRS5ctLCdESr3SjdQF5wmIN4n +-----END CERTIFICATE----- diff --git a/tools/tcg_rim_tool/src/test/resources/generated_no_cert.swidtag b/tools/tcg_rim_tool/src/test/resources/generated_default_cert.swidtag similarity index 76% rename from tools/tcg_rim_tool/src/test/resources/generated_no_cert.swidtag rename to tools/tcg_rim_tool/src/test/resources/generated_default_cert.swidtag index 29193469..ea454392 100644 --- a/tools/tcg_rim_tool/src/test/resources/generated_no_cert.swidtag +++ b/tools/tcg_rim_tool/src/test/resources/generated_default_cert.swidtag @@ -3,7 +3,7 @@ - + @@ -17,14 +17,14 @@ - K3XoBeYvgJBAKl8z273sL7z38qLLVBKLfUPt/gPUzBI= + 97uWB7zSsO5WaGbrcQrlKd1Bju0aDTjK1/ktUYBje8A= - cIl1gPsUyEj2gDv3HTWNFDVxtcBjz4Revxxf2LJejtOXQW8mGepZH8CnvgO7zCAbZYlYUZXjYZ9M -jONVv8dcsAjVHRnP6YHywFfmSm8LUCwxsfuZQqn5jClqzu5VaqLzBhuJYvCpiEdIDJwDINQuORUB -nzul1CWc3Sm1Ms2wjlIq5ctWWJcddhdyIOjl8/oD4EC5E2rOSfNcRMZxldXtie9iinFGVbr0YNE+ -+lQ7hAU+SyV8RMx9tGnnsO8otwV4ddF+OfemcbzWGYBenLs3A8ZqWZyTvWphCgGqDUbOLssYciCC -mnYm5QOeh4QcE9H2kqTgZvcyCgPL/hDC7xhyjQ== + N1YtTeo2Ryuj+CtlXIpICEay+ni7vt8+4J7tAsYpa3efnLwtea69PIqEylPWm9LdA8Eo8XDdpgxV +7h3hi2LTOU+Wxq3bLiLamo99T1EtIwl+ZPcOv8bsfEkmShHdMC0dlfcj6r7x4tc0XkNAhhJgfRNz +FsmPWKJb6FYcsHFbHO/Uw1hSokbAGcWWTshEOqvKHMa8UVkrFMUPnrnMtdyJqZlhDBrZHNi4rWth +8TjlUnQVSCF9s9I04FxJ1cUAdeVMHtXKM8Pvjv68PaJMJK73dW5Yd3SbcgoKLesf/HPWeeZL0rr4 +TNjlqJ/wq61Ons45MFG9bIscVbnd+XxFHx8Skw== 2fdeb8e7d030a2209daa01861a964fedecf2bcc1 diff --git a/tools/tcg_rim_tool/src/test/resources/generated_user_cert.swidtag b/tools/tcg_rim_tool/src/test/resources/generated_user_cert.swidtag new file mode 100644 index 00000000..a86b1edb --- /dev/null +++ b/tools/tcg_rim_tool/src/test/resources/generated_user_cert.swidtag @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + 97uWB7zSsO5WaGbrcQrlKd1Bju0aDTjK1/ktUYBje8A= + + + N1YtTeo2Ryuj+CtlXIpICEay+ni7vt8+4J7tAsYpa3efnLwtea69PIqEylPWm9LdA8Eo8XDdpgxV +7h3hi2LTOU+Wxq3bLiLamo99T1EtIwl+ZPcOv8bsfEkmShHdMC0dlfcj6r7x4tc0XkNAhhJgfRNz +FsmPWKJb6FYcsHFbHO/Uw1hSokbAGcWWTshEOqvKHMa8UVkrFMUPnrnMtdyJqZlhDBrZHNi4rWth +8TjlUnQVSCF9s9I04FxJ1cUAdeVMHtXKM8Pvjv68PaJMJK73dW5Yd3SbcgoKLesf/HPWeeZL0rr4 +TNjlqJ/wq61Ons45MFG9bIscVbnd+XxFHx8Skw== + + + + p3WVYaRJG7EABjbAdqDYZXFSTV1nHY9Ol9A5+W8t5xwBXBryZCGWxERGr5AryKWPxd+qzjj+cFpx +xkM6N18jEhQIx/CEZePEJqpluBO5w2wTEOe7hqtMatqgDDMeDRxUuIpP8LGP00vh1wyDFFew90d9 +dvT3bcLvFh3a3ap9bTm6aBqPup5CXpzrwIU2wZfgkDytYVBm+8bHkMaUrgpNyM+5BAg2zl/Fqw0q +otjaGr7PzbH+urCvaGbKLMPoWkVLIgAE8Qw98HTfoYSFHC7VYQySrzIinaOBFSgViR72kHemH2lW +jDQeHiY0VIoPik/jVVIpjWe6zzeZ2S66Q/LmjQ== + AQAB + + + + + diff --git a/tools/tcg_rim_tool/src/test/resources/generated_with_cert.swidtag b/tools/tcg_rim_tool/src/test/resources/generated_user_cert_embed.swidtag similarity index 84% rename from tools/tcg_rim_tool/src/test/resources/generated_with_cert.swidtag rename to tools/tcg_rim_tool/src/test/resources/generated_user_cert_embed.swidtag index f47230cf..dc711f3e 100644 --- a/tools/tcg_rim_tool/src/test/resources/generated_with_cert.swidtag +++ b/tools/tcg_rim_tool/src/test/resources/generated_user_cert_embed.swidtag @@ -3,7 +3,7 @@ - + @@ -17,14 +17,14 @@ - K3XoBeYvgJBAKl8z273sL7z38qLLVBKLfUPt/gPUzBI= + 97uWB7zSsO5WaGbrcQrlKd1Bju0aDTjK1/ktUYBje8A= - cIl1gPsUyEj2gDv3HTWNFDVxtcBjz4Revxxf2LJejtOXQW8mGepZH8CnvgO7zCAbZYlYUZXjYZ9M -jONVv8dcsAjVHRnP6YHywFfmSm8LUCwxsfuZQqn5jClqzu5VaqLzBhuJYvCpiEdIDJwDINQuORUB -nzul1CWc3Sm1Ms2wjlIq5ctWWJcddhdyIOjl8/oD4EC5E2rOSfNcRMZxldXtie9iinFGVbr0YNE+ -+lQ7hAU+SyV8RMx9tGnnsO8otwV4ddF+OfemcbzWGYBenLs3A8ZqWZyTvWphCgGqDUbOLssYciCC -mnYm5QOeh4QcE9H2kqTgZvcyCgPL/hDC7xhyjQ== + N1YtTeo2Ryuj+CtlXIpICEay+ni7vt8+4J7tAsYpa3efnLwtea69PIqEylPWm9LdA8Eo8XDdpgxV +7h3hi2LTOU+Wxq3bLiLamo99T1EtIwl+ZPcOv8bsfEkmShHdMC0dlfcj6r7x4tc0XkNAhhJgfRNz +FsmPWKJb6FYcsHFbHO/Uw1hSokbAGcWWTshEOqvKHMa8UVkrFMUPnrnMtdyJqZlhDBrZHNi4rWth +8TjlUnQVSCF9s9I04FxJ1cUAdeVMHtXKM8Pvjv68PaJMJK73dW5Yd3SbcgoKLesf/HPWeeZL0rr4 +TNjlqJ/wq61Ons45MFG9bIscVbnd+XxFHx8Skw== CN=example.RIM.signer,OU=PCClient,O=Example,ST=VA,C=US