I've been working on a SAML2 SP, and am currently trying to add signatures to my requests.
I have created self-signed certificates via:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3650 -nodes
I generate an XML signature using the following code:
protected X509Certificate getCertificate() throws IOException, CertificateException
{
Path x509crtfile = dataDir.resolve("cert.pem");
PEMParser pem = new PEMParser(Files.newBufferedReader(x509crtfile, Charset.forName("UTF8")));
return new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)pem. readObject());
}
protected RSAPrivateKey getKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
Path x509keyfile = dataDir.resolve("key.pem");
PEMParser pem = new PEMParser(Files.newBufferedReader(x509keyfile, Charset.forName("UTF8")));
JcaPEMKeyConverter kc = new JcaPEMKeyConverter().setProvider("BC");
return (RSAPrivateKey)kc.getPrivateKey((PrivateKeyInfo)pem.readObject());
}
protected void signRequest(Document request) throws IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, CertificateException, InvalidKeySpecException, MarshalException, XMLSignatureException
{
DOMConfiguration docConfig = request.getDomConfig();
docConfig.setParameter("infoset", Boolean.TRUE);
request.normalizeDocument();
String id = request.getDocumentElement().getAttributeNS("urn:oasis:names:tc:SAML:2.0:protocol", "ID");
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null), Collections. singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
null, null);
SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(getCertificate());
X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
DOMSignContext dsc = new DOMSignContext(getKey(), request.getDocumentElement());
dsc.setIdAttributeNS(request.getDocumentElement(), "urn:oasis:names:tc:SAML:2.0:protocol", "ID");
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
}
// Snippet of calling code:
signRequest(doc);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(doc);
StringWriter sw = new StringWriter();
StreamResult result = new StreamResult(sw);
t.transform(source, result);
byte[] encodedBytes = Base64.encodeBase64(sw.toString().getBytes());
Sample output: Signature verification fails both on the test IdP and using online tools.
<?xml version="1.0" encoding="UTF-8" standalone="no"?><samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" samlp:AssertionConsumerServiceURL="http://inferno.dunlop-lello.uk:8080/plugins/saml-authentication/sp/acs" samlp:Destination="https://shibboleth.dunlop-lello.uk/idp/profile/SAML2/POST/SSO" samlp:ID="3368168774849506" samlp:IssueInstant="2015-11-05T08:57:51" samlp:ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" samlp:Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://inferno.dunlop-lello.uk:8080/plugins/saml-authentication/</saml:Issuer><samlp:NameIDPolicy/><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>sai05O1Ug2BbxFg25WHST29rF0E=</DigestValue></Reference></SignedInfo><SignatureValue>nj6CX1wtYZf20rXKiY7qD5vtiOUc8S7zNYMkXa1ZyhcZs62V92O8rjOi0u/JZaLq7w2Fvwh/T5GQ
+Wvkz+XCJoZgNqv2DmJOZcwZkhZ5acoZM90tFWQPLptJj7IqS7T/egNTC7nXD0L61Ifn2DMhzliC qfT77SHpn5zieSKy20GoEaHYW9ucoDyON62Amghm5x1r7IKz8DoOqI19Au0ahOheIXjus7NqgLgJ eVgSOpoTpmyhZXa0c06+z1aYg+f0yMr91typYoFS3/IxMs4N7VRVVzX+O3/DbgqhVcM90N5bPgKd B/vQ61SMbYNRj5NRmedaaxCDkLDMQv+6LHahxQ==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIID3TCCAsWgAwIBAgIJAM3u0P2WViTzMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYDVQQGEwJHQjEU MBIGA1UECAwLV2VzdCBTdXNzZXgxFzAVBgNVBAcMDkhheXdhcmRzIEhlYXRoMSQwIgYDVQQKDBtE dW5sb3AtTGVsbG8gQ29uc3VsdGluZyBMVEQxIDAeBgNVBAMMF2luZmVybm8uZHVubG9wLWxlbGxv LnVrMB4XDTE1MTEwNDE3MjEyOFoXDTI1MTEwMTE3MjEyOFowgYQxCzAJBgNVBAYTAkdCMRQwEgYD VQQIDAtXZXN0IFN1c3NleDEXMBUGA1UEBwwOSGF5d2FyZHMgSGVhdGgxJDAiBgNVBAoMG0R1bmxv cC1MZWxsbyBDb25zdWx0aW5nIExURDEgMB4GA1UEAwwXaW5mZXJuby5kdW5sb3AtbGVsbG8udWsw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9nCCGd3Ikin15EkE2iMFUZx5C29CqZJjI RVxwPXBN+0SpPZBjiM30zAr4yeIg2YiMx9VfQsxprGUX2vpDzkR6yzgg+6LSACsoTB628pqqq4XH t5z1tKgpeRhef1Y+HA5AHT5ODI5YH4CyPxMXVdrjSfvvOe3mqCSHdkD2R0uPko5ZYzuxV/sQM3ZO wZmDvdLAj1W/16Z2v+l5CEbftI4wIbvRU6hwU7/ylA+gdAQdiIxlRf3mgzG6GW7sh9OK6A8pquBw YVZ7zLtAoV0QMb4r04nBF/N3wyMAdM8YSb3sio/2IxBkE4Osx2J2dH2V6sjBBnzkTZTQCLSuiO82 h6hFAgMBAAGjUDBOMB0GA1UdDgQWBBSJ7UW4/0xrutfCA4IBUp5esoyrADAfBgNVHSMEGDAWgBSJ 7UW4/0xrutfCA4IBUp5esoyrADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAXiu0E Z/A+YaqKrmm193rPJYCQrhl71P1OCBQysrYJIyygUZu3tXONaO/JvloNYLToFRO24kKdqf9WC7oh l2knIhPqoIc5/zsjiTsR9qQzNPHuhTA0GRquFvinR0rqdPBMuSfU+UjzOzXqkOxqr+X8L/e1IT5B 9Y66lGjJQcgzf71+C9m29aG+L4zzAYJURVwuljDYqVic6XmB5dUKdZn+7sNsuXkZp4u6QyyL7yEH rBbFF1tbPb7VXFroknbsUTIM5/Y36RzHWobS9LfYnwye8YyNP098UMWR5UalUCaityW6H6nUduge Du1z0L8uosGzsQr46KcBl038wi53ilzd</X509Certificate></X509Data></KeyInfo></Signature></samlp:AuthnRequest>
Any pointers as to where/why this is going wrong greatly appreciated.
It appears the above code was probably working correctly; my findings are posted here in the hopes they help the next person to run into an issue signing SAML.
I am now using OpenSAML rather than rolling my own solution, but hit the same brick wall, which turned out to be a misleading error message on the IdP.
Specifically, there was no metadata for the SP on the IdP, and the Shibboleth IdP uses metadata for signature validation, not the embedded certificate. The online tool I'd tried using for verification was failing as it didn't recognise the root CA, but again had misleading error messages over.