0

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
Russ
  • 678
  • 8
  • 26
  • If you are interfacing with the IRSs ACA services, you have to be VERY careful. You need to look at the schemas that they provided to you because what they did was override the well-known ws-security schemas. Many runtimes don't let you do that and you'll end up having to roll your own. You have to make darned sure that you have everything in exactly the right order to exactly match their re-written wssec schemas, which don't necessarily match what we're used to. Nothing more, nothing less. From everyone that I've worked with, it takes a LOT of work to get it right. – Barbara Jensen May 18 '16 at 14:26

2 Answers2

1

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

Sangram Jadhav
  • 2,438
  • 16
  • 17
0

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.