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