/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.xml.security.samples.signature.contract; import java.io.File; import java.io.FileOutputStream; import org.apache.xml.security.keys.content.KeyName; import org.apache.xml.security.signature.SignedInfo; import org.apache.xml.security.signature.XMLSignature; import org.apache.xml.security.transforms.Transforms; import org.apache.xml.security.transforms.params.XPathContainer; import org.apache.xml.security.utils.Constants; import org.apache.xml.security.utils.XMLUtils; import org.w3c.dom.Element; /** * In the past the protokol to sign data (like a contract) from more than one people * looks like this: * 1. A signes the hash of the data => SignatureA * 2. B signes SignatureA => SignatureB * 3. C signes SignatureB => SignatureC * * To verify e.g. signature C the following steps were necessary: * 1. Verify signature C thereby decrypt SignatureC (SignatureB) * 2. Verify signature B thereby decrypt SignatureB (SignatureA) * 3. Verify signature A thereby decrypt SignatureA (hash of the data) * 4. Compare the calculated hash of the sent contract with the decrypted SignatureA result * * XML-Signatures are more flexible in this way. * It is possible to sign data in steps from different signers and * verify a signature independent from the others signatures. * Furthermore all the signed data and the signatures can be hold in one file. * * @author Rene Kollmorgen */ public class ThreeSignerContractSign { /** * Method main * * @param unused * @throws Exception */ public static void main(String unused[]) throws Exception { //J- File signatureFile = new File("threeSignerContract.xml"); String BaseURI = signatureFile.toURL().toString(); //J+ javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(); org.w3c.dom.Document doc = db.newDocument(); Element contract = doc.createElementNS(null, "contract"); // create contract //////////////////////////////////////////// doc.appendChild(contract); // beautifying ////// Element condition1 = doc.createElementNS(null, "condition1"); condition1.setAttributeNS(null, "Id", "cond1"); condition1.appendChild( doc.createTextNode( "condition1 not covered in first signature, only binding for the second and third signer")); Element condition2 = doc.createElementNS(null, "condition2"); condition2.appendChild(doc.createTextNode("condition2")); Element condition3 = doc.createElementNS(null, "condition3"); condition3.appendChild(doc.createTextNode("condition3")); contract.appendChild(doc.createTextNode("\n")); contract.appendChild(condition1); contract.appendChild(doc.createTextNode("\n")); contract.appendChild(condition2); contract.appendChild(doc.createTextNode("\n")); contract.appendChild(condition3); contract.appendChild(doc.createTextNode("\n")); //J- String id1 = "firstSigner"; String id2 = "secondSigner"; String id3 = "thirdSigner"; // sign the whole contract and no signature and exclude condition1 String xp1Old = "not(ancestor-or-self::ds:Signature)" + " and not(ancestor-or-self::node()[@Id='cond1'])"; // sign the contract with condition2 and codition3 and no signature String xp1 = "not(ancestor-or-self::ds:Signature)" + "\n" + " and (" + "\n" + " (ancestor-or-self::node() = /contract/condition2) " + "\n" + " or (ancestor-or-self::node() = /contract/condition3) " + "\n" + " or (self::node() = /contract) " + "\n" + " or ((parent::node() = /contract) and (self::text()))" + "\n" + ")"; // sign the whole contract and no signature but the first String xp2 = "not(ancestor-or-self::ds:Signature)" + "\n" + " or ancestor-or-self::ds:Signature[@Id='" + id1 + "']"; // sign the whole contract and no signature but the first and the second String xp3 = "not(ancestor-or-self::ds:Signature)" + "\n" + " or ancestor-or-self::ds:Signature[@Id='" + id1 + "']" + "\n" + " or ancestor-or-self::ds:Signature[@Id='" + id2 + "']"; //J+ ////////////////////////////////////////////////////////////////// // first signer ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// { XMLSignature firstSigner = new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_MAC_HMAC_SHA1); firstSigner.setId(id1); contract.appendChild(firstSigner.getElement()); String rootnamespace = contract.getNamespaceURI(); boolean rootprefixed = (rootnamespace != null) && (rootnamespace.length() > 0); String rootlocalname = contract.getNodeName(); Transforms transforms = new Transforms(doc); XPathContainer xpath = new XPathContainer(doc); xpath.setXPathNamespaceContext("ds", Constants.SignatureSpecNS); xpath.setXPath("\n" + xp1 + "\n"); transforms.addTransform(Transforms.TRANSFORM_XPATH, xpath.getElementPlusReturns()); firstSigner.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1); { // not really secure /////////////////// firstSigner.getKeyInfo().add(new KeyName(doc, "First signer key")); //////////////////////////////////////////////// System.out.println("First signer: Start signing"); firstSigner .sign(firstSigner .createSecretKey("First signer key".getBytes())); System.out.println("First signer: Finished signing"); } SignedInfo s = firstSigner.getSignedInfo(); for (int i = 0; i < s.getSignedContentLength(); i++) { System.out.println("################ Signed Resource " + i + " ################"); System.out.println(new String(s.getSignedContentItem(i))); System.out.println(); } } ////////////////////////////////////////////////////////////////// // second signer ///////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// { XMLSignature secondSigner = new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_MAC_HMAC_SHA1); secondSigner.setId(id2); contract.appendChild(secondSigner.getElement()); Transforms transforms2 = new Transforms(doc); XPathContainer xpath2 = new XPathContainer(doc); xpath2.setXPathNamespaceContext("ds", Constants.SignatureSpecNS); xpath2.setXPath("\n" + xp2 + "\n"); transforms2.addTransform(Transforms.TRANSFORM_XPATH, xpath2.getElementPlusReturns()); secondSigner.addDocument("", transforms2, Constants.ALGO_ID_DIGEST_SHA1); { secondSigner.getKeyInfo().add(new KeyName(doc, "Second signer key")); System.out.println("Second signer: Start signing"); secondSigner .sign(secondSigner .createSecretKey("Second signer key".getBytes())); System.out.println("Second signer: Finished signing"); } SignedInfo s2 = secondSigner.getSignedInfo(); for (int i = 0; i < s2.getSignedContentLength(); i++) { System.out.println("################ Signed Resource " + i + " ################"); System.out.println(new String(s2.getSignedContentItem(i))); System.out.println(); } } ////////////////////////////////////////////////////////////////// // third signer ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// { XMLSignature thirdSigner = new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_MAC_HMAC_SHA1); thirdSigner.setId(id3); contract.appendChild(thirdSigner.getElement()); Transforms transforms3 = new Transforms(doc); XPathContainer xpath3 = new XPathContainer(doc); xpath3.setXPathNamespaceContext("ds", Constants.SignatureSpecNS); xpath3.setXPath("\n" + xp3 + "\n"); transforms3.addTransform(Transforms.TRANSFORM_XPATH, xpath3.getElementPlusReturns()); thirdSigner.addDocument("", transforms3, Constants.ALGO_ID_DIGEST_SHA1); { thirdSigner.getKeyInfo().add(new KeyName(doc, "Third signer key")); System.out.println("Third signer: Start signing"); thirdSigner .sign(thirdSigner .createSecretKey("Third signer key".getBytes())); System.out.println("Third signer: Finished signing"); } SignedInfo s3 = thirdSigner.getSignedInfo(); for (int i = 0; i < s3.getSignedContentLength(); i++) { System.out.println("################ Signed Resource " + i + " ################"); System.out.println(new String(s3.getSignedContentItem(i))); System.out.println(); } } ////////////////////////////////////////////////////////////////// // forth signer ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// { XMLSignature forthSigner = new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_MAC_HMAC_SHA1); forthSigner.setId("sig4"); contract.appendChild(forthSigner.getElement()); { // first of all, add the basic document without signatures Transforms transforms4 = new Transforms(doc); XPathContainer xpath4 = new XPathContainer(doc); xpath4.setXPathNamespaceContext("ds", Constants.SignatureSpecNS); xpath4.setXPath("\n" + "not(ancestor-or-self::ds:Signature)" + "\n"); transforms4.addTransform(Transforms.TRANSFORM_XPATH, xpath4.getElementPlusReturns()); forthSigner.addDocument("", transforms4, Constants.ALGO_ID_DIGEST_SHA1); } { // then add the different signatures /* Transforms transforms4 = new Transforms(doc); XPathContainer xpath4 = new XPathContainer(doc); xpath4.setXPathNamespaceContext("ds", Constants.SignatureSpecNS); xpath4.setXPath("\n" + "ancestor-or-self::ds:Signature[@Id='" + id1 + "']" + "\n"); transforms4.addTransform(Transforms.TRANSFORM_XPATH, xpath4.getElementPlusReturns()); forthSigner.addDocument("#xpointer(id('firstSigner'))", transforms4, Constants.ALGO_ID_DIGEST_SHA1, null, "ds:Signature"); */ forthSigner.addDocument("#xpointer(id('firstSigner'))", null, Constants.ALGO_ID_DIGEST_SHA1, null, "ds:Signature"); } { forthSigner.getKeyInfo().add(new KeyName(doc, "Forth signer key")); System.out.println("Forth signer: Start signing"); forthSigner .sign(forthSigner .createSecretKey("Forth signer key".getBytes())); System.out.println("Forth signer: Finished signing"); } SignedInfo s4 = forthSigner.getSignedInfo(); for (int i = 0; i < s4.getSignedContentLength(); i++) { System.out.println("################ Signed Resource " + i + " ################"); System.out.println(new String(s4.getSignedContentItem(i))); System.out.println(); } } ////////////////////////////////////////////////////////////////// // write away files ////////////////////////////////////////////////////////////////// { FileOutputStream f = new FileOutputStream(signatureFile); XMLUtils.outputDOMc14nWithComments(doc, f); f.close(); System.out.println("Wrote signature to " + BaseURI); } } static { org.apache.xml.security.Init.init(); // org.apache.xml.security.utils.Constants.setSignatureSpecNSprefix(""); } }