You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@santuario.apache.org by bu...@apache.org on 2008/10/07 14:17:10 UTC

DO NOT REPLY [Bug 45961] New: verify with own canonicalization method

https://issues.apache.org/bugzilla/show_bug.cgi?id=45961

           Summary: verify with own canonicalization method
           Product: Security
           Version: unspecified
          Platform: PC
        OS/Version: Windows XP
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Signature
        AssignedTo: security-dev@xml.apache.org
        ReportedBy: anton.k.ekb@gmail.com


I developed own canonicalization method and register it. Sign with my method
perform successful, but verify finished with error: Cannot find SignatureValue
in Signature. Constructor SignedInfo(Element, Strign) canonicalize
ds:SignedInfo by my method, reparse it into a new document and replace the
original not-canonicalized ds:SignedInfo. After replace, XMLSignature try get
ds:SignatureValue by method getNextSibling of element ds:SignedInfo. But it is
null!


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


Anton Kosyakov <an...@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|FIXED                       |




-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


sean.mullan@sun.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |NEEDINFO




--- Comment #1 from sean.mullan@sun.com  2008-10-07 06:55:04 PST ---
(In reply to comment #0)
> I developed own canonicalization method and register it. Sign with my method
> perform successful, but verify finished with error: Cannot find SignatureValue
> in Signature. Constructor SignedInfo(Element, Strign) canonicalize
> ds:SignedInfo by my method, reparse it into a new document and replace the
> original not-canonicalized ds:SignedInfo. After replace, XMLSignature try get
> ds:SignatureValue by method getNextSibling of element ds:SignedInfo. But it is
> null!
> 

You say you parsed it into a new document, so did you use Document.importNode
when replacing the SignedInfo in the original document?

In any case, we will need a reproducible test case to analyze this. Please 
attach it to the bug report, thanks.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

RE: problem enveloping a soap body

Posted by Richard Sand <Ri...@skyworthttg.com>.
Right you are! The sample code threw me off because of how it was dynamically constructing the document to be signed. Thanks for all of your help!

Best regards,

Richard A. Sand, CEO
Skyworth TTG USA, Inc.
+1 (866) 9-TRIPOD
http://www.skyworthttg.com/us

-----Original Message-----
From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
Sent: Thursday, October 16, 2008 4:33 PM
To: security-dev@xml.apache.org
Subject: Re: problem enveloping a soap body

I finally found some time to look at this and fixed your program. The problem is 
that you are also trying to insert the document inside a ds:Object element, 
which is for enveloping signatures, not enveloped signatures. If you remove the 
following lines from your program:

XMLStructure content = new DOMStructure(doc.getDocumentElement());
XMLObject xmlobj = 
xmlSigFactory.newXMLObject(Collections.singletonList(content), null, null, null);

and change:

XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, 
Collections.singletonList(xmlobj), reference, null);

to:

XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, null, reference, 
null);


you should be all set.

--Sean

Richard Sand wrote:
> Hi Sean,
> 
> I'm able to add the enveloped signature to a soap header if I use the org.apache.xml.security.signature package but not the javax.xml.crypto package. Am I still using the javax.xml.crypto package incorrectly? My test program attempts to sign a simple SOAP document with both techniques. "signDocument1" throws the DOMException but "signDocument2" succeeds. The output:
> 
> Raw: <?xml version="1.0" encoding="UTF-8"?>
> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>
> 
> org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
> 	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
> 	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument1(TestClass2.java:180)
> 	at TestClass2.main(TestClass2.java:99)
> 
> Result2: <?xml version="1.0" encoding="UTF-8"?>
> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body><soapenv:Header><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
> <ds:SignedInfo>
> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
> <ds:Reference URI="#body">
> <ds:Transforms>
> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
> <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
> </ds:Transforms>
> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
> <ds:DigestValue>DRGYMs2/nrFwPkXkx5yk03rEbu4=</ds:DigestValue>
> </ds:Reference>
> </ds:SignedInfo>
> <ds:SignatureValue>
> eodGz9w1M07SAvEPyYiSyUKut1oCYRMP8UTCuDRz7TCvI+ZRSVszdIDxNCalUsXDjPRkApfLn/RF
> TTw57AWNVw2hgkDtofN/rMfVSW9mWVVciVoDvW0FAx2/sP6gEznD5CcKiH8Zjxy3egmV/wtsgU4F
> DdAT1E2LR5x+q4dOCo0=
> </ds:SignatureValue>
> <ds:KeyInfo>
> <ds:KeyValue>
> <ds:RSAKeyValue>
> <ds:Modulus>
> rwYPeTtK0+3PtXSZbRmmU0VL4iKbp1c8jINxoFsBtSvhzfwEu+jDotZjsGvBP4QmgLaa6GkEmno1
> 9whsajllSbCPfPeAqpToJkygLzIPhx4wr9bm3XAOp89kApY17ImjdIIdA6xofTZwIQBEZ6deoNbh
> yk7dKj90LCMxObtDHZs=
> </ds:Modulus>
> <ds:Exponent>AQAB</ds:Exponent>
> </ds:RSAKeyValue>
> </ds:KeyValue>
> </ds:KeyInfo>
> </ds:Signature></soapenv:Header></soapenv:Envelope>
> 
> 
> import java.io.ByteArrayInputStream;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.io.StringWriter;
> import java.security.AlgorithmParameters;
> import java.security.InvalidAlgorithmParameterException;
> import java.security.InvalidKeyException;
> import java.security.Key;
> import java.security.KeyFactory;
> import java.security.NoSuchAlgorithmException;
> import java.security.PrivateKey;
> import java.security.Provider;
> import java.security.PublicKey;
> import java.security.Security;
> import java.security.cert.Certificate;
> import java.security.cert.CertificateException;
> import java.security.cert.CertificateFactory;
> import java.security.spec.InvalidKeySpecException;
> import java.security.spec.InvalidParameterSpecException;
> import java.security.spec.PKCS8EncodedKeySpec;
> import java.util.Collections;
> 
> import javax.crypto.BadPaddingException;
> import javax.crypto.Cipher;
> import javax.crypto.EncryptedPrivateKeyInfo;
> import javax.crypto.IllegalBlockSizeException;
> import javax.crypto.NoSuchPaddingException;
> import javax.crypto.SecretKeyFactory;
> import javax.crypto.spec.PBEKeySpec;
> import javax.xml.crypto.XMLStructure;
> import javax.xml.crypto.dom.DOMStructure;
> 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.XMLObject;
> 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.KeyValue;
> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
> import javax.xml.parsers.DocumentBuilderFactory;
> 
> import org.apache.xml.serialize.OutputFormat;
> import org.apache.xml.serialize.XMLSerializer;
> import org.bouncycastle.jce.provider.BouncyCastleProvider;
> import org.w3c.dom.Document;
> import org.w3c.dom.Element;
> import org.w3c.dom.NodeList;
> 
> public class TestClass2 {
> 	public static DocumentBuilderFactory factory = null;
> 
> 	// Add this header to the SOAP message if it does not exist
> 	public static String soap_header = "http://schemas.xmlsoap.org/soap/envelope/";
> 
> 	// Provider name
> 	static String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
> 
> 	/**
> 	 * @param args
> 	 */
> 	public static void main(String[] args) {
> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
> 		// String xmlStr =
> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
> 		String x509certFile = "c:\\gkconfig.der";
> 		String privateKeyFile = "c:\\gkconfig_key.pk8";
> 		String password = "password";
> 
> 		try {
> 			// Load the public and private keys
> 			Security.addProvider(new BouncyCastleProvider());
> 			Certificate certs[] = loadX509CertificateChain(x509certFile);
> 			if (certs.length < 1)
> 				throw new Exception("No certificate found in " + x509certFile);
> 			PublicKey publicKey = certs[0].getPublicKey();
> 			PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
> 
> 			// Create a builder factory
> 			factory = DocumentBuilderFactory.newInstance();
> 			factory.setNamespaceAware(true);
> 			factory.setValidating(false);
> 
> 			// Create the builder and parse the input
> 			Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
> 			String raw = xmlToString(doc);
> 			System.out.println("Raw: " + raw + "\n");
> 
> 			// Sign and output - attempt 1
> 			try {
> 				Document doc1 = signDocument1(doc, "#body", publicKey, privateKey, certs[0]);
> 				String signed1 = xmlToString(doc1);
> 				System.out.println("Result1: " + signed1);
> 			} catch (Exception e) {
> 				e.printStackTrace();
> 			}
> 
> 			// Sign and output - attempt 2
> 			doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
> 			Document doc2 = signDocument2(doc, "#body", publicKey, privateKey, certs[0]);
> 			String signed2 = xmlToString(doc2);
> 			System.out.println("\nResult2: " + signed2);
> 
> 		} catch (Exception e) {
> 			e.printStackTrace();
> 		}
> 	}
> 
> 	/**
> 	 * Attempt 1... using javax.xml.crypto package
> 	 * 
> 	 * @param doc
> 	 * @param reference
> 	 * @param publicKey
> 	 * @param privateKey
> 	 * @param cert
> 	 * @return
> 	 * @throws Exception
> 	 */
> 	public static Document signDocument1(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
> 		// System.out.println("Provider name: " + providerName);
> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
> 
> 		// Create a Reference to a same-document URI that is an Object element
> 		// and specify the SHA1 digest algorithm and the ENVELOPED Transform.
> 		Reference ref = xmlSigFactory.newReference(reference, xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
> 
> 		// Create the SignedInfo
> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
> 
> 		// Create a KeyInfo from the public key
> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
> 		KeyValue kv = kif.newKeyValue(publicKey);
> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
> 
> 		// Create the XML object from the document
> 		XMLStructure content = new DOMStructure(doc.getDocumentElement());
> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), null, null, null);
> 
> 		// Create the XMLSignature (but don't sign it yet)
> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), reference, null);
> 
> 		// Look for the SOAP header
> 		Element headerElement = null;
> 		Element envelopeElement = null;
> 		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
> 		if (nodes.getLength() == 0) {
> 			// No nodes found - add header here
> 			//System.out.println("Searching for the SOAP Envelope Element");
> 			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
> 			if (nodes != null) {
> 				//System.out.println("Adding a SOAP Header Element");
> 				headerElement = doc.createElementNS(soap_header, "Header");
> 				envelopeElement = (Element) nodes.item(0);
> 				headerElement.setPrefix(envelopeElement.getPrefix());
> 				envelopeElement.appendChild(headerElement);
> 			}
> 		} else {
> 			// This shouldn't happen unless explicitly done elsewhere
> 			// System.out.println("Found " + nodes.getLength() +
> 			// " SOAP Header elements.");
> 			headerElement = (Element) nodes.item(0);
> 		}
> 
> 		// Create a DOMSignContext, specifying the PrivateKey and the document
> 		// location of the XMLSignature
> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, headerElement);
> 
> 		// Lastly, generate the enveloping signature using the PrivateKey
> 		signature.sign(domSignContext);
> 		return doc;
> 	}
> 
> 	/**
> 	 * Attempt 2... using org.apache.xml.security.signature package
> 	 * 
> 	 * @param doc
> 	 * @param reference
> 	 * @param publicKey
> 	 * @param privateKey
> 	 * @param cert
> 	 * @return
> 	 * @throws Exception
> 	 */
> 	public static Document signDocument2(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
> 		// Initialize the library
> 		org.apache.xml.security.Init.init();
> 		javax.xml.parsers.DocumentBuilderFactory docFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
> 		docFactory.setNamespaceAware(true);
> 		String baseURI = null;
> 		org.apache.xml.security.signature.XMLSignature xmlSig = new org.apache.xml.security.signature.XMLSignature(doc, baseURI,
> 				org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
> 
> 		// Look for the SOAP header
> 		Element headerElement = null;
> 		Element envelopeElement = null;
> 		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
> 		if (nodes.getLength() == 0) {
> 			// No nodes found - add header here
> 			//System.out.println("Searching for the SOAP Envelope Element");
> 			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
> 			if (nodes != null) {
> 				//System.out.println("Adding a SOAP Header Element");
> 				headerElement = doc.createElementNS(soap_header, "Header");
> 				envelopeElement = (Element) nodes.item(0);
> 				headerElement.setPrefix(envelopeElement.getPrefix());
> 				envelopeElement.appendChild(headerElement);
> 			}
> 		} else {
> 			// This shouldn't happen unless explicitly done elsewhere
> 			// System.out.println("Found " + nodes.getLength() +
> 			// " SOAP Header elements.");
> 			headerElement = (Element) nodes.item(0);
> 		}
> 		org.w3c.dom.Element sigElement = xmlSig.getElement();
> 		headerElement.appendChild(sigElement);
> 
> 		// We also need to define the rules for XML document transformation and
> 		// canonicalization, necessary for parsing and signature verification by
> 		// others
> 		org.apache.xml.security.transforms.Transforms transforms = new org.apache.xml.security.transforms.Transforms(doc);
> 		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
> 		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_WITH_COMMENTS);
> 		xmlSig.addDocument(reference, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);
> 
> 		// Optionally, we can include the public key certificate information
> 		// associated with the private key we used to create this digital
> 		// signature.
> 		xmlSig.addKeyInfo(publicKey);
> 
> 		// All we have to do now is sign the document and write it to a file,
> 		// using a convenience class found in the Apache XML Security package:
> 
> 		xmlSig.sign(privateKey);
> 		return doc;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded X509 certificate chain
> 	 * 
> 	 * @param certificateChainFileName
> 	 * @return
> 	 * @throws IOException
> 	 * @throws CertificateException
> 	 */
> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
> 		java.security.cert.Certificate[] chain = {};
> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
> 		certificateStream.close();
> 
> 		return chain;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded, encrypted PKCS8 private key
> 	 */
> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
> 			NoSuchAlgorithmException {
> 		File keyFile = new File(privateKeyFile);
> 		byte[] encodedKey = new byte[(int) keyFile.length()];
> 		FileInputStream is = new FileInputStream(keyFile);
> 		is.read(encodedKey);
> 		is.close();
> 
> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
> 		return privateKey;
> 	}
> 
> 	/**
> 	 * Decrypts an encrypted RSA private key
> 	 * 
> 	 * @param instream
> 	 * @param password
> 	 * @return
> 	 * @throws InvalidKeyException
> 	 * @throws InvalidAlgorithmParameterException
> 	 * @throws IllegalStateException
> 	 * @throws IllegalBlockSizeException
> 	 * @throws BadPaddingException
> 	 * @throws NoSuchAlgorithmException
> 	 * @throws NoSuchPaddingException
> 	 * @throws InvalidKeySpecException
> 	 * @throws InvalidParameterSpecException
> 	 * @throws IOException
> 	 *             if the key is unencrypted
> 	 */
> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException,
> 			IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
> 			InvalidParameterSpecException, IOException {
> 
> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
> 		// System.out.println("Encrypted private key info's algorithm name is '"
> 		// + epki.getAlgName() + "'");
> 
> 		AlgorithmParameters params = epki.getAlgParameters();
> 		if (params == null)
> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
> 		// PBEParameterSpec pbeParams = (PBEParameterSpec)
> 		// (params.getParameterSpec(PBEParameterSpec.class));
> 
> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
> 		PBEKeySpec keySpec = new PBEKeySpec(password);
> 		Key key = sf.generateSecret(keySpec);
> 		keySpec.clearPassword();
> 
> 		byte[] privateKeyInfoStream = null;
> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
> 
> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
> 		return privateKeyInfoStream;
> 	}
> 
> 	public static String xmlToString(Document doc) throws IOException {
> 		StringWriter sw = new StringWriter();
> 		XMLSerializer ser = new XMLSerializer(sw, new OutputFormat(doc));
> 		ser.serialize(doc.getDocumentElement());
> 
> 		String XMLStr = sw.toString();
> 		return XMLStr;
> 	}
> }
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 
> -----Original Message-----
> From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
> Sent: Friday, October 10, 2008 9:40 AM
> To: security-dev@xml.apache.org
> Subject: Re: problem enveloping a soap body
> 
> Richard Sand wrote:
>> Hi Sean,
>>
>> I guess I'm confused. I thought the whole point of the enveloping technique
>> was that the signature would become part of the original document? 
> 
> But when you are enveloping an existing Document into the Object element, you 
> get yourself into a problem in that you are asking it to insert the Signature at 
> the top of the Document, but also insert the same Document inside the XML 
> Signature Object element. Thus the DOMException is thrown because it is not 
> possible to do that, since it violates the DOM hierarchy. Unless you have some 
> tight memory constraints, I don't see that creating a new Document to hold the 
> signature is a problem ... I assume it will be quickly serialized and sent over 
> the network anyway.
> 
> --Sean
> 
> 


