How to create Password Digest for use with Web Service?

authentication, java, soap, web-services, webservice-client

Solution

WS-Security defines password digest as

Base64 ( SHA1 ( nonce + created + password ) )

not

Base64 ( SHA1 ( password + nonce + created) )

And nonce is supposed to be 128 bits (16 bytes) encoded as Base64. e.g.

java.security.SecureRandom random = java.security.SecureRandom.getInstance("SHA1PRNG");
random.setSeed(System.currentTimeMillis()); 
byte[] nonceBytes = new byte[16]; 
random.nextBytes(nonceBytes); 
String nonce = new String(org.apache.commons.codec.binary.Base64.encodeBase64(nonceBytes), "UTF-8");

Problem

I am trying to create a passwordDigest util which can be used in different environments which are capable of running java byte code. First of all I create nonce. It is done like so. ``` public static String buildNonce(){ StringBuffer nonce=new StringBuffer(); String dateTimeString = Long.toString(new Date().getTime()); byte[] nonceByte= dateTimeString.getBytes(); return Base64.encode(nonceByte); } ``` Once I have nonce, I build password digest. ``` public static String buildPasswordDigest(String userName, String password, String nonce, String dateTime){ MessageDigest sha1; String passwordDigest=null; try { sha1= MessageDigest.getInstance("SHA-1"); byte[] hash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8")); sha1.update(nonce.getBytes("UTF-8")); sha1.update(dateTime.getBytes("UTF-8")); passwordDigest = new String(Base64.encode(sha1.digest(hash))); sha1.reset(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return passwordDigest; ``` In order to test that everything works correctly. I have created a test web service using CXF 2.7. I have manually created SOAP Envelope to test authentication. The envelope looks like this. ``` <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.mytest.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance/"> <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-2"> <wsse:Username>TEST_USER</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">UZsDSW/vANu6fHg4rAHo2OwsF9s=</wsse:Password <wsse:Nonce>MTQwMTMwMDQzNjA3OA==</wsse:Nonce> <wsu:Created>2014-05-28T18:07:16.087Z</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soapenv:Header> <soapenv:Body> <ws:record> <val1>1</val1> <val2>Some Text</val2> </ws:record> </soapenv:Body> </soapenv:Envelope> ``` When I send the envelope using SOAP UI. I get the following authentication error. ``` WARNING: Interceptor for {http://ws.mytest.org/}TestService has thrown exception, unwinding now org.apache.cxf.binding.soap.SoapFault: The security token could not be authenticated or authorized at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:788) at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:336) at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:95) at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272) at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239) at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248) at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222) at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153) at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167) at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286) at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:206) at javax.servlet.http.HttpServlet.service(HttpServlet.java:646) at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744) Caused by: org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized at org.apache.ws.security.validate.UsernameTokenValidator.verifyDigestPassword(UsernameTokenValidator.java:199) at org.apache.ws.security.validate.UsernameTokenValidator.validate(UsernameTokenValidator.java:97) at org.apache.ws.security.processor.UsernameTokenProcessor.handleUsernameToken(UsernameTokenProcessor.java:172) at org.apache.ws.security.processor.UsernameTokenProcessor.handleToken(UsernameTokenProcessor.java:67) at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396) at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:279) ... 31 more ``` I suspect that I have an issue creating either nonce or password. Your help is appreciated.

Original source