WSSecurityEngine says password was not supplied during callback even though it is provided

11,301

After many days of trying very many different things, I finally figured out the answer to my problem. Talk about the problem being in between the keyboard and the chair!!!!!

So without further ado here is what the issue was:

securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);

Now WSConstants.PASSWORD_TEXT actually equates to "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText". What I must have really been using is WSConstants.PW_TEXT which equates to "PasswordText" which is exactly what I needed in my code. Once I made the change, everything works beautifully.

Contrary to popular belief, you can do all this entirely in a programmatic manner. You DO NOT need to configure the WSDD xml for you to intercept a message and handle WS-Security in the SOAP header. To tidy up loose ends, here is what the modified methods look like:

public EngineConfiguration createConfigurationWithSecurityHeaders(Merchant merchant) throws Exception {
    SimpleProvider result;

    Handler securityHandler;
    SimpleChain requestHandler;
    SimpleChain responseHandler;
    Handler pivot;
    Handler transport;

    try {
        result = new SimpleProvider();

        securityHandler = new WSDoAllSender();
        securityHandler.setOption(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
        securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
        securityHandler.setOption(WSHandlerConstants.USER, merchant.toString());
        securityHandler.setOption(WSHandlerConstants.MUST_UNDERSTAND, "false");

        requestHandler = new SimpleChain();
        requestHandler.addHandler(securityHandler);

        responseHandler = new SimpleChain();
        responseHandler.addHandler(securityHandler);

        pivot = new HTTPSender();

        transport = new SimpleTargetedChain(requestHandler, pivot, responseHandler);

        result.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, transport);
    }
    catch (Exception e) {
        throw e;
    }

    return result;
}

public ReplyMessage post (Merchant merchant, RequestMessage request) throws Exception {
    ReplyMessage result;

    TransactionProcessorLocator locator;
    URL endPoint;
    ITransactionProcessorStub stub;
    EngineConfiguration configuration;

    try {
        locator = new TransactionProcessorLocator();

        // use client config
        configuration = createConfigurationWithSecurityHeaders(merchant);
        locator.setEngineConfiguration(configuration);
        locator.setEngine(new org.apache.axis.client.AxisClient(configuration));

        endPoint = new URL(environment.getUrl());

        stub = (ITransactionProcessorStub) locator.getportXML(endPoint);
        stub._setProperty(WSHandlerConstants.PW_CALLBACK_REF, this);

        result = stub.runTransaction(request);
    }
    catch (Exception e) {
        throw e;
    }

    return result;
}

Once these modifications are made, your client will work. Please keep in mind that a few of the settings above are specific to the service I'm integrating with. You might have to tweak these to suit your integration which might require a little trial and error.

Thanks again to all the people who post incredibly knowledgeable articles in SO enabling users like me solve problems we come across every now and then.

Share:
11,301
Karthic Raghupathi
Author by

Karthic Raghupathi

writer . lifelong-student . tippler . foodie . movie-buff . biker . runner . motorbiker

Updated on June 04, 2022

