How to Digitally Sign a SOAP request BODY in ColdFusion?

若如初见. 提交于 2019-12-10 19:44:50

问题


New one for me: I need to digitally sign and encrypt the body of SOAP request from a ColdFusion client application using a provider-issued certificate. I also need to decrypt the response in order to process it.

I have searched for days an have found nothing. I have found information referencing other languages, but nothing in ColdFusion. If it is not possible to do it natively through the ColdFusion language API, can someone help me with possibly calling the correct Java or .net classes via the 'createObject()' function or any other method you may have?

17SEP2012 - ADDITIONAL INFO:

More Info: Post timed out while I was looking at code. Here is the complete post:

I am consuming a web service, not providing one. I have reached the point where I have the whole thing working, but it only works once through a ColdFusion "createObject" invokation.

Still need help. System: Windows Server 2003 with ColdFusion 9 single server installation.

I used Apache's wss4J library and wrote a simple Java class as the entry point. The custom Java class simply takes the complete SOAP message as a String argument, passes the String to the wss4j DateStamp class, then passes the resulting SOAP parts object to the signature class, then the encryption class. It then returns the signed and encrypted SOAP envelope, which has been converted from a document (SOAP parts) to a String by the PrettyDocumentToString function.

All of this works and I get a SOAP envelope with security header and signed, encrypted body. The problem is that, after restarting the ColdFusion service (single server install on Windows Server 2003), it all works one time, but subsequent runs result in an error occurring within the wss4j signature code. I have even used Winmerge to compare the entire ColdFusion9 directory immediately after restart, immediately after the 1st run, and immediately after the 2nd run. The only differences were in the log files. There were differences in the ColdFusion9\lib\neo-datasource.xml file, but only in the order of a couple of datasource descriptors, not the content.

Below is the code and stack trace:

writeOutputs and writeDumps are only for visualization during debugging.

ColdFusion calling script:

<cfscript>
    variables.tempPath = getDirectoryFromPath(getCurrentTemplatePath());
    variables.filePath = tempPath & "ASI_source_request_example.xml";
    variables.fileContent = FileRead(filePath);

writeOutput("FILECONTENT: <br>");
writeOutput("variables.fileContent);
writeDump(var="#variables.fileContent#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");

    variables.encSOAP=createobject("java","ProcessIDSRSSOAP").runProcess(fileContent);

writeOutput("<br><br>encSOAP: <br>");
writeOutput(variables.encSOAP);
writeDump(var="#variables.encSOAP#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");
</cfscript>

Java class:

import java.io.FileReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.ws.security.SOAPConstants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.common.SOAPUtil;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.message.WSSecEncrypt;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.apache.ws.security.util.WSSecurityUtil;
import org.w3c.dom.Document;

public class ProcessIDSRSSOAP {
    private static Crypto crypto = null;
    private static Properties properties = new Properties();
    private static String user = "";
    private static String cryptoPwd = "";
    private static WSSecSignature builder = new WSSecSignature();
    private static SOAPConstants soapConstants = null;
    private final WSSecHeader secHeader = new WSSecHeader();
    private Document tsDoc = null;
    private Document signedDoc = null;
    private Document encryptedDoc = null;

    private static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
            .getLog(ProcessIDSRSSOAP.class);

    public ProcessIDSRSSOAP() throws Exception {
        WSSConfig.init();
    }

    /*
     * public static void main(String[] args) throws Exception {
     * ProcessIDSRSSOAP processor = new ProcessIDSRSSOAP();
     * processor.runProcess(args[0]); }
     */

    public String runProcess(String inDoc) throws Exception {
        // ProcessIDSRSSOAP processor = new ProcessIDSRSSOAP();
        // LOG.debug(inDoc);
        Class<ProcessIDSRSSOAP> thisClass = ProcessIDSRSSOAP.class;
        ClassLoader thisLoader = thisClass.getClassLoader();
        URL propertiesURL = thisLoader.getResource("crypto.properties");
        String propertiesPath = propertiesURL.getPath();
        propertiesPath = propertiesPath.replaceAll("%20", " ");
        properties.load(new FileReader(propertiesPath));
        user = properties
                .getProperty("org.apache.ws.security.crypto.merlin.keystore.alias");
        cryptoPwd = properties
                .getProperty("org.apache.ws.security.crypto.merlin.keystore.password");
        crypto = CryptoFactory.getInstance("crypto.properties");
        builder.setUserInfo(user, cryptoPwd);
        builder.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
        SOAPUtil.toSOAPPart(inDoc.trim());
        Document PKIDoc = processDoc(inDoc);
        String PKIDocString = org.apache.ws.security.util.XMLUtils
                .PrettyDocumentToString(PKIDoc);
        LOG.debug(PKIDocString);
        return PKIDocString;
    }

    /**
     * @param SOAPMsg
     *            The entire SOAP message as a type String
     * @throws Exception
     */
    public Document processDoc(String SOAPMsg) throws Exception {
        tsDoc = timestampMSG(SOAPMsg);// Time stamp the SOAP String and make it
                                        // a Document type.
        secHeader.insertSecurityHeader(tsDoc);// Insert the security header.
        soapConstants = WSSecurityUtil.getSOAPConstants(tsDoc
                .getDocumentElement());
        signedDoc = signBody(tsDoc);// Send the Document on for signing.
        encryptedDoc = encryptBody(signedDoc);
        return encryptedDoc;
    }

    /**
     * @param msg
     *            The entire SOAP message as a type String
     * @throws Exception
     */
    public Document timestampMSG(String msg) throws Exception {
        Document doc = SOAPUtil.toSOAPPart(msg);
        WSSecHeader secHeader = new WSSecHeader();
        secHeader.insertSecurityHeader(doc);

        WSSecTimestamp timestamp = new WSSecTimestamp();
        timestamp.setTimeToLive(300);
        Document createdDoc = timestamp.build(doc, secHeader);
        return createdDoc;
    }

    /**
     * @param doc
     *            Expects a SOAP message as a type Document
     * @throws Exception
     */
    public Document signBody(Document doc) throws Exception {
        List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
        WSEncryptionPart encP = new WSEncryptionPart(soapConstants
                .getBodyQName().getLocalPart(), soapConstants.getEnvelopeURI(),
                "");
        parts.add(encP);
        builder.setParts(parts);
        Document signedDoc = builder.build(doc, crypto, secHeader);
        return signedDoc;
    }

    public Document encryptBody(Document doc) throws Exception {
        SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(doc
                .getDocumentElement());
        WSSecEncrypt encrypt = new WSSecEncrypt();
        encrypt.setUserInfo(user, cryptoPwd);
        encrypt.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);

        WSSecHeader secHeader = new WSSecHeader();
        secHeader.insertSecurityHeader(doc);

        List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
        WSEncryptionPart encP = new WSEncryptionPart(soapConstants
                .getBodyQName().getLocalPart(), // define the body
                soapConstants.getEnvelopeURI(), "");
        parts.add(encP);
        encrypt.setParts(parts);
        Document encryptedDoc = encrypt.build(doc, crypto, secHeader);
        return encryptedDoc;
    }
}