Re: problem enveloping a soap body

Posted by Sean Mullan <Se...@Sun.COM>.
I finally found some time to look at this and fixed your program. The problem is 
that you are also trying to insert the document inside a ds:Object element, 
which is for enveloping signatures, not enveloped signatures. If you remove the 
following lines from your program:

XMLStructure content = new DOMStructure(doc.getDocumentElement());
XMLObject xmlobj = 
xmlSigFactory.newXMLObject(Collections.singletonList(content), null, null, null);

and change:

XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, 
Collections.singletonList(xmlobj), reference, null);

to:

XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, null, reference, 
null);


you should be all set.

--Sean

Richard Sand wrote:
> Hi Sean,
> 
> I'm able to add the enveloped signature to a soap header if I use the org.apache.xml.security.signature package but not the javax.xml.crypto package. Am I still using the javax.xml.crypto package incorrectly? My test program attempts to sign a simple SOAP document with both techniques. "signDocument1" throws the DOMException but "signDocument2" succeeds. The output:
> 
> Raw: <?xml version="1.0" encoding="UTF-8"?>
> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>
> 
> org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
> 	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
> 	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument1(TestClass2.java:180)
> 	at TestClass2.main(TestClass2.java:99)
> 
> Result2: <?xml version="1.0" encoding="UTF-8"?>
> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body><soapenv:Header><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
> <ds:SignedInfo>
> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
> <ds:Reference URI="#body">
> <ds:Transforms>
> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
> <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
> </ds:Transforms>
> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
> <ds:DigestValue>DRGYMs2/nrFwPkXkx5yk03rEbu4=</ds:DigestValue>
> </ds:Reference>
> </ds:SignedInfo>
> <ds:SignatureValue>
> eodGz9w1M07SAvEPyYiSyUKut1oCYRMP8UTCuDRz7TCvI+ZRSVszdIDxNCalUsXDjPRkApfLn/RF
> TTw57AWNVw2hgkDtofN/rMfVSW9mWVVciVoDvW0FAx2/sP6gEznD5CcKiH8Zjxy3egmV/wtsgU4F
> DdAT1E2LR5x+q4dOCo0=
> </ds:SignatureValue>
> <ds:KeyInfo>
> <ds:KeyValue>
> <ds:RSAKeyValue>
> <ds:Modulus>
> rwYPeTtK0+3PtXSZbRmmU0VL4iKbp1c8jINxoFsBtSvhzfwEu+jDotZjsGvBP4QmgLaa6GkEmno1
> 9whsajllSbCPfPeAqpToJkygLzIPhx4wr9bm3XAOp89kApY17ImjdIIdA6xofTZwIQBEZ6deoNbh
> yk7dKj90LCMxObtDHZs=
> </ds:Modulus>
> <ds:Exponent>AQAB</ds:Exponent>
> </ds:RSAKeyValue>
> </ds:KeyValue>
> </ds:KeyInfo>
> </ds:Signature></soapenv:Header></soapenv:Envelope>
> 
> 
> import java.io.ByteArrayInputStream;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.io.StringWriter;
> import java.security.AlgorithmParameters;
> import java.security.InvalidAlgorithmParameterException;
> import java.security.InvalidKeyException;
> import java.security.Key;
> import java.security.KeyFactory;
> import java.security.NoSuchAlgorithmException;
> import java.security.PrivateKey;
> import java.security.Provider;
> import java.security.PublicKey;
> import java.security.Security;
> import java.security.cert.Certificate;
> import java.security.cert.CertificateException;
> import java.security.cert.CertificateFactory;
> import java.security.spec.InvalidKeySpecException;
> import java.security.spec.InvalidParameterSpecException;
> import java.security.spec.PKCS8EncodedKeySpec;
> import java.util.Collections;
> 
> import javax.crypto.BadPaddingException;
> import javax.crypto.Cipher;
> import javax.crypto.EncryptedPrivateKeyInfo;
> import javax.crypto.IllegalBlockSizeException;
> import javax.crypto.NoSuchPaddingException;
> import javax.crypto.SecretKeyFactory;
> import javax.crypto.spec.PBEKeySpec;
> import javax.xml.crypto.XMLStructure;
> import javax.xml.crypto.dom.DOMStructure;
> 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.XMLObject;
> 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.KeyValue;
> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
> import javax.xml.parsers.DocumentBuilderFactory;
> 
> import org.apache.xml.serialize.OutputFormat;
> import org.apache.xml.serialize.XMLSerializer;
> import org.bouncycastle.jce.provider.BouncyCastleProvider;
> import org.w3c.dom.Document;
> import org.w3c.dom.Element;
> import org.w3c.dom.NodeList;
> 
> public class TestClass2 {
> 	public static DocumentBuilderFactory factory = null;
> 
> 	// Add this header to the SOAP message if it does not exist
> 	public static String soap_header = "http://schemas.xmlsoap.org/soap/envelope/";
> 
> 	// Provider name
> 	static String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
> 
> 	/**
> 	 * @param args
> 	 */
> 	public static void main(String[] args) {
> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
> 		// String xmlStr =
> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
> 		String x509certFile = "c:\\gkconfig.der";
> 		String privateKeyFile = "c:\\gkconfig_key.pk8";
> 		String password = "password";
> 
> 		try {
> 			// Load the public and private keys
> 			Security.addProvider(new BouncyCastleProvider());
> 			Certificate certs[] = loadX509CertificateChain(x509certFile);
> 			if (certs.length < 1)
> 				throw new Exception("No certificate found in " + x509certFile);
> 			PublicKey publicKey = certs[0].getPublicKey();
> 			PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
> 
> 			// Create a builder factory
> 			factory = DocumentBuilderFactory.newInstance();
> 			factory.setNamespaceAware(true);
> 			factory.setValidating(false);
> 
> 			// Create the builder and parse the input
> 			Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
> 			String raw = xmlToString(doc);
> 			System.out.println("Raw: " + raw + "\n");
> 
> 			// Sign and output - attempt 1
> 			try {
> 				Document doc1 = signDocument1(doc, "#body", publicKey, privateKey, certs[0]);
> 				String signed1 = xmlToString(doc1);
> 				System.out.println("Result1: " + signed1);
> 			} catch (Exception e) {
> 				e.printStackTrace();
> 			}
> 
> 			// Sign and output - attempt 2
> 			doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
> 			Document doc2 = signDocument2(doc, "#body", publicKey, privateKey, certs[0]);
> 			String signed2 = xmlToString(doc2);
> 			System.out.println("\nResult2: " + signed2);
> 
> 		} catch (Exception e) {
> 			e.printStackTrace();
> 		}
> 	}
> 
> 	/**
> 	 * Attempt 1... using javax.xml.crypto package
> 	 * 
> 	 * @param doc
> 	 * @param reference
> 	 * @param publicKey
> 	 * @param privateKey
> 	 * @param cert
> 	 * @return
> 	 * @throws Exception
> 	 */
> 	public static Document signDocument1(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
> 		// System.out.println("Provider name: " + providerName);
> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
> 
> 		// Create a Reference to a same-document URI that is an Object element
> 		// and specify the SHA1 digest algorithm and the ENVELOPED Transform.
> 		Reference ref = xmlSigFactory.newReference(reference, xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
> 
> 		// Create the SignedInfo
> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
> 
> 		// Create a KeyInfo from the public key
> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
> 		KeyValue kv = kif.newKeyValue(publicKey);
> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
> 
> 		// Create the XML object from the document
> 		XMLStructure content = new DOMStructure(doc.getDocumentElement());
> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), null, null, null);
> 
> 		// Create the XMLSignature (but don't sign it yet)
> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), reference, null);
> 
> 		// Look for the SOAP header
> 		Element headerElement = null;
> 		Element envelopeElement = null;
> 		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
> 		if (nodes.getLength() == 0) {
> 			// No nodes found - add header here
> 			//System.out.println("Searching for the SOAP Envelope Element");
> 			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
> 			if (nodes != null) {
> 				//System.out.println("Adding a SOAP Header Element");
> 				headerElement = doc.createElementNS(soap_header, "Header");
> 				envelopeElement = (Element) nodes.item(0);
> 				headerElement.setPrefix(envelopeElement.getPrefix());
> 				envelopeElement.appendChild(headerElement);
> 			}
> 		} else {
> 			// This shouldn't happen unless explicitly done elsewhere
> 			// System.out.println("Found " + nodes.getLength() +
> 			// " SOAP Header elements.");
> 			headerElement = (Element) nodes.item(0);
> 		}
> 
> 		// Create a DOMSignContext, specifying the PrivateKey and the document
> 		// location of the XMLSignature
> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, headerElement);
> 
> 		// Lastly, generate the enveloping signature using the PrivateKey
> 		signature.sign(domSignContext);
> 		return doc;
> 	}
> 
> 	/**
> 	 * Attempt 2... using org.apache.xml.security.signature package
> 	 * 
> 	 * @param doc
> 	 * @param reference
> 	 * @param publicKey
> 	 * @param privateKey
> 	 * @param cert
> 	 * @return
> 	 * @throws Exception
> 	 */
> 	public static Document signDocument2(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
> 		// Initialize the library
> 		org.apache.xml.security.Init.init();
> 		javax.xml.parsers.DocumentBuilderFactory docFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
> 		docFactory.setNamespaceAware(true);
> 		String baseURI = null;
> 		org.apache.xml.security.signature.XMLSignature xmlSig = new org.apache.xml.security.signature.XMLSignature(doc, baseURI,
> 				org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
> 
> 		// Look for the SOAP header
> 		Element headerElement = null;
> 		Element envelopeElement = null;
> 		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
> 		if (nodes.getLength() == 0) {
> 			// No nodes found - add header here
> 			//System.out.println("Searching for the SOAP Envelope Element");
> 			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
> 			if (nodes != null) {
> 				//System.out.println("Adding a SOAP Header Element");
> 				headerElement = doc.createElementNS(soap_header, "Header");
> 				envelopeElement = (Element) nodes.item(0);
> 				headerElement.setPrefix(envelopeElement.getPrefix());
> 				envelopeElement.appendChild(headerElement);
> 			}
> 		} else {
> 			// This shouldn't happen unless explicitly done elsewhere
> 			// System.out.println("Found " + nodes.getLength() +
> 			// " SOAP Header elements.");
> 			headerElement = (Element) nodes.item(0);
> 		}
> 		org.w3c.dom.Element sigElement = xmlSig.getElement();
> 		headerElement.appendChild(sigElement);
> 
> 		// We also need to define the rules for XML document transformation and
> 		// canonicalization, necessary for parsing and signature verification by
> 		// others
> 		org.apache.xml.security.transforms.Transforms transforms = new org.apache.xml.security.transforms.Transforms(doc);
> 		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
> 		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_WITH_COMMENTS);
> 		xmlSig.addDocument(reference, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);
> 
> 		// Optionally, we can include the public key certificate information
> 		// associated with the private key we used to create this digital
> 		// signature.
> 		xmlSig.addKeyInfo(publicKey);
> 
> 		// All we have to do now is sign the document and write it to a file,
> 		// using a convenience class found in the Apache XML Security package:
> 
> 		xmlSig.sign(privateKey);
> 		return doc;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded X509 certificate chain
> 	 * 
> 	 * @param certificateChainFileName
> 	 * @return
> 	 * @throws IOException
> 	 * @throws CertificateException
> 	 */
> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
> 		java.security.cert.Certificate[] chain = {};
> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
> 		certificateStream.close();
> 
> 		return chain;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded, encrypted PKCS8 private key
> 	 */
> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
> 			NoSuchAlgorithmException {
> 		File keyFile = new File(privateKeyFile);
> 		byte[] encodedKey = new byte[(int) keyFile.length()];
> 		FileInputStream is = new FileInputStream(keyFile);
> 		is.read(encodedKey);
> 		is.close();
> 
> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
> 		return privateKey;
> 	}
> 
> 	/**
> 	 * Decrypts an encrypted RSA private key
> 	 * 
> 	 * @param instream
> 	 * @param password
> 	 * @return
> 	 * @throws InvalidKeyException
> 	 * @throws InvalidAlgorithmParameterException
> 	 * @throws IllegalStateException
> 	 * @throws IllegalBlockSizeException
> 	 * @throws BadPaddingException
> 	 * @throws NoSuchAlgorithmException
> 	 * @throws NoSuchPaddingException
> 	 * @throws InvalidKeySpecException
> 	 * @throws InvalidParameterSpecException
> 	 * @throws IOException
> 	 *             if the key is unencrypted
> 	 */
> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException,
> 			IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
> 			InvalidParameterSpecException, IOException {
> 
> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
> 		// System.out.println("Encrypted private key info's algorithm name is '"
> 		// + epki.getAlgName() + "'");
> 
> 		AlgorithmParameters params = epki.getAlgParameters();
> 		if (params == null)
> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
> 		// PBEParameterSpec pbeParams = (PBEParameterSpec)
> 		// (params.getParameterSpec(PBEParameterSpec.class));
> 
> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
> 		PBEKeySpec keySpec = new PBEKeySpec(password);
> 		Key key = sf.generateSecret(keySpec);
> 		keySpec.clearPassword();
> 
> 		byte[] privateKeyInfoStream = null;
> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
> 
> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
> 		return privateKeyInfoStream;
> 	}
> 
> 	public static String xmlToString(Document doc) throws IOException {
> 		StringWriter sw = new StringWriter();
> 		XMLSerializer ser = new XMLSerializer(sw, new OutputFormat(doc));
> 		ser.serialize(doc.getDocumentElement());
> 
> 		String XMLStr = sw.toString();
> 		return XMLStr;
> 	}
> }
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 
> -----Original Message-----
> From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
> Sent: Friday, October 10, 2008 9:40 AM
> To: security-dev@xml.apache.org
> Subject: Re: problem enveloping a soap body
> 
> Richard Sand wrote:
>> Hi Sean,
>>
>> I guess I'm confused. I thought the whole point of the enveloping technique
>> was that the signature would become part of the original document? 
> 
> But when you are enveloping an existing Document into the Object element, you 
> get yourself into a problem in that you are asking it to insert the Signature at 
> the top of the Document, but also insert the same Document inside the XML 
> Signature Object element. Thus the DOMException is thrown because it is not 
> possible to do that, since it violates the DOM hierarchy. Unless you have some 
> tight memory constraints, I don't see that creating a new Document to hold the 
> signature is a problem ... I assume it will be quickly serialized and sent over 
> the network anyway.
> 
> --Sean
> 
> 


