Moved KeyValue processing to KeySelector inner class

This commit is contained in:
chubtub 2022-04-14 11:04:57 -04:00
parent e5e6db75f4
commit 4b3c01f990

View File

@ -39,11 +39,9 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.Key; import java.security.Key;
import java.security.KeyException; import java.security.KeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.PublicKey; import java.security.PublicKey;
@ -52,9 +50,7 @@ import java.security.SignatureException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -66,6 +62,7 @@ public class SwidTagValidator {
private String rimEventLog; private String rimEventLog;
private String certificateFile; private String certificateFile;
private String trustStoreFile; private String trustStoreFile;
private List<X509Certificate> trustStore;
/** /**
* Ensure that BouncyCastle is configured as a javax.security.Security provider, as this * Ensure that BouncyCastle is configured as a javax.security.Security provider, as this
@ -159,7 +156,7 @@ public class SwidTagValidator {
DOMValidateContext context; DOMValidateContext context;
CredentialParser cp = new CredentialParser(); CredentialParser cp = new CredentialParser();
X509Certificate signingCert = null; X509Certificate signingCert = null;
List<X509Certificate> trustStore = cp.parseCertsFromPEM(trustStoreFile); trustStore = cp.parseCertsFromPEM(trustStoreFile);
X509KeySelector keySelector = new X509KeySelector(); X509KeySelector keySelector = new X509KeySelector();
NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) { if (nodes.getLength() == 0) {
@ -167,30 +164,9 @@ public class SwidTagValidator {
} else { } else {
context = new DOMValidateContext(keySelector, nodes.item(0)); context = new DOMValidateContext(keySelector, nodes.item(0));
} }
NodeList embeddedCert = doc.getElementsByTagName("X509Certificate"); NodeList keyName = doc.getElementsByTagName("KeyName");
if (embeddedCert.getLength() > 0) { if (keyName.getLength() > 0) {
signingCert = cp.parseCertFromPEMString(embeddedCert.item(0).getTextContent()); String skId = keyName.item(0).getTextContent();
} else {
NodeList keyValue = doc.getElementsByTagName("KeyValue");
if (keyValue.getLength() > 0) {
String modulus = doc.getElementsByTagName("Modulus").item(0).getTextContent();
String exponent =
doc.getElementsByTagName("Exponent").item(0).getTextContent();
PublicKey signingKey = calculatePublicKey(modulus, exponent);
for (X509Certificate trustedCert : trustStore) {
System.out.println(trustedCert.getPublicKey().toString());
if (Arrays.equals(trustedCert.getPublicKey().getEncoded(),
signingKey.getEncoded())) {
signingCert = trustedCert;
break;
}
}
if (signingCert == null) {
System.out.println("Calculated public key not found: " + signingKey.toString());
System.exit(1);
}
} else {
String skId = doc.getElementsByTagName("KeyName").item(0).getTextContent();
if (skId != null && !skId.isEmpty()) { if (skId != null && !skId.isEmpty()) {
for (X509Certificate trustedCert : trustStore) { for (X509Certificate trustedCert : trustStore) {
String trustedSkId = cp.getCertificateSubjectKeyIdentifier(trustedCert); String trustedSkId = cp.getCertificateSubjectKeyIdentifier(trustedCert);
@ -199,27 +175,30 @@ public class SwidTagValidator {
break; break;
} }
} }
if (signingCert == null) { if (signingCert != null) {
context = new DOMValidateContext(signingCert.getPublicKey(),
nodes.item(0));
} else {
System.out.println("Issuer certificate with subject key identifier = " System.out.println("Issuer certificate with subject key identifier = "
+ skId + " not found"); + skId + " not found");
System.exit(1); System.exit(1);
} }
} else { } else {
System.out.println("No credentials found with which to validate this swidtag"); System.out.println("Base RIM must have a non-empty, non-null " +
"Subject Key Identifier (SKID) in the <KeyName> element");
System.exit(1); System.exit(1);
} }
context = new DOMValidateContext(signingCert.getPublicKey(), nodes.item(0));
} }
}
cp.setCertificate(signingCert);
System.out.println(cp.getCertificateAuthorityInfoAccess());
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = sigFactory.unmarshalXMLSignature(context); XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
boolean signatureIsValid = signature.validate(context); boolean signatureIsValid = signature.validate(context);
boolean certChainIsValid = validateCertChain(signingCert, trustStore);
System.out.println("Signature validity: " + signatureIsValid); System.out.println("Signature validity: " + signatureIsValid);
System.out.println("Cert chain validity: " + certChainIsValid); if (signingCert == null) {
return signatureIsValid && certChainIsValid; signingCert = keySelector.getSigningCert();
}
cp.setCertificate(signingCert);
System.out.println(System.lineSeparator() + cp.getCertificateAuthorityInfoAccess());
return signatureIsValid;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
System.out.println("Error parsing truststore: " + e.getMessage()); System.out.println("Error parsing truststore: " + e.getMessage());
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
@ -237,37 +216,104 @@ public class SwidTagValidator {
} }
/** /**
* This method calculates an RSA public key from the <KeyValue> element of an XML * This internal class handles parsing the public key from a KeyInfo element.
* signature block.
*
* @param mod the modulus string
* @param exp the exponent string
* @return the calculated public key
*/ */
private PublicKey calculatePublicKey(final String mod, final String exp) public class X509KeySelector extends KeySelector {
throws NoSuchAlgorithmException, InvalidKeySpecException{ PublicKey publicKey;
X509Certificate signingCert;
System.out.println("Decoding " + exp); public PublicKey getPublicKey() {
BigInteger exponent = new BigInteger(Base64.getMimeDecoder().decode(exp)); return publicKey;
System.out.println("MIME: " + exponent); }
System.out.println("Decoding " + mod);
BigInteger modulus = new BigInteger(Base64.getMimeDecoder().decode(mod));
System.out.println("MIME: " + modulus);
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); public X509Certificate getSigningCert() {
return keyFactory.generatePublic(new RSAPublicKeySpec(modulus, exponent)); 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 * 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. * over until a root CA is found, otherwise an error is returned.
* @param cert the certificate at the start of the chain * @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 * @return true if the chain is valid
* @throws Exception if a valid chain is not found in the truststore * @throws Exception if a valid chain is not found in the truststore
*/ */
private boolean validateCertChain(final X509Certificate cert, private boolean isCertChainValid(final X509Certificate cert)
final List<X509Certificate> trustStore)
throws Exception { throws Exception {
if (cert == null || trustStore == null) { if (cert == null || trustStore == null) {
throw new Exception("Null certificate or truststore received"); throw new Exception("Null certificate or truststore received");
@ -361,65 +407,20 @@ public class SwidTagValidator {
} }
/** /**
* This internal class handles parsing the public key from a KeyInfo element. * 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
*/ */
public class X509KeySelector extends KeySelector { private boolean isPublicKeyTrusted(final PublicKey pk) {
/** for (X509Certificate trustedCert : trustStore) {
* This method extracts a public key from either an X509Certificate element if (Arrays.equals(trustedCert.getPublicKey().getEncoded(),
* or a KeyValue element. If the public key's algorithm matches the declared pk.getEncoded())) {
* algorithm it is returned in a KeySelecctorResult. signingCert = trustedCert;
* @param keyinfo the KeyInfo element return true;
* @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();
PublicKey publicKey = null;
System.out.println("Parsing KeyInfo");
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) {
publicKey = ((X509Certificate) object).getPublicKey();
} }
} }
} else if (element instanceof KeyValue) {
try {
publicKey = ((KeyValue) element).getPublicKey();
System.out.println("PK parsed from KeyValue: " + publicKey.toString());
} 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!");
}
/** return false;
* 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");
} }
private class RIMKeySelectorResult implements KeySelectorResult { private class RIMKeySelectorResult implements KeySelectorResult {