comments (not for humans)
Some of you may have seen my old posting where I presented some configuration for accessing a WSS4J secured Axis service from .NET using WSE3.0. I have gotten a lot of questions about how to make this work the other way around. This post contains a working configuration for a WSS4J secured client talking to a WSE3.0 secured web service using x509 certificates.

Update 2008-04-19: The config was updated to work better with with self-generated certificates and without a custom .NET assertion. So now I have a working configuration without any special implementations.

Basic setup
I once again used the interop certificates and keystores from the WSS4J 1.5.3 1.5 release package together with Microsoft WSE 3.0.
Update 2008-04-19: I was also able to make it work with my own certificates issued by the windows certificate authority when using WSS4J 1.5.3. I've added a new post about this here: WSE and WSS4J: Issuing working certificates using Windows Certification Authority.

Java WSS4J client configuration
The client-config.wsdd looks as follows. The crypto.properties file is the file from the WSS4J-package.
Update 2008-04-19: Moved to SKIKeyIdentifier for encryptionKeyIdentifier and disabled signatureconfirmation on responseflow.
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/>
<globalConfiguration>
<parameter name="enableNamespacePrefixOptimization" value="false" />
<requestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="action" value="Signature Encrypt Timestamp"/>
<parameter name="passwordCallbackClass" value="interop.PWCallback"/>
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="encryptionPropFile" value="crypto.properties" />
<parameter name="signatureKeyIdentifier" value="DirectReference" />
<!-- changed as of 2008-04-19 -->
<parameter name="encryptionKeyIdentifier" value="SKIKeyIdentifier" />
<parameter name="encryptionUser" value="bob" />
<parameter name="user" value="alice"/>
<parameter name="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<parameter name="encryptionKeyTransportAlgorithm" value="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
</handler>
</requestFlow>
<responseFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<parameter name="passwordCallbackClass" value="interop.PWCallback"/>
<parameter name="action" value="Signature Encrypt Timestamp"/>
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="decryptionPropFile" value="crypto.properties" />
<parameter name="encryptionPropFile" value="crypto.properties" />
<!-- added as of 2008-04-19 -->
<parameter name="enableSignatureConfirmation" value="false" />
</handler>
</responseFlow>
</globalConfiguration>
</deployment>


.NET side
The .NET side was a bit more difficult to set up. It seems WSS4J sends both the signature key and the encryption key in the security header, even though they may be the same key. WSE did not like this (WSE2007 error - as mentioned in the bottom section), so I had to implement a small change to the MutualCertificate10Assertion (yes still not using the MutualCertificate11Assertion ), where I removed the extra element before the assertion verified the XML. Usings are removed for brevity:
Update 2008-04-19: No longer needed
// no longer needed as of 2008-04-19The policy XML was changed to use this custom assertion instead.
Update 2008-04-19: I've reinstated the native MutualCertificate10Assertion:
<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
<extensions>
<extension name="authorization" type="Microsoft.Web.Services3.Design.AuthorizationAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<!-- CUSTOM -->
<extension name="mutualCertificate10Security" type="Microsoft.Web.Services3.Design.MutualCertificate10Assertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<extension name="x509" type="Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</extensions>
<policy name="x509">
<authorization>
<allow user="CN=Alice, OU=OASIS Interop Test Cert, O=OASIS" />
<deny user="*" />
</authorization>
<mutualCertificate10Security establishSecurityContext="false" renewExpiredSecurityContext="false" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="false" ttlInSeconds="300">
<serviceToken>
<x509 storeLocation="LocalMachine" storeName="My" findValue="CN=Bob, OU=OASIS Interop Test Cert, O=OASIS" findType="FindBySubjectDistinguishedName" />
</serviceToken>
<protection>
<request signatureOptions="IncludeSoapBody" encryptBody="true" />
<response signatureOptions="IncludeSoapBody" encryptBody="true" />
<fault signatureOptions="" encryptBody="false" />
</protection>
</mutualCertificate10Security>
</policy>
</policies>

For web.config the Microsoft.Web.Services3 element looked like this:
<microsoft.web.services3>
<security>
<binarySecurityTokenManager>
<add valueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
<keyAlgorithm name="RSA15"/>
</add>
</binarySecurityTokenManager>
<x509 skiMode="ThumbprintSHA1" verifyTrust="false" storeLocation="LocalMachine"/>
<securityTokenManager>
<add type="Microsoft.Web.Services3.Security.Tokens.EncryptedKeyTokenManager, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="http://www.w3.org/2001/04/xmlenc#" localName="EncryptedKey">
<keyAlgorithm name="AES128"/>
</add>
</securityTokenManager>
</security>
<policy fileName="wse3policyCache.config"/>
<diagnostics>
<trace enabled="true" input="D:\projects\websites\SecuredServiceInteropTest\trace.in" output="D:\projects\websites\SecuredServiceInteropTest\trace.out"/>
</diagnostics>
</microsoft.web.services3>

Now if anybody has a working configuration for Axis2 with Rampart, please let me know.
Update 2007-11-21: Shelly Saunders sent me a link to a blog post with a configuration for WCF and Rampart/Axis2: Shelly's blog post

