Calling a soap webservice from java with nonce in security headers

13,064

The Oasis reference for the UsernameToken helped me fill in some of the blanks. Pages 7,8,9 are most appropriate in this case. In particular these sections

/wsse:UsernameToken/wsse:Nonce

This optional element specifies a cryptographically random nonce. Each message including a element MUST use a new nonce value in order for web service producers to detect replay attacks.

and

/wsse:UsernameToken/wsse:Nonce/@EncodingType

This optional attribute URI specifies the encoding type of the nonce (see the definition of <wsse:BinarySecurityToken> for valid values). If this attribute isn't specified then the default of Base64 encoding is used.

In regards to generating the 'cryptographically random' nonce, can suggest you use this answer and then create an encoded string from it. Base64 encoding in your case, as that is the encodingType you are using in your XML request above.

Share:
13,064
ErikL
Author by

ErikL

It guy, specialized in Databases (Specifically Oracle) and always on the lookout for new techniques.

Updated on June 09, 2022

Comments

  • ErikL
    ErikL almost 2 years

    I am trying to call a webservice from java. This is basically not that difficult, except that the webservice expects some security in the form of a username and password and a nonce. When I try to call the webservice from SoapUi, I see that the raw message looks like this:

    <soapenv:Envelope xmlns:sch="http://somedomain.com/pe/ws/schema"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Header>
            <wsse:Security soapenv:mustUnderstand="1"
                xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <wsse:UsernameToken wsu:Id="UsernameToken-E70691ACBDEFEC750814238295617871">
                    <wsse:Username>usr</wsse:Username>
                    <wsse:Password
                        Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
                        >pw</wsse:Password>
                    <wsse:Nonce
                        EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
                        >4smQZF5KMSktEXrQc0v5yw==</wsse:Nonce>
                    <wsu:Created>2015-02-13T12:12:41.784Z</wsu:Created>
                </wsse:UsernameToken>
            </wsse:Security>
        </soapenv:Header>
        <soapenv:Body>
            <sch:EventSubmitRequest>
                <sch:Event>
                    <sch:EventId>392</sch:EventId>
                    <sch:Recoverable>false</sch:Recoverable>
                </sch:Event>
            </sch:EventSubmitRequest>
        </soapenv:Body>
    </soapenv:Envelope>
    

    The obvious elements in the message are the Username, Password and Created, but what puzzles me is the nonce. In the example this field has the value 4smQZF5KMSktEXrQc0v5yw==, but this value difference upon each request (which makes sense, since according to wikipedia, a nonce is an arbitrary number used only once). When searching around, I can't find any usable examples of how to generate the nonce in java (Although I did find some php examples here on stack overflow, but I can't easily verify weather they work) . While I don't mind construction this nonce myself, I'm wondering if this is really necessary, I kind of would expect this to be standard functionality in java.

    Below is the code I'm using:

    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import javax.xml.namespace.QName;
    import javax.xml.soap.*;
    import javax.xml.transform.*;
    import javax.xml.transform.stream.*;
    
    public class soaptest {
    
        public static void main(String args[]) {
            try {
                // Create SOAP Connection
                SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
                SOAPConnection soapConnection = soapConnectionFactory.createConnection();
    
                // Send SOAP Message to SOAP Server
                String url = "http://142.10.10.52:8080/pe/ws/pe/";
                SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);
    
                // Process the SOAP Response
                printSOAPResponse(soapResponse);
    
                soapConnection.close();
            } catch (Exception e) {
                System.err.println("Error occurred while sending SOAP Request to Server");
                e.printStackTrace();
            }
        }
    
        private static SOAPMessage createSOAPRequest() throws Exception {
            MessageFactory messageFactory = MessageFactory.newInstance();
            SOAPMessage soapMessage = messageFactory.createMessage();
            SOAPPart soapPart = soapMessage.getSOAPPart();
    
            SOAPEnvelope envelope = soapPart.getEnvelope();
            SOAPHeader header = soapMessage.getSOAPHeader();
    
            SOAPElement security = header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
    
            SOAPElement usernameToken = security.addChildElement("UsernameToken", "wsse");
            usernameToken.addAttribute(new QName("xmlns:wsu"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
    
            SOAPElement username = usernameToken.addChildElement("Username", "wsse");
            username.addTextNode("usr");
    
            SOAPElement password = usernameToken.addChildElement("Password", "wsse");
            password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
            password.addTextNode("pw");
    
            SOAPElement nonce = usernameToken.addChildElement("Nonce", "wsse");
            nonce.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
            nonce.addTextNode("???");
    
            SOAPElement created = usernameToken.addChildElement("Created", "wsse");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            Calendar c1 = Calendar.getInstance();
            created.addTextNode(sdf.format(c1.getTime()));
    
            String serverURI = "http://somedomain.com/pe/ws/schema";
    
            envelope.addNamespaceDeclaration("sch", serverURI);
    
            // SOAP Body
            SOAPBody soapBody = envelope.getBody();
            SOAPElement soapBodyElem = soapBody.addChildElement("EventSubmitRequest", "sch");
            SOAPElement soapBodyElem1 = soapBody.addChildElement("Event", "sch");
            soapBodyElem.addChildElement(soapBodyElem1);
    
            SOAPElement soapBodyElem2 = soapBodyElem1.addChildElement("EventId", "sch");
            soapBodyElem2.addTextNode("392");
            SOAPElement soapBodyElem3 = soapBodyElem1.addChildElement("Recoverable", "sch");
            soapBodyElem3.addTextNode("false");
    
            MimeHeaders headers = soapMessage.getMimeHeaders();
            headers.addHeader("SOAPAction", serverURI  + "EventSubmitRequest");
    
            soapMessage.saveChanges();
    
            /* Print the request message */
            System.out.print("Request SOAP Message = ");
            soapMessage.writeTo(System.out);
            System.out.println();
    
            return soapMessage;
        }
    
        /**
         * Method used to print the SOAP Response
         */
        private static void printSOAPResponse(SOAPMessage soapResponse) throws Exception {
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            Source sourceContent = soapResponse.getSOAPPart().getContent();
            System.out.print("\nResponse SOAP Message = ");
            StreamResult result = new StreamResult(System.out);
            transformer.transform(sourceContent, result);
        }
    
    }