The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Client Side Certificate Handling in Secure Metro WebServices

Posted by kumarjayanti on November 24, 2008 at 5:42 AM PST

If you have a WebService configured to use the Mutual Certificates Security  mechanism as supported by Netbeans.  Then when developing a client for the service you would generally be required to configure the client side keystore alias or provide a callbackhandler. And since it is a mutual certificate scenario you would also have to configure an alias from the truststore or a callbackhandler to identify the server certificate on the client side.  Sometimes it may not be practical to expect the  Certificates to be present inside a  Java Keystore.   The application invoking the WebService Proxy might obtain it from other sources (not necessarily a Java Keystore).  With the latest metro builds starting 25th November  you should be able to set the information as follows :

 import com.sun.xml.wss.XWSSConstants;


....
try { // Call Web Service Operation
test.MyWebService port = service.getMyWebServicePort();
java.lang.String parameter = " This is a Secure Test";

//You may have them available from various sources (including from some URL)
//For the purpose of this sample i would read them from files.
CertificateFactory fact = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) fact.generateCertificate(new FileInputStream("D:\\openesb\\client-cert.der"));
PrivateKey key = readPrivateKey("D:\\openesb\\client-key.der");
X509Certificate serverCert = (X509Certificate) fact.generateCertificate(new FileInputStream("D:\\openesb\\server-cert.der"));

((BindingProvider) port).getRequestContext().put(XWSSConstants.CERTIFICATE_PROPERTY, cert);
((BindingProvider) port).getRequestContext().put(XWSSConstants.PRIVATEKEY_PROPERTY, key);
((BindingProvider) port).getRequestContext().put(XWSSConstants.SERVER_CERTIFICATE_PROPERTY, serverCert);
               
java.lang.String result = port.operation(parameter);
out.println("Result = " + result);
} catch (Exception ex) {
// TODO handle custom exceptions here
}

           

As you can see i am reading the certificates and private-key from DER encoded files.  Here is the non-trivial readPrivateKey() method that is used above :

/**
 * Private key should be in "DER" format.
 */
public static PrivateKey readPrivateKey(String fileLocation)
throws Exception {

FileInputStream fis = new FileInputStream(fileLocation);
byte input[] = new byte[fis.available()];
fis.read(input, 0, input.length);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(input);
KeyFactory key_fac = KeyFactory.getInstance("RSA");
return key_fac.generatePrivate(spec);
}


          
So as you can see we have introduced three properties CERTIFICATE_PROPERTY, PRIVATEKEY_PROPERTY and  SERVER_CERTIFICATE_PROPERTY.  These properties are akin to BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY supported in JAXWS specification for Basic Authentication.  They serve to supply the  Client Certificate,  Client PrivateKey and Server Certifcate respectively.  Note that for programmatically specifying username and password for secure Metro scenarios that involve username, we already advocate the use of  XWSSConstants.USERNAME_PROPERTY and  XWSSConstants.PASSWORD_PROPERTY.

The metro client side configuration file wsit-client.xml does not need to have any Keystore/Truststore configuration in this case. Infact there is no need for a wsit-client.xml (client side configuration) in this case and if you are using NetBeans it will not generate one unless you try to specify any of the Keystore information.

Now you can also get rid of the wsit-client.xml for the first NetBeans secure mechanims "Username Authentication with Symmetric Keys" by specifying the following three properties. 

1.  USERNAME_PROPERTY
2.  PASSWORD_PROPERTY
3. SERVER_CERTIFICATE_PROPERTY

try { // Call Web Service Operation
test.MyWebService port = service.getMyWebServicePort();
java.lang.String parameter = " This is a Secure Test";
               
CertificateFactory fact = CertificateFactory.getInstance("X509");
X509Certificate serverCert = (X509Certificate) fact.generateCertificate(new FileInputStream("D:\\openesb\\server-cert.der"));

((BindingProvider) port).getRequestContext().put(XWSSConstants.USERNAME_PROPERTY, "test");
((BindingProvider) port).getRequestContext().put(XWSSConstants.PASSWORD_PROPERTY, "test");
((BindingProvider) port).getRequestContext().put(XWSSConstants.SERVER_CERTIFICATE_PROPERTY, serverCert);
               
java.lang.String result = port.operation(parameter);
out.println("Result = " + result);
} catch (Exception ex) {
// TODO handle custom exceptions here
}


The  SERVER_CERTIFICATE_PROPERTY is required when using this mechanism because it is used to secure (encrypt) a Random SecretKey which is then used to Sign and Encrypt the Request Message from the Client. 

With all the complexity of WS-* specifications sometimes we get a feeling that it has become a little hard to do some very simple secure scenarios.  For example someone just wants to send a simple username and password (no signature or encryption). Someone just wants to sign the message payload, or just encrypt the message payload.  Unfortunately you have to hand edit the  WS-SecurityPolicy assertions in the WSDL (OR Metro wsit-*.xml, configuration file) today.  Nebeans defines a predefined set of  Security Mechanisms which we believe would cover the most common usecases. But we have always found people wanting to do other things.  One approach is to pick the closest Netbeans Mechanism and generate the initial  Metro configuration file and then manipulate the  Policy assertions to suite your needs.  In the coming weeks i will try to post  how one can do some of the very simple Username/Password, Password Digest, Sign-Only etc. scenarios with Metro. I shall post the policies of the service for your use.   I might use the  properties described in this blog for simplicity in configuring the client.