ColdFusion Error:

Signature creation failed (Cannot setup signature data structure)  


The error occurred in G:/Inetpub/wwwroot/SOAP/index.cfm: line 14

12 : writeDump(var="#variables.fileContent#", format="html", output="#expandPath('./')#_DUMP-OUTPUT.htm");
13 : 
14 :    variables.encSOAP=createobject("java","ProcessIDSRSSOAP").runProcess(fileContent);
15 : 
16 : writeOutput("<br><br>encSOAP: <br>");

Stack Trace:

at cfindex2ecfm1134068877.runPage(G:/Inetpub/wwwroot/SOAP/index.cfm:14) 


org.apache.ws.security.WSSecurityException: Signature creation failed (Cannot setup signature data structure)
    at org.apache.ws.security.message.WSSecSignatureBase.addReferencesToSign(WSSecSignatureBase.java:191)
    at org.apache.ws.security.message.WSSecSignature.addReferencesToSign(WSSecSignature.java:409)
    at org.apache.ws.security.message.WSSecSignature.build(WSSecSignature.java:381)
    at ProcessIDSRSSOAP.signBody(ProcessIDSRSSOAP.java:118)
    at ProcessIDSRSSOAP.processDoc(ProcessIDSRSSOAP.java:85)
    at ProcessIDSRSSOAP.runProcess(ProcessIDSRSSOAP.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at coldfusion.runtime.java.JavaProxy.invoke(JavaProxy.java:97)
    at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2360)
    at cfindex2ecfm1134068877.runPage(G:\Inetpub\wwwroot\SOAP\index.cfm:14)
    at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231)
    at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416)
    at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
    at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:381)
    at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48)
    at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
    at coldfusion.filter.PathFilter.invoke(PathFilter.java:94)
    at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70)
    at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:79)
    at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
    at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
    at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46)
    at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
    at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
    at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62)
    at coldfusion.CfmServlet.service(CfmServlet.java:200)
    at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
    at jrun.servlet.FilterChain.doFilter(FilterChain.java:86)
    at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
    at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
    at jrun.servlet.FilterChain.doFilter(FilterChain.java:94)
    at jrun.servlet.FilterChain.service(FilterChain.java:101)
    at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106)
    at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42)
    at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286)
    at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543)
    at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203)
    at jrunx.scheduler.ThreadPool$DownstreamMetrics.invokeRunnable(ThreadPool.java:320)
    at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428)
    at jrunx.scheduler.ThreadPool$UpstreamMetrics.invokeRunnable(ThreadPool.java:266)
    at jrunx.scheduler.WorkerThread.run(WorkerThread.java:66)
Caused by: java.lang.NullPointerException
    at org.apache.ws.security.message.DOMCallbackLookup.getElements(DOMCallbackLookup.java:94)
    at org.apache.ws.security.util.WSSecurityUtil.findElements(WSSecurityUtil.java:267)
    at org.apache.ws.security.message.WSSecSignatureBase.addReferencesToSign(WSSecSignatureBase.java:156)
    ... 43 more

Obviously, something is missing from org.apache.ws.security.message.DOMCallbackLookup.getElements. The code feeding it is:

return callbackLookup.getElements(part.getName(),part.getNamespace());

I can't seem to figure out why it works the first time when called from CF, but fails with this error on subsequent runs.


回答1:


The actual issue was that I had my function calls reversed. I reversed the order of the block

signedDoc = signBody(tsDoc);// Send the Document on for signing. encryptedDoc = encryptBody(signedDoc);

to

encryptedDoc = encryptBody(signedDoc); signedDoc = signBody(tsDoc);// Send the Document on for signing.

and it worked.




回答2:


You would need to add a handler, have a look here as I believe this is similar to what you are trying to achieve: http://milanchandnacf.blogspot.co.uk/2011/09/adding-handler-to-coldfusion-web.html



来源:https://stackoverflow.com/questions/11611866/how-to-digitally-sign-a-soap-request-body-in-coldfusion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!