Comments

  • Karthic Raghupathi
    Karthic Raghupathi almost 2 years

    I'm in the process of creating a client for a web service. I keep getting the following error:

    AxisFault
        faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity
        faultSubcode:
        faultString:
            Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
        faultActor: 
        faultNode: 
        faultDetail: {http://xml.apache.org/axis/}
        stackTrace:
            Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    

    This is my environment here:

    • Intellij IDEA IDE
    • Apache Axis
    • Apache WSS4J 1.5.1
    • Apache XML Security 1.4.0
    • JDK 1.6
    • Max OS X

    Although searching on the internet provides a lot of examples of how the security header can be added to the request via XML configuration, my requirement is to do this dynamically via the program. So here is my code:

    public class AxisClient implements CallbackHandler {
    
        ServerEnvironment environment;
    
        AxisClient(ServerEnvironment environment) {
            this.environment = environment;
        }
    
        public enum ServerEnvironment {
            LIVE("https://ics2ws.ic3.com/commerce/1.x/transactionProcessor"),
            TEST("https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor");
    
            String url;
    
            ServerEnvironment (String url) {
                this.url = url;
            }
    
            public String getUrl() {
                return url;
            }
        }
    
        public enum Merchant {
            TestMerchant ("testpassword");
    
            private String transactionKey;
    
            Merchant(String transactionKey) {
                this.transactionKey = transactionKey;
            }
    
            public String getTransactionKey() {
                return transactionKey;
            }
        }
    
        public static void main(String[] argv) {
            String ani = "7162502800";
            String zipCode = "14221";
            String ccNum ="5555555555554444";
            String expMonth = "01";
            String expYear = "15";
            String cvv = "123";
            String unitPrice = "9.99";
            String qty = "2";
    
            try {
                new AxisClient(ServerEnvironment.TEST).doAuth(Merchant.TestMerchant, ani, zipCode, ccNum, expMonth, expYear, cvv, String.valueOf(new Date().getTime()), unitPrice, qty);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public Boolean doAuth(Merchant merchant, String ani, String zipCode, String ccNum, String expMonth, String expYear, String cvv, String id, String unitPrice, String qty) throws Exception {
            Boolean result = false;
    
            RequestMessage request;
            BillTo billTo;
            Card card;
            PurchaseTotals purchaseTotals;
            Item item;
            Item[] items;
            ReplyMessage reply;
    
            try {
                // billing info
                billTo = new BillTo();
                billTo.setPhoneNumber(ani);
                billTo.setPostalCode(zipCode);
    
                // card info
                card = new Card();
                card.setAccountNumber(ccNum);
                card.setExpirationMonth(new BigInteger(expMonth));
                card.setExpirationYear(new BigInteger(expYear));
                card.setCvNumber(cvv);
    
                // currency info
                purchaseTotals = new PurchaseTotals();
                purchaseTotals.setCurrency("USD");
    
                // item
                item = new Item();
                item.setId(new BigInteger(id));
                item.setUnitPrice(unitPrice);
                item.setQuantity(new BigInteger(qty));
    
                // add item to items array
                items = new Item[1];
                items[0] = item;
    
                // create our request
                request = new RequestMessage();
                request.setMerchantID(merchant.toString());
                request.setCcAuthService(new CCAuthService());
                request.getCcAuthService().setRun("true");
    
                // add request specific params
                request.setBillTo(billTo);
                request.setCard(card);
                request.setPurchaseTotals(purchaseTotals);
                request.setItem(items);
    
                reply = post(merchant, request);
    
                if (reply != null) {
                    System.out.println(ReflectionToStringBuilder.toString(reply, ToStringStyle.MULTI_LINE_STYLE));
                }
            }
            catch (Exception e) {
                throw e;
            }
    
            return result;
        }
    
        public EngineConfiguration createConfigurationWithSecurityHeaders(Merchant merchant) throws Exception {
            SimpleProvider result;
    
            Handler securityHandler;
            SimpleChain requestHandler;
            SimpleChain responseHandler;
            Handler pivot;
            Handler transport;
    
            try {
                result = new SimpleProvider();
    
                securityHandler = new WSDoAllSender();
                securityHandler.setOption(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
                securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
                securityHandler.setOption(WSHandlerConstants.PW_CALLBACK_REF, this);
                securityHandler.setOption(WSHandlerConstants.USER, merchant.toString());
                securityHandler.setOption(WSHandlerConstants.MUST_UNDERSTAND, "false");
    
                requestHandler = new SimpleChain();
                requestHandler.addHandler(securityHandler);
    
                responseHandler = new SimpleChain();
                responseHandler.addHandler(securityHandler);
    
                pivot = new HTTPSender();
    
                transport = new SimpleTargetedChain(requestHandler, pivot, responseHandler);
    
                result.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, transport);
            }
            catch (Exception e) {
                throw e;
            }
    
            return result;
        }
    
        public ReplyMessage post (Merchant merchant, RequestMessage request) throws Exception {
            ReplyMessage result;
    
            TransactionProcessorLocator locator;
            URL endPoint;
            ITransactionProcessorStub stub;
            EngineConfiguration configuration;
    
            try {
                locator = new TransactionProcessorLocator();
    
                // use client config
                configuration = createConfigurationWithSecurityHeaders(merchant);
                locator.setEngineConfiguration(configuration);
                locator.setEngine(new org.apache.axis.client.AxisClient(configuration));
    
                endPoint = new URL(environment.getUrl());
    
                stub = (ITransactionProcessorStub) locator.getportXML(endPoint);
                stub._setProperty(WSHandlerConstants.USER, request.getMerchantID());
                stub._setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
                stub._setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
                stub._setProperty(WSHandlerConstants.PW_CALLBACK_REF, this);
                stub._setProperty(WSHandlerConstants.MUST_UNDERSTAND, "false");
    
                result = stub.runTransaction(request);
            }
            catch (Exception e) {
                throw e;
            }
    
            return result;
        }
    
        @Override
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
    
            for (Callback callback : callbacks) {
    
                System.out.println(ReflectionToStringBuilder.toString(callback, ToStringStyle.MULTI_LINE_STYLE));
    
                if (callback instanceof WSPasswordCallback) {
    
                    WSPasswordCallback passwordCallback = (WSPasswordCallback) callback;
    
                    switch (Merchant.valueOf(passwordCallback.getIdentifer())) {
    
                        case TestMerchant:
                            passwordCallback.setPassword(Merchant.TestMerchant.getTransactionKey());
                            System.out.println(ReflectionToStringBuilder.toString(passwordCallback, ToStringStyle.MULTI_LINE_STYLE));
                            break;
    
                        default:
                            throw new UnsupportedCallbackException(callback, "Unrecognized prompt!");
                    }
                }
                else {
                    throw new UnsupportedCallbackException(callback, "Unrecognized callback!");
                }
            }
        }
    }
    

    As you can see from the above, my class implements CallbackHandler and I'm overriding handle() which provides the password for WSPasswordCallback.

    Here is the output of the print statement for when I get the callback first:

    org.apache.ws.security.WSPasswordCallback@50c713d2[
      identifier=TestMerchant
      password=<null>
      key=<null>
      usage=2
      passwordType=<null>
    ]
    

    Here is the output for after setting the password:

    org.apache.ws.security.WSPasswordCallback@50c713d2[
      identifier=TestMerchant
      password=testpassword
      key=<null>
      usage=2
      passwordType=<null>
    ]
    

    So I'm not really sure why I keep getting that error message. Any help in solving this issue will be greatly appreciated.

    Also recommendations for a different approach (axis2, cxf) are welcome.

    Here is my full stack trace if it is of any help:

    AxisFault
     faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity
     faultSubcode: 
     faultString: 
    Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    
     faultActor: 
     faultNode: 
     faultDetail: 
        {http://xml.apache.org/axis/}stackTrace:
    Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    
        at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:222)
        at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:129)
        at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2939)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
        at javax.xml.parsers.SAXParser.parse(SAXParser.java:395)
        at org.apache.axis.encoding.DeserializationContext.parse(DeserializationContext.java:227)
        at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:696)
        at org.apache.axis.Message.getSOAPEnvelope(Message.java:435)
        at org.apache.axis.handlers.soap.MustUnderstandChecker.invoke(MustUnderstandChecker.java:62)
        at org.apache.axis.client.AxisClient.invoke(AxisClient.java:206)
        at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
        at org.apache.axis.client.Call.invoke(Call.java:2767)
        at org.apache.axis.client.Call.invoke(Call.java:2443)
        at org.apache.axis.client.Call.invoke(Call.java:2366)
        at org.apache.axis.client.Call.invoke(Call.java:1812)
        at itg.cybersource.axis.ITransactionProcessorStub.runTransaction(ITransactionProcessorStub.java:1284)
        at itg.AxisClient.post(AxisClient.java:208)
        at itg.AxisClient.doAuth(AxisClient.java:132)
        at itg.AxisClient.main(AxisClient.java:75)
    
        {http://xml.apache.org/axis/}hostname:C02GD302DRJL.local
    
    
    Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
    
        at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:222)
        at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:129)
        at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2939)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
        at javax.xml.parsers.SAXParser.parse(SAXParser.java:395)
        at org.apache.axis.encoding.DeserializationContext.parse(DeserializationContext.java:227)
        at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:696)
        at org.apache.axis.Message.getSOAPEnvelope(Message.java:435)
        at org.apache.axis.handlers.soap.MustUnderstandChecker.invoke(MustUnderstandChecker.java:62)
        at org.apache.axis.client.AxisClient.invoke(AxisClient.java:206)
        at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
        at org.apache.axis.client.Call.invoke(Call.java:2767)
        at org.apache.axis.client.Call.invoke(Call.java:2443)
        at org.apache.axis.client.Call.invoke(Call.java:2366)
        at org.apache.axis.client.Call.invoke(Call.java:1812)
        at itg.cybersource.axis.ITransactionProcessorStub.runTransaction(ITransactionProcessorStub.java:1284)
        at itg.AxisClient.post(AxisClient.java:208)
        at itg.AxisClient.doAuth(AxisClient.java:132)
        at itg.AxisClient.main(AxisClient.java:75)