Comments
Comments are listed in date ascending order (oldest first)

Sometimes using the certificate with the private key requires a password. Can it be hard-coded similarly to the wss password in your sample? http://webservices20.blogspot.com/ Web Services Security, Interoperability And Testing Blog

Fantastic! In this way is much easier than before! Thanks Kumar! ;)

How can I set more than one credential for every SupportingToken defined in the security policies for the web service?

Good question!!. We do not support a good way of doing this today. We have been thinking of some possible solution for this. If it is important for you please file a bug so we give it more priority.

hi dloiacono, I would like to restate my answer to your question. If you are using WS-SX version of WS-SecurityPolicy which is supported by NetBeans 6.5 and then you make sure your supporting tokens have the Claims assertions as part of them the there is a way to achieve what you are looking for, We have conciously made the Claims MetaData available to the Callbackhandlers. So you can decide which certificate to use. I can show a sample on how to do this but it will take me some time because i am busy with something else. Let me know if you need this urgently.

Hi Kumar, I'm interested at this solution. I'm using WS-SX and NetBeans 6.5 so I can define in my WSDL a supporting token policy like this: <sp:SignedEndorsingSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wst:Claims Dialect="urn:myDialect">AddictionalX509Claim</wst:Claims> <wsp:Policy> <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient" wsp:Optional="true"> <wsp:Policy> <sp:RequireKeyIdentifierReference/> <sp:WssX509V3Token10/> </wsp:Policy> </sp:X509Token> </wsp:Policy> </sp:SignedEndorsingSupportingTokens> And in my Callback handler I can use: CallbackHandler cb = ... Map props = cb.getRuntimeProperties(); com.sun.xml.wss.TokenPolicyMetaData metaData = new com.sun.xml.wss.TokenPolicyMetaData(props); org.w3c.dom.Element claims = metaData.getClaims(); and then verify if Claim contains "AddictionalX509Claim".

Yes this is what i was referring to. Please try it out and let us know if it worked.

Ok, I'm trying to do it, but I have a question:I have to specify a particular callback handler ? If in WSIT client configuration file I define: <sc:KeyStore wspp:visibility="private" aliasSelector="wsit.security.AliasSelector" callbackHandler="wsit.security.KeyStoreCallbackHandler"/> I notice that metaData.getClaims(); in the KeyStoreCallbackHandler return a null element.

That sounds like a bug.. I will try and fix it soon (this week) then. That's why i wanted to try it out before writing about it. But what you are trying is correct the keystorecallback should have had it. However can you check if the select() method of AliasSelector has the claims info.

You could file an issue.

I'm trying to do the client side stuff as you suggested with the XWSSConstants, but run into the problem, that when I'm requesting the ws port from the service, I get this exception: Dec 19, 2008 4:04:16 PM com.sun.xml.wss.impl.misc.DefaultCallbackHandler getTrustStore SEVERE: Could not locate TrustStore, check truststore assertion in WSIT configuration Dec 19, 2008 4:04:16 PM com.sun.xml.wss.impl.misc.DefaultSecurityEnvironmentImpl getCertificate SEVERE: WSS0216: An Error occurred using Callback Handler for : EncryptionKeyCallback.AliasX509CertificateRequest Dec 19, 2008 4:04:16 PM com.sun.xml.wss.impl.misc.DefaultSecurityEnvironmentImpl getCertificate SEVERE: WSS0217: An Error occurred using Callback Handler handle() Method. com.sun.xml.wss.impl.XWSSecurityRuntimeException: Could not locate TrustStore, check truststore assertion in WSIT configuration It seems that the system wants to setup the truststore when creating the port, and is not waiting for me to provide the certificate. Isn't there something I must set for this to work?

Are you using Metro 1.4. Let my try the whole scenario and get back to you in a day or two. I will let you know .

Hi gekkothelizard , You should be using the latest builds from : https://metro.dev.java.net/servlets/ProjectDocumentList?folderID=9700&ex... Use December 23rd nightly for example.

Hi dloiacono , I notice that you are not using the correct version of WS-SecurityPolicy which supports the Claims assertion. AddictionalX509Claim the security policy Namespace URI should be : xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" but you have given : xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"

Also the wst:Claim element should appear inside the X509Token Policy : /sp:X509Token/wst:Claims This optional element identifies the required claims that a security token must contain in order to satisfy the token assertion requirements. see : http://docs.oasis-open.org/ws-sx/ws-securitypolicy/v1.2/ws-securitypolic...

Hi Kumar, with "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" namespace URI it works fine. Thanks Domenico Loiacono

Hi Kumar! The CERTIFICATE_PROPERTY, PRIVATEKEY_PROPERTY, and SERVER_CERTIFICATE_PROPERTY properties don't work in Metro 1.5. Is it normal? Thanks!

yes, you would have to use Metro 2.0 for it. We have also added a new feature recently where the WSDL can publish the server certificate inside the EndpointReference as an Identity element. The client would then never need to configure the peer-alias for encryption since it would automatically get the server certificate. I will post more about it later.

Hi Kumar! It would be great! :-D I asked about this possiblility a few months ago: http://forums.java.net/jive/message.jspa?messageID=339230 Please, let me know when it is ready! ;-) Thanks! Ernesto.