Return-Path: Delivered-To: apmail-xml-security-dev-archive@www.apache.org Received: (qmail 60051 invoked from network); 10 Oct 2008 13:40:17 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 10 Oct 2008 13:40:17 -0000 Received: (qmail 51308 invoked by uid 500); 10 Oct 2008 13:40:16 -0000 Delivered-To: apmail-xml-security-dev-archive@xml.apache.org Received: (qmail 51283 invoked by uid 500); 10 Oct 2008 13:40:16 -0000 Mailing-List: contact security-dev-help@xml.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: Reply-To: security-dev@xml.apache.org List-Id: Delivered-To: mailing list security-dev@xml.apache.org Received: (qmail 51272 invoked by uid 99); 10 Oct 2008 13:40:15 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 10 Oct 2008 06:40:15 -0700 X-ASF-Spam-Status: No, hits=-3.6 required=10.0 tests=DNS_FROM_SECURITYSAGE,RCVD_IN_DNSWL_MED,SPF_PASS,WHOIS_DMNBYPROXY X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: local policy) Received: from [192.18.98.31] (HELO brmea-mail-1.sun.com) (192.18.98.31) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 10 Oct 2008 13:39:07 +0000 Received: from fe-amer-09.sun.com ([192.18.109.79]) by brmea-mail-1.sun.com (8.13.6+Sun/8.12.9) with ESMTP id m9ADdXRO022847 for ; Fri, 10 Oct 2008 13:39:33 GMT Received: from conversion-daemon.mail-amer.sun.com by mail-amer.sun.com (Sun Java System Messaging Server 6.2-8.04 (built Feb 28 2007)) id <0K8I00901YUZW600@mail-amer.sun.com> (original mail from Sean.Mullan@Sun.COM) for security-dev@xml.apache.org; Fri, 10 Oct 2008 07:39:33 -0600 (MDT) Received: from sean-mullans-imac-2.local ([129.150.65.105]) by mail-amer.sun.com (Sun Java System Messaging Server 6.2-8.04 (built Feb 28 2007)) with ESMTPSA id <0K8I0068QZ9V3L20@mail-amer.sun.com> for security-dev@xml.apache.org; Fri, 10 Oct 2008 07:39:32 -0600 (MDT) Date: Fri, 10 Oct 2008 09:39:30 -0400 From: Sean Mullan Subject: Re: problem enveloping a soap body In-reply-to: Sender: Sean.Mullan@Sun.COM To: security-dev@xml.apache.org Message-id: <48EF5B12.9040203@sun.com> MIME-version: 1.0 Content-type: text/plain; format=flowed; charset=UTF-8 Content-transfer-encoding: 8BIT References: <20081008045118.1F97F234C210@brutus.apache.org> <48ECB611.4090208@sun.com> <48ED2139.8040109@sun.com> User-Agent: Thunderbird 2.0.0.12 (Macintosh/20080213) X-Virus-Checked: Checked by ClamAV on apache.org 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 = ">> xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">>> Id=\"body\">>> xmlns:ns2=\"http://soa.examples.ttg.com\">133666Dark >>> Red"; // >>> String xmlStr = // >>> "133666Dark >>> Red"; // String xmlStr = "Some >>> stuff"; 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 >>> >