RE: problem enveloping a soap body

Posted by Richard Sand <Ri...@skyworthttg.com>.
Hi Sean,

I'm able to add the enveloped signature to a soap header if I use the org.apache.xml.security.signature package but not the javax.xml.crypto package. Am I still using the javax.xml.crypto package incorrectly? My test program attempts to sign a simple SOAP document with both techniques. "signDocument1" throws the DOMException but "signDocument2" succeeds. The output:

Raw: <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>

org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
	at TestClass2.signDocument1(TestClass2.java:180)
	at TestClass2.main(TestClass2.java:99)

Result2: <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body Id="body"><ns2:getGoKartAll xmlns:ns2="http://soa.examples.ttg.com"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body><soapenv:Header><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#body">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>DRGYMs2/nrFwPkXkx5yk03rEbu4=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
eodGz9w1M07SAvEPyYiSyUKut1oCYRMP8UTCuDRz7TCvI+ZRSVszdIDxNCalUsXDjPRkApfLn/RF
TTw57AWNVw2hgkDtofN/rMfVSW9mWVVciVoDvW0FAx2/sP6gEznD5CcKiH8Zjxy3egmV/wtsgU4F
DdAT1E2LR5x+q4dOCo0=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>
rwYPeTtK0+3PtXSZbRmmU0VL4iKbp1c8jINxoFsBtSvhzfwEu+jDotZjsGvBP4QmgLaa6GkEmno1
9whsajllSbCPfPeAqpToJkygLzIPhx4wr9bm3XAOp89kApY17ImjdIIdA6xofTZwIQBEZ6deoNbh
yk7dKj90LCMxObtDHZs=
</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature></soapenv:Header></soapenv:Envelope>


import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collections;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
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.XMLObject;
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.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class TestClass2 {
	public static DocumentBuilderFactory factory = null;

	// Add this header to the SOAP message if it does not exist
	public static String soap_header = "http://schemas.xmlsoap.org/soap/envelope/";

	// Provider name
	static String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
		// String xmlStr =
		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
		String x509certFile = "c:\\gkconfig.der";
		String privateKeyFile = "c:\\gkconfig_key.pk8";
		String password = "password";

		try {
			// Load the public and private keys
			Security.addProvider(new BouncyCastleProvider());
			Certificate certs[] = loadX509CertificateChain(x509certFile);
			if (certs.length < 1)
				throw new Exception("No certificate found in " + x509certFile);
			PublicKey publicKey = certs[0].getPublicKey();
			PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);

			// Create a builder factory
			factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			factory.setValidating(false);

			// Create the builder and parse the input
			Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
			String raw = xmlToString(doc);
			System.out.println("Raw: " + raw + "\n");

			// Sign and output - attempt 1
			try {
				Document doc1 = signDocument1(doc, "#body", publicKey, privateKey, certs[0]);
				String signed1 = xmlToString(doc1);
				System.out.println("Result1: " + signed1);
			} catch (Exception e) {
				e.printStackTrace();
			}

			// Sign and output - attempt 2
			doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xmlStr.getBytes()));
			Document doc2 = signDocument2(doc, "#body", publicKey, privateKey, certs[0]);
			String signed2 = xmlToString(doc2);
			System.out.println("\nResult2: " + signed2);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Attempt 1... using javax.xml.crypto package
	 * 
	 * @param doc
	 * @param reference
	 * @param publicKey
	 * @param privateKey
	 * @param cert
	 * @return
	 * @throws Exception
	 */
	public static Document signDocument1(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
		// System.out.println("Provider name: " + providerName);
		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());

		// Create a Reference to a same-document URI that is an Object element
		// and specify the SHA1 digest algorithm and the ENVELOPED Transform.
		Reference ref = xmlSigFactory.newReference(reference, xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);

		// Create the SignedInfo
		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));

		// Create a KeyInfo from the public key
		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
		KeyValue kv = kif.newKeyValue(publicKey);
		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

		// Create the XML object from the document
		XMLStructure content = new DOMStructure(doc.getDocumentElement());
		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), null, null, null);

		// Create the XMLSignature (but don't sign it yet)
		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), reference, null);

		// Look for the SOAP header
		Element headerElement = null;
		Element envelopeElement = null;
		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
		if (nodes.getLength() == 0) {
			// No nodes found - add header here
			//System.out.println("Searching for the SOAP Envelope Element");
			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
			if (nodes != null) {
				//System.out.println("Adding a SOAP Header Element");
				headerElement = doc.createElementNS(soap_header, "Header");
				envelopeElement = (Element) nodes.item(0);
				headerElement.setPrefix(envelopeElement.getPrefix());
				envelopeElement.appendChild(headerElement);
			}
		} else {
			// This shouldn't happen unless explicitly done elsewhere
			// System.out.println("Found " + nodes.getLength() +
			// " SOAP Header elements.");
			headerElement = (Element) nodes.item(0);
		}

		// Create a DOMSignContext, specifying the PrivateKey and the document
		// location of the XMLSignature
		DOMSignContext domSignContext = new DOMSignContext(privateKey, headerElement);

		// Lastly, generate the enveloping signature using the PrivateKey
		signature.sign(domSignContext);
		return doc;
	}

	/**
	 * Attempt 2... using org.apache.xml.security.signature package
	 * 
	 * @param doc
	 * @param reference
	 * @param publicKey
	 * @param privateKey
	 * @param cert
	 * @return
	 * @throws Exception
	 */
	public static Document signDocument2(Document doc, String reference, PublicKey publicKey, PrivateKey privateKey, Certificate cert) throws Exception {
		// Initialize the library
		org.apache.xml.security.Init.init();
		javax.xml.parsers.DocumentBuilderFactory docFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
		docFactory.setNamespaceAware(true);
		String baseURI = null;
		org.apache.xml.security.signature.XMLSignature xmlSig = new org.apache.xml.security.signature.XMLSignature(doc, baseURI,
				org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);

		// Look for the SOAP header
		Element headerElement = null;
		Element envelopeElement = null;
		NodeList nodes = doc.getElementsByTagNameNS(soap_header, "Header");
		if (nodes.getLength() == 0) {
			// No nodes found - add header here
			//System.out.println("Searching for the SOAP Envelope Element");
			nodes = doc.getElementsByTagNameNS(soap_header, "Envelope");
			if (nodes != null) {
				//System.out.println("Adding a SOAP Header Element");
				headerElement = doc.createElementNS(soap_header, "Header");
				envelopeElement = (Element) nodes.item(0);
				headerElement.setPrefix(envelopeElement.getPrefix());
				envelopeElement.appendChild(headerElement);
			}
		} else {
			// This shouldn't happen unless explicitly done elsewhere
			// System.out.println("Found " + nodes.getLength() +
			// " SOAP Header elements.");
			headerElement = (Element) nodes.item(0);
		}
		org.w3c.dom.Element sigElement = xmlSig.getElement();
		headerElement.appendChild(sigElement);

		// We also need to define the rules for XML document transformation and
		// canonicalization, necessary for parsing and signature verification by
		// others
		org.apache.xml.security.transforms.Transforms transforms = new org.apache.xml.security.transforms.Transforms(doc);
		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
		transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_WITH_COMMENTS);
		xmlSig.addDocument(reference, transforms, org.apache.xml.security.utils.Constants.ALGO_ID_DIGEST_SHA1);

		// Optionally, we can include the public key certificate information
		// associated with the private key we used to create this digital
		// signature.
		xmlSig.addKeyInfo(publicKey);

		// All we have to do now is sign the document and write it to a file,
		// using a convenience class found in the Apache XML Security package:

		xmlSig.sign(privateKey);
		return doc;
	}

	/**
	 * Loads the DER-encoded X509 certificate chain
	 * 
	 * @param certificateChainFileName
	 * @return
	 * @throws IOException
	 * @throws CertificateException
	 */
	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
		java.security.cert.Certificate[] chain = {};
		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
		certificateStream.close();

		return chain;
	}

	/**
	 * Loads the DER-encoded, encrypted PKCS8 private key
	 */
	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
			NoSuchAlgorithmException {
		File keyFile = new File(privateKeyFile);
		byte[] encodedKey = new byte[(int) keyFile.length()];
		FileInputStream is = new FileInputStream(keyFile);
		is.read(encodedKey);
		is.close();

		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
		return privateKey;
	}

	/**
	 * Decrypts an encrypted RSA private key
	 * 
	 * @param instream
	 * @param password
	 * @return
	 * @throws InvalidKeyException
	 * @throws InvalidAlgorithmParameterException
	 * @throws IllegalStateException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeySpecException
	 * @throws InvalidParameterSpecException
	 * @throws IOException
	 *             if the key is unencrypted
	 */
	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException,
			IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
			InvalidParameterSpecException, IOException {

		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
		// System.out.println("Encrypted private key info's algorithm name is '"
		// + epki.getAlgName() + "'");

		AlgorithmParameters params = epki.getAlgParameters();
		if (params == null)
			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
		// PBEParameterSpec pbeParams = (PBEParameterSpec)
		// (params.getParameterSpec(PBEParameterSpec.class));

		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
		PBEKeySpec keySpec = new PBEKeySpec(password);
		Key key = sf.generateSecret(keySpec);
		keySpec.clearPassword();

		byte[] privateKeyInfoStream = null;
		Cipher cipher = Cipher.getInstance(epki.getAlgName());
		cipher.init(Cipher.DECRYPT_MODE, key, params);

		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
		return privateKeyInfoStream;
	}

	public static String xmlToString(Document doc) throws IOException {
		StringWriter sw = new StringWriter();
		XMLSerializer ser = new XMLSerializer(sw, new OutputFormat(doc));
		ser.serialize(doc.getDocumentElement());

		String XMLStr = sw.toString();
		return XMLStr;
	}
}