Error messages I got during development
"Signature verification failed" - the solution to this error was to set the enableNamespacePrefixOptimization to false in the client-config.wsdd. XML-optimizations and XML-signatures don't work well together.
"WSE2007: More than one X509SecurityToken is present in the security header of the incoming message, but only one was expected" - the solution to this was to use the SKIKeyIdentifier for the server certificate implement the MutualCertificate10Assertion subclass.
Brook H

Try SKIKeyIdentifier

If you want to avoid having to create the custom assertion, you can get around the "WSE2007: More than one X509SecurityToken is present in the security header of the incoming message, but only one was expected":

In the axis client config file, change *one* or *both* of the signatureKeyIdentifier/encryptionKeyIdentifier from "DirectReference" to "SKIKeyIdentifier" in WSDoAllSender.

The drawback of this approach is that SKIKeyIdentifier does not include the certificate in the SOAP, it creates a reference. You'll need to have the public certificates installed on the server (.Net/WSE) for this to work.
Erlend

Re: Try SKIKeyIdentifier

Thanks. I'll test it out, and see how that works.
Shelly Saunders

Axis2/Rampart client with WCF service

Thanks for the article Erland. It helped me no end. I've now got a working config for an Axis2/Rampart client with a Windows Communication Foundation (WCF) service using Mutual X509 Certificates:
http://www.shellysaunders.co.uk/Blog/tabid/65/EntryID/2/Default.aspx
Erlend

Re:Axis2/Rampart client with WCF service

Nice work, Shelly. I'll add a link to your config.
Erlend

Updated version

I've just updated the blogpost after testing some of Brook H.'s stuff. And it works nicely! Thanks Brook!
Alok

Both client and server in axis

This is good but i want both client and server to be written in axis and securing it with wss4j
Ravi

Implementing Message Layer Security with X.509 Certificates in WSE 3.0

Hello,

I am Implementing Message Layer Security with X.509 Certificates in WSE 3.0. Services are developed in .net 2.0 and interface is java platform. I successfully implemented for .net client and .net server and it is working fine. so, we hosted the services and gave to url and certificated(.pfx files) to the java team. Now what the problem they are facing is, they need user name and password to proceed further but we didn\'t set the user name and password in your configuration and not even password is set for the .pfx files. please find the policy file below and give me suggestion. Thanks in advance.

Ravi.
<policies xmlns=\"http://schemas.microsoft.com/wse/2005/06/policy\">
<extensions>
<extension name=\"authorization\" type=\"Microsoft.Web.Services3.Design.AuthorizationAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" />
<extension name=\"mutualCertificate11Security\" type=\"Microsoft.Web.Services3.Design.MutualCertificate11Assertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" />
<extension name=\"x509\" type=\"Microsoft.Web.Services3.Design.X509TokenProvider, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" />
<extension name=\"requireActionHeader\" type=\"Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" />
</extensions>
<policy name=\"ServerCert\">
<mutualCertificate11Security establishSecurityContext=\"true\" renewExpiredSecurityContext=\"true\" requireSignatureConfirmation=\"true\" messageProtectionOrder=\"SignBeforeEncrypt\" requireDerivedKeys=\"true\" ttlInSeconds=\"86400\">
<serviceToken>
<x509 storeLocation=\"LocalMachine\" storeName=\"My\" findValue=\"CN=ABCServer\" findType=\"FindBySubjectDistinguishedName\" />
</serviceToken>
<protection>
<request signatureOptions=\"IncludeAddressing, IncludeTimestamp, IncludeSoapBody\" encryptBody=\"true\" />
<response signatureOptions=\"IncludeAddressing, IncludeTimestamp, IncludeSoapBody\" encryptBody=\"true\" />
<fault signatureOptions=\"IncludeAddressing, IncludeTimestamp, IncludeSoapBody\" encryptBody=\"false\" />
</protection>
</mutualCertificate11Security>
<requireActionHeader />
</policy>
</policies>
Minal

requireactionheader assertion

I have a JAVA client comsuming a .NET WSE3.0 enabled webservice. The webservice contains the requireActionHeader assertion in the secuirty policy file. What does the JAVA client need to do to fulfil this assertion? I see you have the same assertion in the example above but what specifically does the client need to include for this?
Minal

requireactionheader assertion

I have a JAVA client comsuming a .NET WSE3.0 enabled webservice. The webservice contains the requireActionHeader assertion in the secuirty policy file. What does the JAVA client need to do to fulfil this assertion? I see you have the same assertion in the example above but what specifically does the client need to include for this?
Mahesh
I have .Net client using a java webservice in CXF. I cant get path is exception when I get the response back from the webservice. Any help is greatly appreciated.

Microsoft.Web.Services3.Security.SecurityFault: Referenced security token could not be retrieved ---> System.Exception: WSE590: Failed to resolve the following Key Info <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=GreyhoundTicketing</ds:X509IssuerName>
<ds:X509SerialNumber>-151607773298184276178099932590328839740</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data></wsse:SecurityTokenReference></KeyInfo>.

Thanks
Erlend
What kind of skiMode did you set? Looks like the java web service is using serial number. Can you configure the java web service to use thumbprint sha-1? Or did you configure your client to use serial number as skiMode. Is the serial number correct? (negative?)
Mahesh
The serial number is coming through correctly and I have set the skimode to IssuerSerial on my side, but still see the same error
Mahesh
I downgraded to WSE 2.0, could you send me your app.config and policyCache.config if you have it. Thanks for your help.
Comments closed for this post