I am using WSS4J to create a soap request signer with a certificate from the keystore. My keystore format is .pkcs and everything works with the first signing, but if I try to run the program to sign more than one document and it doesn't find the certificate in the keystore. It seems to me that the certificate is possibly consumed during signing, which means that it no longer exists for the current environment. If the program stops and starts again it will sign the first request but the second request fails. I have stepped through multiple times and cannot figure out what the cause is. This code is the last thing being called before the certificate disappears from the keystore this.certUri = getWsConfig().getIdAllocator().createSecureId("X509-", certs[0]);
this is found on line 556 in WSSecSignature.class
. Here is my code.
package com.soapsigner;
import java.io.*;
import java.util.Properties;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.ws.security.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.w3c.dom.Document;
import org.xml.sax.*;
public class SoapSigner {
private Crypto crypto;
private WSSecSignature sig;
private WSSecTimestamp time;
private WSSecHeader header;
private String alias;
private String password;
private String keyFile;
private String keyFileType;
private Document signedDoc;
private String lastError;
{
Logger rootLogger = Logger.getRootLogger();
rootLogger.setLevel(Level.INFO);
rootLogger.addAppender(new ConsoleAppender(
new PatternLayout("%-6r [%p] %c - %m%n")));
}
//constructor
public SoapSigner(String XML){
try {
alias = "myalias";
password = "mypassword";
keyFile = "/keystore/mykeystore.pkcs";
keyFileType = "pkcs12";
sig = new WSSecSignature();
time = new WSSecTimestamp();
header = new WSSecHeader();
signedDoc = null;
lastError = "";
Merlin merlin = new Merlin(getCryptoProperties());
System.out.println("real signing keystore object: "+merlin.getKeyStore().getCertificate(alias).toString().length()); //Passed
crypto = merlin;
signDocument(xmlToDoc(XML));
Merlin test = new Merlin(getCryptoProperties());
System.out.println("test keystore object: "+test.getKeyStore().getCertificate(alias).toString().length()); //Failed, this is null
} catch (Exception e) {
setLastError(e);
}
}
//properties
public Properties getCryptoProperties(){
Properties cryptoProperties = new Properties();
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.alias", alias);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", password);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", keyFileType);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.file", keyFile);
return cryptoProperties;
}
//sign the document
public void signDocument(Document doc){
try {
header.setMustUnderstand(true);
sig.setSignatureAlgorithm(WSConstants.C14N_EXCL_OMIT_COMMENTS);
sig.setSignatureAlgorithm(WSConstants.RSA);
sig.setUserInfo(alias, password);
sig.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
header.insertSecurityHeader(doc);
sig.build(doc, crypto, header);
time.build(doc, header);
signedDoc = doc;
} catch (Exception e) {
setLastError(e);
}
}
//get the signed document
public Document getDocument(){
return signedDoc;
}
//get the signed xml
public String getXML(){
return getStringFromDoc(getDocument()).trim();
}
//get last error
public String getLastError(){
return lastError;
}
//set last error
private void setLastError(Throwable e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
lastError += " NEXT ERROR "+sw.toString();
e.printStackTrace();
}
//document to string
public String getStringFromDoc(Document doc){
try
{
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
writer.flush();
return writer.toString();
}
catch(Exception e)
{
setLastError(e);
return null;
}
}
//string to document
public Document xmlToDoc(String XML){
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db;
db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(XML));
Document doc = db.parse(is);
return doc;
} catch (Exception e) {
setLastError(e);
return null;
}
}
//main
public static void main(String[] args){
String XML1 = "<?xml version='1.0' encoding='UTF-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> <soap:Body> <test1></test1> </soap:Body> </soap:Envelope>";
String XML2 = "<?xml version='1.0' encoding='UTF-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> <soap:Body> <test2></test2> </soap:Body> </soap:Envelope>";
new SoapSigner(XML1);
new SoapSigner(XML2);
}
}
Also, I would like to turn the doDebug mode on in the WSSecBase.class
but it won't showup in the variable viewer to toggle the value. I created a breakpoint in the constructor and set it to be watched but it never shows up to toggle.
Your testcase works fine for me with a .p12 from WSS4J test source on the latest WSS4J SNAPSHOT code. Are you using an older version of WSS4J? If so please try with the latest releases + see if it works. If not, then please create a test-case with your .p12 file + create a new issue in the WSS4J JIRA:
https://issues.apache.org/jira/browse/WSS
Colm.