Best regards,

Richard A. Sand, CEO
Skyworth TTG USA, Inc.
+1 (866) 9-TRIPOD
http://www.skyworthttg.com/us

-----Original Message-----
From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
Sent: Friday, October 10, 2008 9:40 AM
To: security-dev@xml.apache.org
Subject: Re: problem enveloping a soap body

Richard Sand wrote:
> Hi Sean,
> 
> I guess I'm confused. I thought the whole point of the enveloping technique
> was that the signature would become part of the original document? 

But when you are enveloping an existing Document into the Object element, you 
get yourself into a problem in that you are asking it to insert the Signature at 
the top of the Document, but also insert the same Document inside the XML 
Signature Object element. Thus the DOMException is thrown because it is not 
possible to do that, since it violates the DOM hierarchy. Unless you have some 
tight memory constraints, I don't see that creating a new Document to hold the 
signature is a problem ... I assume it will be quickly serialized and sent over 
the network anyway.

--Sean



Re: problem enveloping a soap body

Posted by Sean Mullan <Se...@Sun.COM>.
Richard Sand wrote:
> Hi Sean,
> 
> I guess I'm confused. I thought the whole point of the enveloping technique
> was that the signature would become part of the original document? 

But when you are enveloping an existing Document into the Object element, you 
get yourself into a problem in that you are asking it to insert the Signature at 
the top of the Document, but also insert the same Document inside the XML 
Signature Object element. Thus the DOMException is thrown because it is not 
possible to do that, since it violates the DOM hierarchy. Unless you have some 
tight memory constraints, I don't see that creating a new Document to hold the 
signature is a problem ... I assume it will be quickly serialized and sent over 
the network anyway.

--Sean

> Really
> what I want to do is sign the soap body (id=Body) and then put the resulting
> signature into the soap header.
> 
> Am I better off trying to do a detached signature instead?  Then create a new
> blank document, add the body from the original, add a new node for the soap
> header, and put the detached signature content on that?
> 
> Best regards,
> 
> Richard A. Sand, CEO Skyworth TTG USA, Inc. +1 (866) 9-TRIPOD 
> http://www.skyworthttg.com/us
> 
> -----Original Message----- From: Sean.Mullan@Sun.COM
> [mailto:Sean.Mullan@Sun.COM] Sent: Wednesday, October 08, 2008 5:08 PM To:
> security-dev@xml.apache.org Subject: Re: problem enveloping a soap body
> 
> I believe it is because of this line:
> 
> // Create a DOMSignContext, specifying the PrivateKey and the document
>> // location of the XMLSignature DOMSignContext domSignContext = new
>> DOMSignContext(privateKey,
> doc.getDocumentElement());
> 
> 
> I think this is because you are trying to create an enveloping signature over
>  the Document that you parsed, but then also trying to insert the Signature 
> element as a child element of the root element of the same document. You
> really should create a brand new Document object to insert the Signature in
> and then pass the root Element of that document to the DOMSignContext above.
> 
> --Sean
> 
> Richard Sand wrote:
>> Hi Sean,
>> 
>> Thanks for the prompt reply! Actually I had tried that as well- it also
>> fails but I get a different error message and stacktrace.  Maybe this sheds
>> some light on what's happening?
>> 
>> org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to
>> insert a node where it is not permitted. at
>> org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source) at
>> org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source) at
>> org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source) at
>> org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source) at
>> TestClass2.signDocument(TestClass2.java:125) at
>> TestClass2.main(TestClass2.java:75)
>> 
>> Best regards,
>> 
>> Richard A. Sand, CEO Skyworth TTG USA, Inc. +1 (866) 9-TRIPOD 
>> http://www.skyworthttg.com/us
>> 
>> 
>> -----Original Message----- From: Sean.Mullan@Sun.COM
>> [mailto:Sean.Mullan@Sun.COM] Sent: Wednesday, October 08, 2008 9:31 AM To:
>> security-dev@xml.apache.org Subject: Re: problem enveloping a soap body
>> 
>> You are trying to import a Document node which is illegal according to 
>> Document.importNode. Try changing the following line:
>> 
>> XMLStructure content = new DOMStructure(doc);
>> 
>> to:
>> 
>> XMLStructure content = new DOMStructure(doc.getDocumentElement());
>> 
>> --Sean
>> 
>> 
>> Richard Sand wrote:
>>> Hi all,
>>> 
>>> I'm sure this has been encountered before... I'm trying to use the XML
>>> security API to sign a SOAP request. For various reasons I'm not using
>>> WS-Security, only XML security.
>>> 
>>> I've gone through the sample code provided with the API and I can see
>>> that the enveloping sample does not load the XML from an existing stream
>>> (such as a file), but rather instantiates the XML document
>>> programmatically. When I build my Document using any sort of stream, I
>>> get a DOMException upon signing, presumably because the Document cannot
>>> be altered.
>>> 
>>> org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not
>>> support the requested type of object or operation. at
>>> org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source) at
>>> org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source) at
>>> org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source) at
>>> org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source) at
>>> org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source) at
>>> org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source) at
>>> TestClass2.signDocument(TestClass2.java:125) at
>>> TestClass2.main(TestClass2.java:75)
>>> 
>>> My apologies for the elementary question, but how should I generate the
>>> Document such that DOMXMLSignature.sign() doesn't have this problem with
>>> importNode?
>>> 
>>> Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with
>>> only the libraries provided with xmlsec-1.4.2.
>>> 
>>> My source code is here for reference. FWIW I had a heck of a time loading
>>> an encrypted private key, I had to scour the net for that code too, so if
>>> that’s useful for anyone please help yourselves. :-)
>>> 
>>> import java.io.ByteArrayInputStream; import java.io.File; import
>>> java.io.FileInputStream; import java.io.IOException; import
>>> java.security.AlgorithmParameters; import
>>> java.security.InvalidAlgorithmParameterException; import
>>> java.security.InvalidKeyException; import java.security.Key; import
>>> java.security.KeyFactory; import java.security.NoSuchAlgorithmException; 
>>> import java.security.PrivateKey; import java.security.Provider; import
>>> java.security.PublicKey; import java.security.cert.Certificate; import
>>> java.security.cert.CertificateException; import
>>> java.security.cert.CertificateFactory; import
>>> java.security.spec.InvalidKeySpecException; import
>>> java.security.spec.InvalidParameterSpecException; import
>>> java.security.spec.PKCS8EncodedKeySpec; import java.util.Collections;
>>> 
>>> import javax.crypto.BadPaddingException; import javax.crypto.Cipher; 
>>> import javax.crypto.EncryptedPrivateKeyInfo; import
>>> javax.crypto.IllegalBlockSizeException; import
>>> javax.crypto.NoSuchPaddingException; import
>>> javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; 
>>> import javax.xml.crypto.XMLStructure; import
>>> javax.xml.crypto.dom.DOMStructure; 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.XMLObject; 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.KeyValue; import
>>> javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import
>>> javax.xml.crypto.dsig.spec.TransformParameterSpec; import
>>> javax.xml.parsers.DocumentBuilderFactory;
>>> 
>>> import org.w3c.dom.Document;
>>> 
>>> public class TestClass2 {
>>> 
>>> /** * @param args */ public static void main(String[] args) { String
>>> xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope
>>> xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body
>>> Id=\"body\"><ns2:getGoKartAll
>>> xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark
>>> Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>"; //
>>> String xmlStr = //
>>> "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark
>>> Red</color></getGoKartAll>"; // String xmlStr = "<Object>Some
>>> stuff</Object>"; String X509cert = "c:\\gkconfig.der"; String privateKey
>>> = "c:\\gkconfig_key.pk8"; String password = "password";
>>> 
>>> try { // Create a builder factory ByteArrayInputStream is = new
>>> ByteArrayInputStream(xmlStr.getBytes()); DocumentBuilderFactory factory =
>>> DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); 
>>> factory.setValidating(false);
>>> 
>>> // Create the builder and parse the input Document doc =
>>> factory.newDocumentBuilder().parse(is);
>>> 
>>> // Sign and output signDocument(doc, "body", privateKey, password,
>>> X509cert); String signed = doc.toString(); System.out.println("Signed: "
>>> + signed);
>>> 
>>> } catch (Exception e) { e.printStackTrace(); } }
>>> 
>>> public static void signDocument(Document doc, String reference, String
>>> privateKeyFile, String password, String x509certFile) throws Exception { 
>>> String providerName = System.getProperty("jsr105Provider",
>>> "org.jcp.xml.dsig.internal.dom.XMLDSigRI"); XMLSignatureFactory
>>> xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider)
>>> Class.forName(providerName).newInstance());
>>> 
>>> // Create a Reference to a same-document URI that is an Object // element
>>> and specify the SHA1 digest algorithm // Reference ref =
>>> xmlSigFactory.newReference(reference, //
>>> xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));
>>> 
>>> // also specify the SHA1 digest algorithm and the ENVELOPED Transform. 
>>> Reference ref = xmlSigFactory.newReference("",
>>> xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null),
>>> Collections.singletonList(xmlSigFactory 
>>> .newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null,
>>> null);
>>> 
>>> // Create the SignedInfo SignedInfo si =
>>> xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
>>>  (C14NMethodParameterSpec) null),
>>> xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null),
>>> Collections.singletonList(ref));
>>> 
>>> // Create the XML object from the document XMLStructure content = new
>>> DOMStructure(doc); XMLObject xmlobj =
>>> xmlSigFactory.newXMLObject(Collections.singletonList(content), "body",
>>> null, null);
>>> 
>>> // Load the public and private keys Certificate certs[] =
>>> loadX509CertificateChain(x509certFile); if (certs.length < 1) return; 
>>> PublicKey publicKey = certs[0].getPublicKey(); PrivateKey privateKey =
>>> loadPKCS8PrivateKey(privateKeyFile, password);
>>> 
>>> // Create a KeyInfo from the public key KeyInfoFactory kif =
>>> xmlSigFactory.getKeyInfoFactory(); KeyValue kv =
>>> kif.newKeyValue(publicKey); KeyInfo ki =
>>> kif.newKeyInfo(Collections.singletonList(kv));
>>> 
>>> // Create the XMLSignature (but don't sign it yet) XMLSignature signature
>>> = xmlSigFactory.newXMLSignature(si, ki,
>>> Collections.singletonList(xmlobj), null, null);
>>> 
>>> // Create a DOMSignContext, specifying the PrivateKey and the document //
>>> location of the XMLSignature DOMSignContext domSignContext = new
>>> DOMSignContext(privateKey, doc.getDocumentElement());
>>> 
>>> // Lastly, generate the enveloping signature using the PrivateKey 
>>> signature.sign(domSignContext); }
>>> 
>>> /** * Loads the DER-encoded X509 certificate chain * * @param
>>> certificateChainFileName * @return * @throws IOException * @throws
>>> CertificateException */ public static Certificate[]
>>> loadX509CertificateChain(String certificateChainFileName) throws
>>> IOException, CertificateException { FileInputStream certificateStream =
>>> new FileInputStream(certificateChainFileName); CertificateFactory
>>> certificateFactory = CertificateFactory.getInstance("X.509"); 
>>> java.security.cert.Certificate[] chain = {}; chain =
>>> certificateFactory.generateCertificates(certificateStream).toArray(chain);
>>>  certificateStream.close();
>>> 
>>> return chain; }
>>> 
>>> /** * Loads the DER-encoded, encrypted PKCS8 private key */ public static
>>> PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password)
>>> throws InvalidKeyException, InvalidParameterSpecException, 
>>> IllegalBlockSizeException, InvalidAlgorithmParameterException,
>>> NoSuchPaddingException, BadPaddingException, IOException,
>>> InvalidKeySpecException, NoSuchAlgorithmException { File keyFile = new
>>> File(privateKeyFile); byte[] encodedKey = new byte[(int)
>>> keyFile.length()]; FileInputStream is = new FileInputStream(keyFile); 
>>> is.read(encodedKey); is.close();
>>> 
>>> byte[] decryptedKey = decryptPrivateKey(encodedKey,
>>> password.toCharArray()); KeyFactory rSAKeyFactory =
>>> KeyFactory.getInstance("RSA"); PrivateKey privateKey =
>>> rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey)); 
>>> return privateKey; }
>>> 
>>> /** * Decrypts an encrypted RSA private key * * @param instream * @param
>>> password * @return * @throws InvalidKeyException * @throws
>>> InvalidAlgorithmParameterException * @throws IllegalStateException *
>>> @throws IllegalBlockSizeException * @throws BadPaddingException * @throws
>>> NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws
>>> InvalidKeySpecException * @throws InvalidParameterSpecException * @throws
>>> IOException if the key is unencrypted */ public static byte[]
>>> decryptPrivateKey(byte[] instream, char[] password) throws
>>> InvalidKeyException, InvalidAlgorithmParameterException,
>>> IllegalStateException, IllegalBlockSizeException, BadPaddingException,
>>> NoSuchAlgorithmException, NoSuchPaddingException,
>>> InvalidKeySpecException, InvalidParameterSpecException, IOException {
>>> 
>>> EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream); 
>>> //System.out.println("Encrypted private key info's algorithm name is '" +
>>> epki.getAlgName() + "'");
>>> 
>>> AlgorithmParameters params = epki.getAlgParameters(); if (params == null)
>>>  throw new IllegalStateException("The private key info's algorithm
>>> parameters are (null). The algorithm is probably not supported!"); 
>>> //PBEParameterSpec pbeParams = (PBEParameterSpec)
>>> (params.getParameterSpec(PBEParameterSpec.class));
>>> 
>>> SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName()); 
>>> PBEKeySpec keySpec = new PBEKeySpec(password); Key key =
>>> sf.generateSecret(keySpec); keySpec.clearPassword();
>>> 
>>> byte[] privateKeyInfoStream = null; Cipher cipher =
>>> Cipher.getInstance(epki.getAlgName()); cipher.init(Cipher.DECRYPT_MODE,
>>> key, params);
>>> 
>>> privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData()); return
>>> privateKeyInfoStream; } }
>>> 
>>> Best regards,
>>> 
>>> Richard A. Sand, CEO Skyworth TTG USA, Inc. +1 (866) 9-TRIPOD 
>>> http://www.skyworthttg.com/us
>>> 
> 


