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


Reply via email to