Reputation: 147
I'm newbie and I'm in a hurry. I'm just trying to digital sign a part of an XML.
The XML to sign is like this:
<?xml version="1.0" encoding="UTF-8"?><ns0:CEE_Adenda xmlns:ns0="http://adenda.es" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://adenda.es Test_v1.xsd">
<ns0:CEE version="1.0" xmlns:ns0="http://adenda.es">//XML to sing
............
</ns0:CEE>
<ns0:Adenda> //Part to exclude
......
</ns0:Adenda>
</ns0:CEE_Adenda>
, in order to get an structure like this:
<?xml version="1.0" encoding="UTF-8"?><ns0:CEE_Adenda xmlns:ns0="http://adenda.es" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://adenda.es Test_v1.xsd">
<ns0:CEE version="1.0" xmlns:ns0="http://adenda.es">//XML to sing
............
<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>vMg+tzKiwC8epApusLGo23at0ss=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>dVqqHp437r7jAeEOB6mxgSOKnpT6EITRscd0mzA/zDep3Wkg1CM/m0ojDHnlkC7l
</ns0:CEE>
<ns0:Adenda> //Part to exclude
......
</ns0:Adenda>
</ns0:CEE_Adenda>
This is the code I'm using to try to sign it, but I need to sign only the part of XML and put the result inside this tag.
This is the code I'm using:
//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.
try {
List<XPathType> xpaths = new ArrayList<XPathType>();
xpaths.add(new XPathType("//ns0:CFE", XPathType.Filter.INTERSECT));
Reference ref = fac.newReference("",
fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList(fac.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null)),null,null);
//ori
//Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, 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.
String p12Password = clave;
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(keyStoreName), p12Password.toCharArray());
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)ks.getEntry(alias,
new KeyStore.PasswordProtection(p12Password.toCharArray()));
X509Certificate cert = (X509Certificate)keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List<Serializable> x509Content = new ArrayList<Serializable>();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
javax.xml.crypto.dsig.keyinfo.KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
// Instantiate the document to be signed.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(xmlEntrada));
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element.
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement());
// Create the XMLSignature, but don't sign it yet.
XMLSignature signature = fac.newXMLSignature(si, ki);
// Marshal, generate, and sign the enveloped signature.
signature.sign(dsc);
// Output the resulting document.
OutputStream os = new FileOutputStream(xmlSalida);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
Any help will be very appreciated.
Regards
Upvotes: 4
Views: 9754
Reputation: 83
It's probably too late for you but if it can help anybody, here is the code I finally used to sign a SAML Assertion where the signature must be applied only on a part of my XML Document. My first tests was done with org.apache.xml.security but I did not find how to sign just a part of the my XML DOM. Finally I found an other way to sign XML using the Java XML Digital Signature API: http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
Please note that I'm not a Java Developer. So the code here-under can surely be improved.
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/*
* This Class contains method to sign SAML assertion.
*/
public class SAMLSigner {
// Sign an Assertion using the Private Key & Public Certificate from a KeyStore.
public static Document signAssertion(Document doc, SAMLKeyStore samlKeyStore) throws Exception {
// Instance main XML Signature Toolkit.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
XPathFactory xPathfactory = XPathFactory.newInstance();
// Retreive PrivateKey and Public Certificate from Specified KeyStore.
PrivateKey privateKey = samlKeyStore.getPrivateKey();
X509Certificate publicCertificate = samlKeyStore.getPublicCertificate();
// Retreive Assertion Node to be signed.
XPath xpath = xPathfactory.newXPath();
XPathExpression exprAssertion = xpath.compile("//*[local-name()='Response']//*[local-name()='Assertion']");
Element assertionNode = (Element) exprAssertion.evaluate(doc, XPathConstants.NODE);
// Must mark ID Atrribute as XML ID to avoid BUG in Java 1.7.25.
assertionNode.setIdAttribute("ID", true);
// Retreive Assertion ID because it is used in the URI attribute of the signature.
XPathExpression exprAssertionID = xpath.compile("//*[local-name()='Response']//*[local-name()='Assertion']//@ID");
String assertionID = (String) exprAssertionID.evaluate(doc, XPathConstants.STRING);
// Retreive Subject Node because the signature will be inserted before.
XPathExpression exprAssertionSubject = xpath.compile("//*[local-name()='Response']//*[local-name()='Assertion']//*[local-name()='Subject']");
Node insertionNode = (Node) exprAssertionSubject.evaluate(doc, XPathConstants.NODE);
// Create the DOMSignContext by specifying the signing informations: Private Key, Node to be signed, Where to insert the Signature.
DOMSignContext dsc = new DOMSignContext(privateKey, assertionNode, insertionNode);
dsc.setDefaultNamespacePrefix("ds");
// Create a CanonicalizationMethod which specify how the XML will be canonicalized before signed.
CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
// Create a SignatureMethod which specify how the XML will be signed.
SignatureMethod signatureMethod = fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
// Create an Array of Transform, add it one Transform which specify the Signature ENVELOPED method.
List<Transform> transformList = new ArrayList<Transform>(1);
transformList.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
// Create a Reference which contain: An URI to the Assertion ID, the Digest Method and the Transform List which specify the Signature ENVELOPED method.
Reference reference = fac.newReference("#" + assertionID, fac.newDigestMethod(DigestMethod.SHA1, null), transformList, null, null);
List<Reference> referenceList = Collections.singletonList(reference);
// Create a SignedInfo with the pre-specified: Canonicalization Method, Signature Method and List of References.
SignedInfo si = fac.newSignedInfo(canonicalizationMethod, signatureMethod, referenceList);
// Create a new KeyInfo and add it the Public Certificate.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(publicCertificate);
X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
// Create a new XML Signature with the pre-created : Signed Info & Key Info
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
// Return the Signed Assertion.
return doc;
}
}
My XML before signing.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://tmoobiee11gv9.kitrybe.dyndns.org:9704/saml2/sp/acs/post" ID="_0f3f5675-7a3a-4659-be6d-d3cdb0923fc2" IssueInstant="2016-10-19T12:43:40Z" Version="2.0">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">MyIssuer</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_9990f3f5675-7a3a-4659-be6d-d3cdb0923fc2" IssueInstant="2016-10-19T12:43:40Z" Version="2.0">
<saml:Issuer>MyIssuer</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">MyUser</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2016-10-19T12:53:40Z" Recipient="http://tmoobiee11gv9.kitrybe.dyndns.org:9704/saml2/sp/acs/post"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2016-10-19T12:33:40Z" NotOnOrAfter="2016-10-19T12:53:40Z">
<saml:AudienceRestriction>
<saml:Audience>MyAudience</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2016-10-19T12:43:40Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>
</samlp:Response>
Here is the XML after signing.
> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
> <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://tmoobiee11gv9.kitrybe.dyndns.org:9704/saml2/sp/acs/post"
> ID="_0f3f5675-7a3a-4659-be6d-d3cdb0923fc2"
> IssueInstant="2016-10-19T12:43:40Z" Version="2.0">
> <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">MyIssuer</saml:Issuer>
> <samlp:Status>
> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
> </samlp:Status>
> <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
> ID="_9990f3f5675-7a3a-4659-be6d-d3cdb0923fc2"
> IssueInstant="2016-10-19T12:43:40Z" Version="2.0">
> <saml:Issuer>MyIssuer</saml:Issuer>
> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
> <ds:SignedInfo>
> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
> <ds:Reference URI="#_9990f3f5675-7a3a-4659-be6d-d3cdb0923fc2">
> <ds:Transforms>
> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
> </ds:Transforms>
> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
> <ds:DigestValue>8uL7NzYK...</ds:DigestValue>
> </ds:Reference>
> </ds:SignedInfo>
> <ds:SignatureValue>BRsAX3XPvFcDw1...</ds:SignatureValue>
> <ds:KeyInfo>
> <ds:X509Data>
> <ds:X509Certificate>MIICvzCCAae...</ds:X509Certificate>
> </ds:X509Data>
> </ds:KeyInfo>
> </ds:Signature>
> <saml:Subject>
> <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">MyUser</saml:NameID>
> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
> <saml:SubjectConfirmationData NotOnOrAfter="2016-10-19T12:53:40Z"
> Recipient="http://tmoobiee11gv9.kitrybe.dyndns.org:9704/saml2/sp/acs/post"/>
> </saml:SubjectConfirmation>
> </saml:Subject>
> <saml:Conditions NotBefore="2016-10-19T12:33:40Z" NotOnOrAfter="2016-10-19T12:53:40Z">
> <saml:AudienceRestriction>
> <saml:Audience>MyAudience</saml:Audience>
> </saml:AudienceRestriction>
> </saml:Conditions>
> <saml:AuthnStatement AuthnInstant="2016-10-19T12:43:40Z">
> <saml:AuthnContext>
> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef>
> </saml:AuthnContext>
> </saml:AuthnStatement>
> </saml:Assertion>
> </samlp:Response>
The signature only applied on Assertion Tag and is placed before the Subject Tag.
Hope it will help.
Upvotes: 7