are these cxf/ws-security properties set correctly? (getting TPE1122 error, "WS Security Header invalid")

934 Views Asked by At

We're trying to send a signed, partially encrypted SOAP message to the IRS using CXF. We think we're following all their instructions but there are ambiguities. Some of the Signature and Encryption properties could be set in several ways, but none of the permutations I've tried get us past the dreaded "TPE1122" error (WS Security Header invalid). If anyone has done this successfully, is there some property we're failing to set? I'm especially unsure about the encryption algorithm setting and whether the whole element should be encrypted or just the 3 header elements within it. Thanks.

    BulkRequestTransmitterService ss = new BulkRequestTransmitterService(wsdlURL, SERVICE_NAME);
    BulkRequestTransmitterPortType port = ss.getBulkRequestTransmitterPort();

    org.apache.cxf.endpoint.Client client = ClientProxy.getClient(port);

    // set up MTOM
    Binding binding = ((BindingProvider)port).getBinding();
    ((SOAPBinding)binding).setMTOMEnabled(true);

    // set output properties
    Map<String, Object> outProps = new HashMap<String, Object>();
    outProps.put("action", "Timestamp Signature Encrypt");
    outProps.put("passwordType", "PasswordDigest");
    outProps.put("signatureUser", "[REDACTED]";
    outProps.put(WSHandlerConstants.SIG_KEY_ID, "X509KeyIdentifier"); 
    outProps.put("passwordCallbackClass", "UTPasswordCallback"); 
    outProps.put("encryptionUser", "irs"); 
    outProps.put("encryptionPropFile", "encryption.properties");
    outProps.put("encryptionKeyIdentifier", "DirectReference"); 
    outProps.put("encryptionKeyTransportAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");

    // ENC_SYM_ALGO: what is the default?  what should correct value be?  and are these two lines equivalent?
    outProps.put(WSHandlerConstants.ENC_SYM_ALGO, WSConstants.TRIPLE_DES);  
    outProps.put("encryptionSymAlgorithm", "http://www.w3.org/2001/04/xmlenc#tripledes-cbc");

    // do we encrypt each of the three signed headers, or entire Signature element?  have tried it both ways
    outProps.put("encryptionParts",
        //"{Element}{" + WSU_NS + "}Timestamp;"
        //+"{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACATransmitterManifestReqDtl;"
        //+"{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACABusinessHeader;");
        "{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;");

    outProps.put("signaturePropFile", "signature.properties");
    outProps.put("signatureAlgorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
    outProps.put("signatureParts",
            "{Element}{" + WSU_NS + "}Timestamp;"
            + "{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACATransmitterManifestReqDtl;"
            + "{Element}{urn:us:gov:treasury:irs:ext:aca:air:7.0}ACABusinessHeader;");  
    outProps.put(WSHandlerConstants.SIG_C14N_ALGO, "http://www.w3.org/2001/10/xml-exc-c14n#WithComments");

    // is "Direct Reference" preferable?  have tried it both ways
    outProps.put(WSHandlerConstants.ENC_KEY_ID, "X509KeyIdentifier"); 

    outProps.put("timeToLive", "600");  // = 10 min
    outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "false");

    WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
    client.getOutInterceptors().add(wssOut); 

    // add gzip interceptor  
    GZIPOutInterceptor gz = new GZIPOutInterceptor();
    gz.setForce(true); 
    client.getOutInterceptors().add(gz);

    [ create & populate Manifest Detail here]

    Header detailHeader = new Header(new QName("urn:us:gov:treasury:irs:ext:aca:air:7.0", "ACATransmitterManifestReqDtl"), aCATrnsmtManifestReqDtlType,
            new JAXBDataBinding(ACATrnsmtManifestReqDtlType.class));
    headers.add(detailHeader);  

    Header businessHeader = new Header(new QName("urn:us:gov:treasury:irs:ext:aca:air:7.0", "ACABusinessHeader"), aCABulkBusinessHeaderRequestType,
                                    new JAXBDataBinding(ACABulkBusinessHeaderRequestType.class));
    headers.add(businessHeader);

    // add headers to Request
    client.getRequestContext().put(Header.HEADER_LIST, headers); 

    // add namespaces:
    Map<String, String> nsMap = new HashMap<>();
    nsMap.put("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");  
    nsMap.put("urn", "urn:us:gov:treasury:irs:ext:aca:air:7.0");
    nsMap.put("urn1", "urn:us:gov:treasury:irs:common");        
    nsMap.put("urn2", "urn:us:gov:treasury:irs:msg:acabusinessheader");
    nsMap.put("urn3", "urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter");
    nsMap.put("urn4", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
    client.getRequestContext().put("soap.env.ns.map", nsMap); 

    // then transmit, get TPE1122 error in Response
2

There are 2 best solutions below

0
On

Thanks to both responders for your feedback. We have it working now and the main problem was that we were not supposed to use encryption. Everything in the sample I posted worked (including using CXF to sign certain parts of the header), but the encryption had to be removed.

It is indeed quite difficult to get everything right when transmitting to the IRS AIR system. The next hurdle was solved by changing a namespace in one of the wsdl-generated files, and then it went through, but their Linux system's value for the file size was 2 chars smaller than our Windows system's value - the culprit was the trailing CRLF.

I would be happy to answer specific questions if anyone is trying to do this as we did, using Java, Apache-CXF, and Windows.

0
On

Bulit in CXF security will not work in IRS submission. You need to code your interceptors for matching IRS requirements.

I have a sample project ready, which can do submission and status requests. This is by no means a production code, but can be a starting point

https://github.com/sangramjadhav/irsclient

Please see application.yml file. You need to provide keystore and other configuration for submission to IRS