Hi , being not new in Java but certainly not an expert I have tried to get some example code (Programming With the Java XML Digital Signature API ) working in my own environment.
The signature creation part worked without any issues and created the transformed XML code with an Signature element attached to the payload. So for now I assume that this is functioning as expected.
However I have doubts about the signature validation mechanism which validates the signature in this file. The same keystore is used for both generation and validation.
Since I don't see any separate include mechanisme I will give the code inline as follows:
boolean verifySAMLSignature() throws Exception {
// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Create a Reference to the enveloped document (in this case,
// you are signing the whole document, so a URI of "" signifies
// that, and also specify the SHA1 digest algorithm and
// the ENVELOPED Transform.
Reference ref = fac.newReference("", fac.newDigestMethod(
DigestMethod.SHA1, null), Collections.singletonList(fac
.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null)), null, null);
// Create the SignedInfo.
SignedInfo si = fac
.newSignedInfo(fac.newCanonicalizationMethod(
CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null), fac
.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
Collections.singletonList(ref));
// Load the KeyStore and get the signing key and certificate.
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("mykeystore.jks"),
"assertive".toCharArray());
keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("forSigning",
new KeyStore.PasswordProtection("assertive".toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
ki = kif.newKeyInfo(Collections.singletonList(xd));
} catch (Exception ex) {
ex.printStackTrace();
}
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
doc = dbf.newDocumentBuilder().parse(
new FileInputStream("signedPurchaseOrder.xml"));
} catch (Exception ex) {
ex.printStackTrace();
}
// Validating an XML Signature
// You will now learn to use the API to validate an XML signature over
// the
// contents of the PurchaseOrder element that you just signed. Code
// Sample 5
// shows the key steps in validating an XML signature.
// Code Sample 5
System.out.println(getNiceLyFormattedXMLDocument(doc));
// Find Signature element.
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
"Signature");
if (nl.getLength() == 0) {
System.out.println("Cannot find Signature element");
throw new Exception("Cannot find Signature element");
}
System.out.println("NodeList element " + nl.item(0).getTextContent());
// Create a DOMValidateContext and specify a KeySelector
// and document context.
valContext = new DOMValidateContext(new X509KeySelector(), nl.item(0));
// System.out
// .println("valcontext: " + valContext.getNode().getNodeValue());
// Unmarshal the XMLSignature.
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
System.out.println("signature: " + signature.getId());
// Validate the XMLSignature.
boolean coreValidity = signature.validate(valContext);
// If the signature is invalid, some additional code is necessary to
// determine the cause of the failure, as Code Sample 6 shows.
// Code Sample 6
// Check core validation status.
if (coreValidity == false) {
System.err.println("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("signature validation status: " + sv);
if (sv == false) {
// Check the validation status of each Reference.
Iterator i = signature.getSignedInfo().getReferences()
.iterator();
for (int j = 0; i.hasNext(); j++) {
boolean refValid = ((Reference) i.next())
.validate(valContext);
System.out.println("ref[" + j + "] validity status: "
+ refValid);
}
}
} else {
System.out.println("Signature passed core validation");
}
return coreValidity;
}
// The KeySelector Class
// A KeySelector is an abstract class that is responsible for finding and
// returning a key using the data contained in a KeyInfo object. In Code
// Sample 5, you passed an X509KeySelector object, which is a very simple
// implementation of KeySelector that looks for and returns a public key of
// an X.509 certificate, as Code Sample 8 shows.
// Code Sample 8
public class X509KeySelector extends KeySelector {
public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose, AlgorithmMethod method,
XMLCryptoContext context) throws KeySelectorException {
Iterator ki = keyInfo.getContent().iterator();
while (ki.hasNext()) {
XMLStructure info = (XMLStructure) ki.next();
if (!(info instanceof X509Data))
continue;
X509Data x509Data = (X509Data) info;
Iterator xi = x509Data.getContent().iterator();
while (xi.hasNext()) {
Object o = xi.next();
if (!(o instanceof X509Certificate))
continue;
final PublicKey key = ((X509Certificate) o).getPublicKey();
// Make sure the algorithm is compatible
// with the method.
if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
return new KeySelectorResult() {
public Key getKey() {
return key;
}
};
}
}
}
throw new KeySelectorException("No key found!");
}
}
-------------------------------------
I have also listed the X509KeySelector class code since it is quite mysterious to me - so I don't know exactly what is does and whether it is correct.
The code for the XMLSignatureFactury and the keystore is identical to the generation path and I assume is correct. Same goes for the Documentbuilder to read the inputfile.
Finding the signature (into Nodelist) also seems to work judging from println listing its content.
I am however doubtful about the DOMValidateContext call. All my efforts to print something out of the "valContext" seem to result in "null".
The remaining code however indicates that the validation is successful.
So I am a number of questions:
1. Does this code look correct in what it does in the XMLSignature area (don't judge its style please ) ?
2. How can I verify that valContext contains anything useful ? (or could it be empty in some scenarios) ?
3. what would be the best approach for another test setup in order to generate a validation failure (e.g. loading another keystore with a different cert/pub key) ?
Thanks
Peter Geerts