RE: problem enveloping a soap body

Posted by Richard Sand <Ri...@skyworthttg.com>.
Hi Sean,

I guess I'm confused. I thought the whole point of the enveloping technique was that the signature would become part of the original document?  Really what I want to do is sign the soap body (id=Body) and then put the resulting signature into the soap header.

Am I better off trying to do a detached signature instead?  Then create a new blank document, add the body from the original, add a new node for the soap header, and put the detached signature content on that?

Best regards,

Richard A. Sand, CEO
Skyworth TTG USA, Inc.
+1 (866) 9-TRIPOD
http://www.skyworthttg.com/us

-----Original Message-----
From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
Sent: Wednesday, October 08, 2008 5:08 PM
To: security-dev@xml.apache.org
Subject: Re: problem enveloping a soap body

I believe it is because of this line:

// Create a DOMSignContext, specifying the PrivateKey and the document
 > 		// location of the XMLSignature
 > 		DOMSignContext domSignContext = new DOMSignContext(privateKey, 
doc.getDocumentElement());


I think this is because you are trying to create an enveloping signature over 
the Document that you parsed, but then also trying to insert the Signature 
element as a child element of the root element of the same document. You really 
should create a brand new Document object to insert the Signature in and then 
pass the root Element of that document to the DOMSignContext above.

--Sean

Richard Sand wrote:
> Hi Sean,
> 
> Thanks for the prompt reply! Actually I had tried that as well- it also fails but I get a different error message and stacktrace.  Maybe this sheds some light on what's happening?
> 
> org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
> 	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
> 	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument(TestClass2.java:125)
> 	at TestClass2.main(TestClass2.java:75)
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 
> 
> -----Original Message-----
> From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
> Sent: Wednesday, October 08, 2008 9:31 AM
> To: security-dev@xml.apache.org
> Subject: Re: problem enveloping a soap body
> 
> You are trying to import a Document node which is illegal according to 
> Document.importNode. Try changing the following line:
> 
>   		XMLStructure content = new DOMStructure(doc);
> 
> to:
> 
>   		XMLStructure content = new DOMStructure(doc.getDocumentElement());
> 
> --Sean
> 
> 
> Richard Sand wrote:
>> Hi all,
>>
>> I'm sure this has been encountered before... I'm trying to use the XML security API to sign a SOAP request. For various reasons I'm not using WS-Security, only XML security.
>>
>> I've gone through the sample code provided with the API and I can see that the enveloping sample does not load the XML from an existing stream (such as a file), but rather instantiates the XML document programmatically. When I build my Document using any sort of stream, I get a DOMException upon signing, presumably because the Document cannot be altered.
>>
>> org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. 
>> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
>> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
>> 	at TestClass2.signDocument(TestClass2.java:125)
>> 	at TestClass2.main(TestClass2.java:75)
>>
>> My apologies for the elementary question, but how should I generate the Document such that DOMXMLSignature.sign() doesn't have this problem with importNode?
>>
>> Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with only the libraries provided with xmlsec-1.4.2.
>>
>> My source code is here for reference. FWIW I had a heck of a time loading an encrypted private key, I had to scour the net for that code too, so if that’s useful for anyone please help yourselves. :-)
>>
>> import java.io.ByteArrayInputStream;
>> import java.io.File;
>> import java.io.FileInputStream;
>> import java.io.IOException;
>> import java.security.AlgorithmParameters;
>> import java.security.InvalidAlgorithmParameterException;
>> import java.security.InvalidKeyException;
>> import java.security.Key;
>> import java.security.KeyFactory;
>> import java.security.NoSuchAlgorithmException;
>> import java.security.PrivateKey;
>> import java.security.Provider;
>> import java.security.PublicKey;
>> import java.security.cert.Certificate;
>> import java.security.cert.CertificateException;
>> import java.security.cert.CertificateFactory;
>> import java.security.spec.InvalidKeySpecException;
>> import java.security.spec.InvalidParameterSpecException;
>> import java.security.spec.PKCS8EncodedKeySpec;
>> import java.util.Collections;
>>
>> import javax.crypto.BadPaddingException;
>> import javax.crypto.Cipher;
>> import javax.crypto.EncryptedPrivateKeyInfo;
>> import javax.crypto.IllegalBlockSizeException;
>> import javax.crypto.NoSuchPaddingException;
>> import javax.crypto.SecretKeyFactory;
>> import javax.crypto.spec.PBEKeySpec;
>> import javax.xml.crypto.XMLStructure;
>> import javax.xml.crypto.dom.DOMStructure;
>> 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.XMLObject;
>> 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.KeyValue;
>> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
>> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
>> import javax.xml.parsers.DocumentBuilderFactory;
>>
>> import org.w3c.dom.Document;
>>
>> public class TestClass2 {
>>
>> 	/**
>> 	 * @param args
>> 	 */
>> 	public static void main(String[] args) {
>> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
>> 		// String xmlStr =
>> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
>> 		// String xmlStr = "<Object>Some stuff</Object>";
>> 		String X509cert = "c:\\gkconfig.der";
>> 		String privateKey = "c:\\gkconfig_key.pk8";
>> 		String password = "password";
>>
>> 		try {
>> 			// Create a builder factory
>> 			ByteArrayInputStream is = new ByteArrayInputStream(xmlStr.getBytes());
>> 			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
>> 			factory.setNamespaceAware(true);
>> 			factory.setValidating(false);
>>
>> 			// Create the builder and parse the input
>> 			Document doc = factory.newDocumentBuilder().parse(is);
>>
>> 			// Sign and output
>> 			signDocument(doc, "body", privateKey, password, X509cert);
>> 			String signed = doc.toString();
>> 			System.out.println("Signed: " + signed);
>>
>> 		} catch (Exception e) {
>> 			e.printStackTrace();
>> 		}
>> 	}
>>
>> 	public static void signDocument(Document doc, String reference, String privateKeyFile, String password, String x509certFile) throws Exception {
>> 		String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
>> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
>>
>> 		// Create a Reference to a same-document URI that is an Object
>> 		// element and specify the SHA1 digest algorithm
>> 		// Reference ref = xmlSigFactory.newReference(reference,
>> 		// xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));
>>
>> 		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
>> 		Reference ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
>> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
>>
>> 		// Create the SignedInfo
>> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
>> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));
>>
>> 		// Create the XML object from the document
>> 		XMLStructure content = new DOMStructure(doc);
>> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), "body", null, null);
>>
>> 		// Load the public and private keys
>> 		Certificate certs[] = loadX509CertificateChain(x509certFile);
>> 		if (certs.length < 1)
>> 			return;
>> 		PublicKey publicKey = certs[0].getPublicKey();
>> 		PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
>>
>> 		// Create a KeyInfo from the public key
>> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
>> 		KeyValue kv = kif.newKeyValue(publicKey);
>> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
>>
>> 		// Create the XMLSignature (but don't sign it yet)
>> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null);
>>
>> 		// Create a DOMSignContext, specifying the PrivateKey and the document
>> 		// location of the XMLSignature
>> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, doc.getDocumentElement());
>>
>> 		// Lastly, generate the enveloping signature using the PrivateKey
>> 		signature.sign(domSignContext);
>> 	}
>>
>> 	/**
>> 	 * Loads the DER-encoded X509 certificate chain
>> 	 * 
>> 	 * @param certificateChainFileName
>> 	 * @return
>> 	 * @throws IOException
>> 	 * @throws CertificateException
>> 	 */
>> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
>> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
>> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
>> 		java.security.cert.Certificate[] chain = {};
>> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
>> 		certificateStream.close();
>>
>> 		return chain;
>> 	}
>>
>> 	/**
>> 	 * Loads the DER-encoded, encrypted PKCS8 private key
>> 	 */
>> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
>> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
>> 			NoSuchAlgorithmException {
>> 		File keyFile = new File(privateKeyFile);
>> 		byte[] encodedKey = new byte[(int) keyFile.length()];
>> 		FileInputStream is = new FileInputStream(keyFile);
>> 		is.read(encodedKey);
>> 		is.close();
>>
>> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
>> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
>> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
>> 		return privateKey;
>> 	}
>>
>> 	/**
>> 	 * Decrypts an encrypted RSA private key
>> 	 *             
>> 	 * @param instream
>> 	 * @param password
>> 	 * @return
>> 	 * @throws InvalidKeyException
>> 	 * @throws InvalidAlgorithmParameterException
>> 	 * @throws IllegalStateException
>> 	 * @throws IllegalBlockSizeException
>> 	 * @throws BadPaddingException
>> 	 * @throws NoSuchAlgorithmException
>> 	 * @throws NoSuchPaddingException
>> 	 * @throws InvalidKeySpecException
>> 	 * @throws InvalidParameterSpecException
>> 	 * @throws IOException if the key is unencrypted
>> 	 */
>> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException,
>> 			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
>> 			InvalidParameterSpecException, IOException {
>>
>> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
>> 		//System.out.println("Encrypted private key info's algorithm name is '" + epki.getAlgName() + "'");
>>
>> 		AlgorithmParameters params = epki.getAlgParameters();
>> 		if (params == null)
>> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
>> 		//PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));
>>
>> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
>> 		PBEKeySpec keySpec = new PBEKeySpec(password);
>> 		Key key = sf.generateSecret(keySpec);
>> 		keySpec.clearPassword();
>>
>> 		byte[] privateKeyInfoStream = null;
>> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
>> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
>>
>> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
>> 		return privateKeyInfoStream;
>> 	}
>> }
>>
>> Best regards,
>>
>> Richard A. Sand, CEO
>> Skyworth TTG USA, Inc.
>> +1 (866) 9-TRIPOD
>> http://www.skyworthttg.com/us
>>
> 


Re: problem enveloping a soap body

Posted by Sean Mullan <Se...@Sun.COM>.
I believe it is because of this line:

// Create a DOMSignContext, specifying the PrivateKey and the document
 > 		// location of the XMLSignature
 > 		DOMSignContext domSignContext = new DOMSignContext(privateKey, 
doc.getDocumentElement());


I think this is because you are trying to create an enveloping signature over 
the Document that you parsed, but then also trying to insert the Signature 
element as a child element of the root element of the same document. You really 
should create a brand new Document object to insert the Signature in and then 
pass the root Element of that document to the DOMSignContext above.

--Sean

Richard Sand wrote:
> Hi Sean,
> 
> Thanks for the prompt reply! Actually I had tried that as well- it also fails but I get a different error message and stacktrace.  Maybe this sheds some light on what's happening?
> 
> org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
> 	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
> 	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument(TestClass2.java:125)
> 	at TestClass2.main(TestClass2.java:75)
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 
> 
> -----Original Message-----
> From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
> Sent: Wednesday, October 08, 2008 9:31 AM
> To: security-dev@xml.apache.org
> Subject: Re: problem enveloping a soap body
> 
> You are trying to import a Document node which is illegal according to 
> Document.importNode. Try changing the following line:
> 
>   		XMLStructure content = new DOMStructure(doc);
> 
> to:
> 
>   		XMLStructure content = new DOMStructure(doc.getDocumentElement());
> 
> --Sean
> 
> 
> Richard Sand wrote:
>> Hi all,
>>
>> I'm sure this has been encountered before... I'm trying to use the XML security API to sign a SOAP request. For various reasons I'm not using WS-Security, only XML security.
>>
>> I've gone through the sample code provided with the API and I can see that the enveloping sample does not load the XML from an existing stream (such as a file), but rather instantiates the XML document programmatically. When I build my Document using any sort of stream, I get a DOMException upon signing, presumably because the Document cannot be altered.
>>
>> org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. 
>> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
>> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
>> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
>> 	at TestClass2.signDocument(TestClass2.java:125)
>> 	at TestClass2.main(TestClass2.java:75)
>>
>> My apologies for the elementary question, but how should I generate the Document such that DOMXMLSignature.sign() doesn't have this problem with importNode?
>>
>> Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with only the libraries provided with xmlsec-1.4.2.
>>
>> My source code is here for reference. FWIW I had a heck of a time loading an encrypted private key, I had to scour the net for that code too, so if that’s useful for anyone please help yourselves. :-)
>>
>> import java.io.ByteArrayInputStream;
>> import java.io.File;
>> import java.io.FileInputStream;
>> import java.io.IOException;
>> import java.security.AlgorithmParameters;
>> import java.security.InvalidAlgorithmParameterException;
>> import java.security.InvalidKeyException;
>> import java.security.Key;
>> import java.security.KeyFactory;
>> import java.security.NoSuchAlgorithmException;
>> import java.security.PrivateKey;
>> import java.security.Provider;
>> import java.security.PublicKey;
>> import java.security.cert.Certificate;
>> import java.security.cert.CertificateException;
>> import java.security.cert.CertificateFactory;
>> import java.security.spec.InvalidKeySpecException;
>> import java.security.spec.InvalidParameterSpecException;
>> import java.security.spec.PKCS8EncodedKeySpec;
>> import java.util.Collections;
>>
>> import javax.crypto.BadPaddingException;
>> import javax.crypto.Cipher;
>> import javax.crypto.EncryptedPrivateKeyInfo;
>> import javax.crypto.IllegalBlockSizeException;
>> import javax.crypto.NoSuchPaddingException;
>> import javax.crypto.SecretKeyFactory;
>> import javax.crypto.spec.PBEKeySpec;
>> import javax.xml.crypto.XMLStructure;
>> import javax.xml.crypto.dom.DOMStructure;
>> 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.XMLObject;
>> 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.KeyValue;
>> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
>> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
>> import javax.xml.parsers.DocumentBuilderFactory;
>>
>> import org.w3c.dom.Document;
>>
>> public class TestClass2 {
>>
>> 	/**
>> 	 * @param args
>> 	 */
>> 	public static void main(String[] args) {
>> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
>> 		// String xmlStr =
>> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
>> 		// String xmlStr = "<Object>Some stuff</Object>";
>> 		String X509cert = "c:\\gkconfig.der";
>> 		String privateKey = "c:\\gkconfig_key.pk8";
>> 		String password = "password";
>>
>> 		try {
>> 			// Create a builder factory
>> 			ByteArrayInputStream is = new ByteArrayInputStream(xmlStr.getBytes());
>> 			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
>> 			factory.setNamespaceAware(true);
>> 			factory.setValidating(false);
>>
>> 			// Create the builder and parse the input
>> 			Document doc = factory.newDocumentBuilder().parse(is);
>>
>> 			// Sign and output
>> 			signDocument(doc, "body", privateKey, password, X509cert);
>> 			String signed = doc.toString();
>> 			System.out.println("Signed: " + signed);
>>
>> 		} catch (Exception e) {
>> 			e.printStackTrace();
>> 		}
>> 	}
>>
>> 	public static void signDocument(Document doc, String reference, String privateKeyFile, String password, String x509certFile) throws Exception {
>> 		String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
>> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
>>
>> 		// Create a Reference to a same-document URI that is an Object
>> 		// element and specify the SHA1 digest algorithm
>> 		// Reference ref = xmlSigFactory.newReference(reference,
>> 		// xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));
>>
>> 		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
>> 		Reference ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
>> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
>>
>> 		// Create the SignedInfo
>> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
>> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));
>>
>> 		// Create the XML object from the document
>> 		XMLStructure content = new DOMStructure(doc);
>> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), "body", null, null);
>>
>> 		// Load the public and private keys
>> 		Certificate certs[] = loadX509CertificateChain(x509certFile);
>> 		if (certs.length < 1)
>> 			return;
>> 		PublicKey publicKey = certs[0].getPublicKey();
>> 		PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
>>
>> 		// Create a KeyInfo from the public key
>> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
>> 		KeyValue kv = kif.newKeyValue(publicKey);
>> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
>>
>> 		// Create the XMLSignature (but don't sign it yet)
>> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null);
>>
>> 		// Create a DOMSignContext, specifying the PrivateKey and the document
>> 		// location of the XMLSignature
>> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, doc.getDocumentElement());
>>
>> 		// Lastly, generate the enveloping signature using the PrivateKey
>> 		signature.sign(domSignContext);
>> 	}
>>
>> 	/**
>> 	 * Loads the DER-encoded X509 certificate chain
>> 	 * 
>> 	 * @param certificateChainFileName
>> 	 * @return
>> 	 * @throws IOException
>> 	 * @throws CertificateException
>> 	 */
>> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
>> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
>> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
>> 		java.security.cert.Certificate[] chain = {};
>> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
>> 		certificateStream.close();
>>
>> 		return chain;
>> 	}
>>
>> 	/**
>> 	 * Loads the DER-encoded, encrypted PKCS8 private key
>> 	 */
>> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
>> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
>> 			NoSuchAlgorithmException {
>> 		File keyFile = new File(privateKeyFile);
>> 		byte[] encodedKey = new byte[(int) keyFile.length()];
>> 		FileInputStream is = new FileInputStream(keyFile);
>> 		is.read(encodedKey);
>> 		is.close();
>>
>> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
>> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
>> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
>> 		return privateKey;
>> 	}
>>
>> 	/**
>> 	 * Decrypts an encrypted RSA private key
>> 	 *             
>> 	 * @param instream
>> 	 * @param password
>> 	 * @return
>> 	 * @throws InvalidKeyException
>> 	 * @throws InvalidAlgorithmParameterException
>> 	 * @throws IllegalStateException
>> 	 * @throws IllegalBlockSizeException
>> 	 * @throws BadPaddingException
>> 	 * @throws NoSuchAlgorithmException
>> 	 * @throws NoSuchPaddingException
>> 	 * @throws InvalidKeySpecException
>> 	 * @throws InvalidParameterSpecException
>> 	 * @throws IOException if the key is unencrypted
>> 	 */
>> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException,
>> 			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
>> 			InvalidParameterSpecException, IOException {
>>
>> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
>> 		//System.out.println("Encrypted private key info's algorithm name is '" + epki.getAlgName() + "'");
>>
>> 		AlgorithmParameters params = epki.getAlgParameters();
>> 		if (params == null)
>> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
>> 		//PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));
>>
>> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
>> 		PBEKeySpec keySpec = new PBEKeySpec(password);
>> 		Key key = sf.generateSecret(keySpec);
>> 		keySpec.clearPassword();
>>
>> 		byte[] privateKeyInfoStream = null;
>> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
>> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
>>
>> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
>> 		return privateKeyInfoStream;
>> 	}
>> }
>>
>> Best regards,
>>
>> Richard A. Sand, CEO
>> Skyworth TTG USA, Inc.
>> +1 (866) 9-TRIPOD
>> http://www.skyworthttg.com/us
>>
> 


RE: problem enveloping a soap body

Posted by Richard Sand <Ri...@skyworthttg.com>.
Hi Sean,

Thanks for the prompt reply! Actually I had tried that as well- it also fails but I get a different error message and stacktrace.  Maybe this sheds some light on what's happening?

org.w3c.dom.DOMException: HIERARCHY_REQUEST_ERR: An attempt was made to insert a node where it is not permitted. 
	at org.apache.xerces.dom.ParentNode.internalInsertBefore(Unknown Source)
	at org.apache.xerces.dom.ParentNode.insertBefore(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
	at TestClass2.signDocument(TestClass2.java:125)
	at TestClass2.main(TestClass2.java:75)

Best regards,

Richard A. Sand, CEO
Skyworth TTG USA, Inc.
+1 (866) 9-TRIPOD
http://www.skyworthttg.com/us


-----Original Message-----
From: Sean.Mullan@Sun.COM [mailto:Sean.Mullan@Sun.COM] 
Sent: Wednesday, October 08, 2008 9:31 AM
To: security-dev@xml.apache.org
Subject: Re: problem enveloping a soap body

You are trying to import a Document node which is illegal according to 
Document.importNode. Try changing the following line:

  		XMLStructure content = new DOMStructure(doc);

to:

  		XMLStructure content = new DOMStructure(doc.getDocumentElement());

--Sean


Richard Sand wrote:
> Hi all,
> 
> I'm sure this has been encountered before... I'm trying to use the XML security API to sign a SOAP request. For various reasons I'm not using WS-Security, only XML security.
> 
> I've gone through the sample code provided with the API and I can see that the enveloping sample does not load the XML from an existing stream (such as a file), but rather instantiates the XML document programmatically. When I build my Document using any sort of stream, I get a DOMException upon signing, presumably because the Document cannot be altered.
> 
> org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. 
> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument(TestClass2.java:125)
> 	at TestClass2.main(TestClass2.java:75)
> 
> My apologies for the elementary question, but how should I generate the Document such that DOMXMLSignature.sign() doesn't have this problem with importNode?
> 
> Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with only the libraries provided with xmlsec-1.4.2.
> 
> My source code is here for reference. FWIW I had a heck of a time loading an encrypted private key, I had to scour the net for that code too, so if that’s useful for anyone please help yourselves. :-)
> 
> import java.io.ByteArrayInputStream;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.security.AlgorithmParameters;
> import java.security.InvalidAlgorithmParameterException;
> import java.security.InvalidKeyException;
> import java.security.Key;
> import java.security.KeyFactory;
> import java.security.NoSuchAlgorithmException;
> import java.security.PrivateKey;
> import java.security.Provider;
> import java.security.PublicKey;
> import java.security.cert.Certificate;
> import java.security.cert.CertificateException;
> import java.security.cert.CertificateFactory;
> import java.security.spec.InvalidKeySpecException;
> import java.security.spec.InvalidParameterSpecException;
> import java.security.spec.PKCS8EncodedKeySpec;
> import java.util.Collections;
> 
> import javax.crypto.BadPaddingException;
> import javax.crypto.Cipher;
> import javax.crypto.EncryptedPrivateKeyInfo;
> import javax.crypto.IllegalBlockSizeException;
> import javax.crypto.NoSuchPaddingException;
> import javax.crypto.SecretKeyFactory;
> import javax.crypto.spec.PBEKeySpec;
> import javax.xml.crypto.XMLStructure;
> import javax.xml.crypto.dom.DOMStructure;
> 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.XMLObject;
> 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.KeyValue;
> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
> import javax.xml.parsers.DocumentBuilderFactory;
> 
> import org.w3c.dom.Document;
> 
> public class TestClass2 {
> 
> 	/**
> 	 * @param args
> 	 */
> 	public static void main(String[] args) {
> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
> 		// String xmlStr =
> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
> 		// String xmlStr = "<Object>Some stuff</Object>";
> 		String X509cert = "c:\\gkconfig.der";
> 		String privateKey = "c:\\gkconfig_key.pk8";
> 		String password = "password";
> 
> 		try {
> 			// Create a builder factory
> 			ByteArrayInputStream is = new ByteArrayInputStream(xmlStr.getBytes());
> 			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
> 			factory.setNamespaceAware(true);
> 			factory.setValidating(false);
> 
> 			// Create the builder and parse the input
> 			Document doc = factory.newDocumentBuilder().parse(is);
> 
> 			// Sign and output
> 			signDocument(doc, "body", privateKey, password, X509cert);
> 			String signed = doc.toString();
> 			System.out.println("Signed: " + signed);
> 
> 		} catch (Exception e) {
> 			e.printStackTrace();
> 		}
> 	}
> 
> 	public static void signDocument(Document doc, String reference, String privateKeyFile, String password, String x509certFile) throws Exception {
> 		String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
> 
> 		// Create a Reference to a same-document URI that is an Object
> 		// element and specify the SHA1 digest algorithm
> 		// Reference ref = xmlSigFactory.newReference(reference,
> 		// xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));
> 
> 		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
> 		Reference ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
> 
> 		// Create the SignedInfo
> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));
> 
> 		// Create the XML object from the document
> 		XMLStructure content = new DOMStructure(doc);
> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), "body", null, null);
> 
> 		// Load the public and private keys
> 		Certificate certs[] = loadX509CertificateChain(x509certFile);
> 		if (certs.length < 1)
> 			return;
> 		PublicKey publicKey = certs[0].getPublicKey();
> 		PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
> 
> 		// Create a KeyInfo from the public key
> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
> 		KeyValue kv = kif.newKeyValue(publicKey);
> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
> 
> 		// Create the XMLSignature (but don't sign it yet)
> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null);
> 
> 		// Create a DOMSignContext, specifying the PrivateKey and the document
> 		// location of the XMLSignature
> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, doc.getDocumentElement());
> 
> 		// Lastly, generate the enveloping signature using the PrivateKey
> 		signature.sign(domSignContext);
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded X509 certificate chain
> 	 * 
> 	 * @param certificateChainFileName
> 	 * @return
> 	 * @throws IOException
> 	 * @throws CertificateException
> 	 */
> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
> 		java.security.cert.Certificate[] chain = {};
> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
> 		certificateStream.close();
> 
> 		return chain;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded, encrypted PKCS8 private key
> 	 */
> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
> 			NoSuchAlgorithmException {
> 		File keyFile = new File(privateKeyFile);
> 		byte[] encodedKey = new byte[(int) keyFile.length()];
> 		FileInputStream is = new FileInputStream(keyFile);
> 		is.read(encodedKey);
> 		is.close();
> 
> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
> 		return privateKey;
> 	}
> 
> 	/**
> 	 * Decrypts an encrypted RSA private key
> 	 *             
> 	 * @param instream
> 	 * @param password
> 	 * @return
> 	 * @throws InvalidKeyException
> 	 * @throws InvalidAlgorithmParameterException
> 	 * @throws IllegalStateException
> 	 * @throws IllegalBlockSizeException
> 	 * @throws BadPaddingException
> 	 * @throws NoSuchAlgorithmException
> 	 * @throws NoSuchPaddingException
> 	 * @throws InvalidKeySpecException
> 	 * @throws InvalidParameterSpecException
> 	 * @throws IOException if the key is unencrypted
> 	 */
> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException,
> 			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
> 			InvalidParameterSpecException, IOException {
> 
> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
> 		//System.out.println("Encrypted private key info's algorithm name is '" + epki.getAlgName() + "'");
> 
> 		AlgorithmParameters params = epki.getAlgParameters();
> 		if (params == null)
> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
> 		//PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));
> 
> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
> 		PBEKeySpec keySpec = new PBEKeySpec(password);
> 		Key key = sf.generateSecret(keySpec);
> 		keySpec.clearPassword();
> 
> 		byte[] privateKeyInfoStream = null;
> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
> 
> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
> 		return privateKeyInfoStream;
> 	}
> }
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 


Re: problem enveloping a soap body

Posted by Sean Mullan <Se...@Sun.COM>.
You are trying to import a Document node which is illegal according to 
Document.importNode. Try changing the following line:

  		XMLStructure content = new DOMStructure(doc);

to:

  		XMLStructure content = new DOMStructure(doc.getDocumentElement());

--Sean


Richard Sand wrote:
> Hi all,
> 
> I'm sure this has been encountered before... I'm trying to use the XML security API to sign a SOAP request. For various reasons I'm not using WS-Security, only XML security.
> 
> I've gone through the sample code provided with the API and I can see that the enveloping sample does not load the XML from an existing stream (such as a file), but rather instantiates the XML document programmatically. When I build my Document using any sort of stream, I get a DOMException upon signing, presumably because the Document cannot be altered.
> 
> org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. 
> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
> 	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
> 	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
> 	at TestClass2.signDocument(TestClass2.java:125)
> 	at TestClass2.main(TestClass2.java:75)
> 
> My apologies for the elementary question, but how should I generate the Document such that DOMXMLSignature.sign() doesn't have this problem with importNode?
> 
> Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with only the libraries provided with xmlsec-1.4.2.
> 
> My source code is here for reference. FWIW I had a heck of a time loading an encrypted private key, I had to scour the net for that code too, so if that’s useful for anyone please help yourselves. :-)
> 
> import java.io.ByteArrayInputStream;
> import java.io.File;
> import java.io.FileInputStream;
> import java.io.IOException;
> import java.security.AlgorithmParameters;
> import java.security.InvalidAlgorithmParameterException;
> import java.security.InvalidKeyException;
> import java.security.Key;
> import java.security.KeyFactory;
> import java.security.NoSuchAlgorithmException;
> import java.security.PrivateKey;
> import java.security.Provider;
> import java.security.PublicKey;
> import java.security.cert.Certificate;
> import java.security.cert.CertificateException;
> import java.security.cert.CertificateFactory;
> import java.security.spec.InvalidKeySpecException;
> import java.security.spec.InvalidParameterSpecException;
> import java.security.spec.PKCS8EncodedKeySpec;
> import java.util.Collections;
> 
> import javax.crypto.BadPaddingException;
> import javax.crypto.Cipher;
> import javax.crypto.EncryptedPrivateKeyInfo;
> import javax.crypto.IllegalBlockSizeException;
> import javax.crypto.NoSuchPaddingException;
> import javax.crypto.SecretKeyFactory;
> import javax.crypto.spec.PBEKeySpec;
> import javax.xml.crypto.XMLStructure;
> import javax.xml.crypto.dom.DOMStructure;
> 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.XMLObject;
> 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.KeyValue;
> import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
> import javax.xml.crypto.dsig.spec.TransformParameterSpec;
> import javax.xml.parsers.DocumentBuilderFactory;
> 
> import org.w3c.dom.Document;
> 
> public class TestClass2 {
> 
> 	/**
> 	 * @param args
> 	 */
> 	public static void main(String[] args) {
> 		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
> 		// String xmlStr =
> 		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
> 		// String xmlStr = "<Object>Some stuff</Object>";
> 		String X509cert = "c:\\gkconfig.der";
> 		String privateKey = "c:\\gkconfig_key.pk8";
> 		String password = "password";
> 
> 		try {
> 			// Create a builder factory
> 			ByteArrayInputStream is = new ByteArrayInputStream(xmlStr.getBytes());
> 			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
> 			factory.setNamespaceAware(true);
> 			factory.setValidating(false);
> 
> 			// Create the builder and parse the input
> 			Document doc = factory.newDocumentBuilder().parse(is);
> 
> 			// Sign and output
> 			signDocument(doc, "body", privateKey, password, X509cert);
> 			String signed = doc.toString();
> 			System.out.println("Signed: " + signed);
> 
> 		} catch (Exception e) {
> 			e.printStackTrace();
> 		}
> 	}
> 
> 	public static void signDocument(Document doc, String reference, String privateKeyFile, String password, String x509certFile) throws Exception {
> 		String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
> 		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
> 
> 		// Create a Reference to a same-document URI that is an Object
> 		// element and specify the SHA1 digest algorithm
> 		// Reference ref = xmlSigFactory.newReference(reference,
> 		// xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));
> 
> 		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
> 		Reference ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
> 				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
> 
> 		// Create the SignedInfo
> 		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
> 				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));
> 
> 		// Create the XML object from the document
> 		XMLStructure content = new DOMStructure(doc);
> 		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), "body", null, null);
> 
> 		// Load the public and private keys
> 		Certificate certs[] = loadX509CertificateChain(x509certFile);
> 		if (certs.length < 1)
> 			return;
> 		PublicKey publicKey = certs[0].getPublicKey();
> 		PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);
> 
> 		// Create a KeyInfo from the public key
> 		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
> 		KeyValue kv = kif.newKeyValue(publicKey);
> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
> 
> 		// Create the XMLSignature (but don't sign it yet)
> 		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null);
> 
> 		// Create a DOMSignContext, specifying the PrivateKey and the document
> 		// location of the XMLSignature
> 		DOMSignContext domSignContext = new DOMSignContext(privateKey, doc.getDocumentElement());
> 
> 		// Lastly, generate the enveloping signature using the PrivateKey
> 		signature.sign(domSignContext);
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded X509 certificate chain
> 	 * 
> 	 * @param certificateChainFileName
> 	 * @return
> 	 * @throws IOException
> 	 * @throws CertificateException
> 	 */
> 	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
> 		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
> 		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
> 		java.security.cert.Certificate[] chain = {};
> 		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
> 		certificateStream.close();
> 
> 		return chain;
> 	}
> 
> 	/**
> 	 * Loads the DER-encoded, encrypted PKCS8 private key
> 	 */
> 	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
> 			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
> 			NoSuchAlgorithmException {
> 		File keyFile = new File(privateKeyFile);
> 		byte[] encodedKey = new byte[(int) keyFile.length()];
> 		FileInputStream is = new FileInputStream(keyFile);
> 		is.read(encodedKey);
> 		is.close();
> 
> 		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
> 		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
> 		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
> 		return privateKey;
> 	}
> 
> 	/**
> 	 * Decrypts an encrypted RSA private key
> 	 *             
> 	 * @param instream
> 	 * @param password
> 	 * @return
> 	 * @throws InvalidKeyException
> 	 * @throws InvalidAlgorithmParameterException
> 	 * @throws IllegalStateException
> 	 * @throws IllegalBlockSizeException
> 	 * @throws BadPaddingException
> 	 * @throws NoSuchAlgorithmException
> 	 * @throws NoSuchPaddingException
> 	 * @throws InvalidKeySpecException
> 	 * @throws InvalidParameterSpecException
> 	 * @throws IOException if the key is unencrypted
> 	 */
> 	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException,
> 			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
> 			InvalidParameterSpecException, IOException {
> 
> 		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
> 		//System.out.println("Encrypted private key info's algorithm name is '" + epki.getAlgName() + "'");
> 
> 		AlgorithmParameters params = epki.getAlgParameters();
> 		if (params == null)
> 			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
> 		//PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));
> 
> 		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
> 		PBEKeySpec keySpec = new PBEKeySpec(password);
> 		Key key = sf.generateSecret(keySpec);
> 		keySpec.clearPassword();
> 
> 		byte[] privateKeyInfoStream = null;
> 		Cipher cipher = Cipher.getInstance(epki.getAlgName());
> 		cipher.init(Cipher.DECRYPT_MODE, key, params);
> 
> 		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
> 		return privateKeyInfoStream;
> 	}
> }
> 
> Best regards,
> 
> Richard A. Sand, CEO
> Skyworth TTG USA, Inc.
> +1 (866) 9-TRIPOD
> http://www.skyworthttg.com/us
> 


problem enveloping a soap body

Posted by Richard Sand <Ri...@skyworthttg.com>.
Hi all,

I'm sure this has been encountered before... I'm trying to use the XML security API to sign a SOAP request. For various reasons I'm not using WS-Security, only XML security.

I've gone through the sample code provided with the API and I can see that the enveloping sample does not load the XML from an existing stream (such as a file), but rather instantiates the XML document programmatically. When I build my Document using any sort of stream, I get a DOMException upon signing, presumably because the Document cannot be altered.

org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation. 
	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
	at org.apache.xerces.dom.CoreDocumentImpl.importNode(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMUtils.appendChild(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLObject.marshal(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.marshal(Unknown Source)
	at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(Unknown Source)
	at TestClass2.signDocument(TestClass2.java:125)
	at TestClass2.main(TestClass2.java:75)

My apologies for the elementary question, but how should I generate the Document such that DOMXMLSignature.sign() doesn't have this problem with importNode?

Thanks for any help! FYI I'm using Sun JDK 1.5.0_12 and building with only the libraries provided with xmlsec-1.4.2.

My source code is here for reference. FWIW I had a heck of a time loading an encrypted private key, I had to scour the net for that code too, so if that’s useful for anyone please help yourselves. :-)

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collections;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
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.XMLObject;
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.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

public class TestClass2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body Id=\"body\"><ns2:getGoKartAll xmlns:ns2=\"http://soa.examples.ttg.com\"><ns2:model>1</ns2:model><ns2:width>33</ns2:width><ns2:length>66</ns2:length><ns2:tires>6</ns2:tires><ns2:color>Dark Red</ns2:color></ns2:getGoKartAll></soapenv:Body></soapenv:Envelope>";
		// String xmlStr =
		// "<getGoKartAll><model>1</model><width>33</width><length>66</length><tires>6</tires><color>Dark Red</color></getGoKartAll>";
		// String xmlStr = "<Object>Some stuff</Object>";
		String X509cert = "c:\\gkconfig.der";
		String privateKey = "c:\\gkconfig_key.pk8";
		String password = "password";

		try {
			// Create a builder factory
			ByteArrayInputStream is = new ByteArrayInputStream(xmlStr.getBytes());
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			factory.setValidating(false);

			// Create the builder and parse the input
			Document doc = factory.newDocumentBuilder().parse(is);

			// Sign and output
			signDocument(doc, "body", privateKey, password, X509cert);
			String signed = doc.toString();
			System.out.println("Signed: " + signed);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void signDocument(Document doc, String reference, String privateKeyFile, String password, String x509certFile) throws Exception {
		String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
		XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());

		// Create a Reference to a same-document URI that is an Object
		// element and specify the SHA1 digest algorithm
		// Reference ref = xmlSigFactory.newReference(reference,
		// xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null));

		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
		Reference ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory
				.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);

		// Create the SignedInfo
		SignedInfo si = xmlSigFactory.newSignedInfo(xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
				(C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));

		// Create the XML object from the document
		XMLStructure content = new DOMStructure(doc);
		XMLObject xmlobj = xmlSigFactory.newXMLObject(Collections.singletonList(content), "body", null, null);

		// Load the public and private keys
		Certificate certs[] = loadX509CertificateChain(x509certFile);
		if (certs.length < 1)
			return;
		PublicKey publicKey = certs[0].getPublicKey();
		PrivateKey privateKey = loadPKCS8PrivateKey(privateKeyFile, password);

		// Create a KeyInfo from the public key
		KeyInfoFactory kif = xmlSigFactory.getKeyInfoFactory();
		KeyValue kv = kif.newKeyValue(publicKey);
		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

		// Create the XMLSignature (but don't sign it yet)
		XMLSignature signature = xmlSigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null);

		// Create a DOMSignContext, specifying the PrivateKey and the document
		// location of the XMLSignature
		DOMSignContext domSignContext = new DOMSignContext(privateKey, doc.getDocumentElement());

		// Lastly, generate the enveloping signature using the PrivateKey
		signature.sign(domSignContext);
	}

	/**
	 * Loads the DER-encoded X509 certificate chain
	 * 
	 * @param certificateChainFileName
	 * @return
	 * @throws IOException
	 * @throws CertificateException
	 */
	public static Certificate[] loadX509CertificateChain(String certificateChainFileName) throws IOException, CertificateException {
		FileInputStream certificateStream = new FileInputStream(certificateChainFileName);
		CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
		java.security.cert.Certificate[] chain = {};
		chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
		certificateStream.close();

		return chain;
	}

	/**
	 * Loads the DER-encoded, encrypted PKCS8 private key
	 */
	public static PrivateKey loadPKCS8PrivateKey(String privateKeyFile, String password) throws InvalidKeyException, InvalidParameterSpecException,
			IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, BadPaddingException, IOException, InvalidKeySpecException,
			NoSuchAlgorithmException {
		File keyFile = new File(privateKeyFile);
		byte[] encodedKey = new byte[(int) keyFile.length()];
		FileInputStream is = new FileInputStream(keyFile);
		is.read(encodedKey);
		is.close();

		byte[] decryptedKey = decryptPrivateKey(encodedKey, password.toCharArray());
		KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(decryptedKey));
		return privateKey;
	}

	/**
	 * Decrypts an encrypted RSA private key
	 *             
	 * @param instream
	 * @param password
	 * @return
	 * @throws InvalidKeyException
	 * @throws InvalidAlgorithmParameterException
	 * @throws IllegalStateException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeySpecException
	 * @throws InvalidParameterSpecException
	 * @throws IOException if the key is unencrypted
	 */
	public static byte[] decryptPrivateKey(byte[] instream, char[] password) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalStateException,
			IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
			InvalidParameterSpecException, IOException {

		EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
		//System.out.println("Encrypted private key info's algorithm name is '" + epki.getAlgName() + "'");

		AlgorithmParameters params = epki.getAlgParameters();
		if (params == null)
			throw new IllegalStateException("The private key info's algorithm parameters are (null). The algorithm is probably not supported!");
		//PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));

		SecretKeyFactory sf = SecretKeyFactory.getInstance(epki.getAlgName());
		PBEKeySpec keySpec = new PBEKeySpec(password);
		Key key = sf.generateSecret(keySpec);
		keySpec.clearPassword();

		byte[] privateKeyInfoStream = null;
		Cipher cipher = Cipher.getInstance(epki.getAlgName());
		cipher.init(Cipher.DECRYPT_MODE, key, params);

		privateKeyInfoStream = cipher.doFinal(epki.getEncryptedData());
		return privateKeyInfoStream;
	}
}

Best regards,

Richard A. Sand, CEO
Skyworth TTG USA, Inc.
+1 (866) 9-TRIPOD
http://www.skyworthttg.com/us


DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961





--- Comment #3 from Anton Kosyakov <an...@gmail.com>  2008-10-07 21:51:17 PST ---
> You say you parsed it into a new document, so did you use Document.importNode
> when replacing the SignedInfo in the original document?

See org.apache.xml.security.signature.SignedInfo(element : Element, BaseURI :
String) constructor.

> In any case, we will need a reproducible test case to analyze this. Please 
> attach it to the bug report, thanks.

Ok. See attachement.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961





--- Comment #2 from Anton Kosyakov <an...@gmail.com>  2008-10-07 21:48:06 PST ---
Created an attachment (id=22689)
 --> (https://issues.apache.org/bugzilla/attachment.cgi?id=22689)
Bug 45961 test case. (Eclipse project)


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961





--- Comment #9 from Anton Kosyakov <an...@gmail.com>  2008-10-15 03:24:53 PST ---
I sign document with enveloped transform and own canonicaliztion method.
Document verification is finishing with error in a work of transformer
TransformEnvelopedSignature. Because a instance of SignedInfo saves references
to old element Reference into array _referenceEl. See attachment.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961





--- Comment #8 from Anton Kosyakov <an...@gmail.com>  2008-10-15 03:01:24 PST ---
Created an attachment (id=22732)
 --> (https://issues.apache.org/bugzilla/attachment.cgi?id=22732)
Bug 45961 test case 3. 


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


sean.mullan@sun.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|                            |FIXED




--- Comment #10 from sean.mullan@sun.com  2008-10-16 08:44:51 PST ---
Thanks for the updated test case. Should be fixed now. Fixes have been checked
into latest source tree.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


Anton Kosyakov <an...@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |




-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


Anton Kosyakov <an...@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |NEW




--- Comment #6 from Anton Kosyakov <an...@gmail.com>  2008-10-12 22:42:02 PST ---
(In reply to comment #4)
> This is not a bug. You are invoking the XMLSignature(Element, String)
> constructor with a Signature element that is not complete. This constructor
> should be invoked when you are validating an XML Signature that has been parsed
> from a Document. You need to first generate an XML Signature using your
> CanonicalizationMethod, but before you do that you must register your
> CanonicalizationMethod impl. with the register method so that the XMLSec
> library is aware of it. You may want to look at some of the XML Signature
> samples to see how to generate XML Signatures.
> 

Ok. Now I’m invoking the XMLSignature(Element, String) constructor with a
Signature element that is complete. And again I'm getting a exception with a
error message “Cannot find SignatureValue in Signature”. See attachment.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


sean.mullan@sun.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |FIXED




--- Comment #7 from sean.mullan@sun.com  2008-10-14 08:18:12 PST ---
Fixed in the latest source tree.

The problem was that if you define a custom canonicalization method, the
SignedInfo element is canonicalized and replaced before validating the
signature and references (as a security precaution). However, the code was
still holding a stale reference to the old SignedInfo element, thus the
exception. The fix was to simply get a reference to the new SignedInfo element.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


Anton Kosyakov <an...@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEEDINFO                    |ASSIGNED




-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


sean.mullan@sun.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|ASSIGNED                    |RESOLVED
         Resolution|                            |INVALID




--- Comment #4 from sean.mullan@sun.com  2008-10-10 06:07:22 PST ---
This is not a bug. You are invoking the XMLSignature(Element, String)
constructor with a Signature element that is not complete. This constructor
should be invoked when you are validating an XML Signature that has been parsed
from a Document. You need to first generate an XML Signature using your
CanonicalizationMethod, but before you do that you must register your
CanonicalizationMethod impl. with the register method so that the XMLSec
library is aware of it. You may want to look at some of the XML Signature
samples to see how to generate XML Signatures.


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

DO NOT REPLY [Bug 45961] verify with own canonicalization method

Posted by bu...@apache.org.
https://issues.apache.org/bugzilla/show_bug.cgi?id=45961


Anton Kosyakov <an...@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
  Attachment #22689|0                           |1
        is obsolete|                            |




--- Comment #5 from Anton Kosyakov <an...@gmail.com>  2008-10-12 22:05:44 PST ---
Created an attachment (id=22717)
 --> (https://issues.apache.org/bugzilla/attachment.cgi?id=22717)
Bug 45961 test case 2. (Eclipse project)


-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.