Issue #1: Although I have 4+ years experience in .NET, I had never used Windows Communication Foundation in .NET. On top of that I was not using the Microsoft VS 2008 suite. Not wanting to shell out $800 for a license, I choose the open source IDE SharpDevelop.
I quickly learned that to generated my WCF proxy client I would be using the svcutil.exe application that is included in the Windows SDK installation. The basic usage is:
svcutil.exe http://myosbserver:7001/HelloWorldProxy?wsdl
Issue #2: Unrecognized WS-Policy
OSB runs in WebLogic Server and uses proprietary WS-Policy implementation that BEA included in WLS 9. WCF does not recognize the policies when the proxy client is created using svcutil.exe. Here is the section in the output.config file indicating the unrecognized policy:
<!-- WsdlImporter encountered unrecognized policy assertions in ServiceDescription 'http://service.myosbserver.com/': -->
<!-- <wsdl:binding name='HelloWorldImplServiceSoapBinding'> -->
<!-- <wssp:Integrity xmlns:wssp="http://www.bea.com/wls90/security/policy">..</wssp:Integrity> -->
<!-- <wssp:MessageAge xmlns:wssp="http://www.bea.com/wls90/security/policy">..</wssp:MessageAge> -->
This is the actual policy that I was using in OSB when I began:
<wsp:Policy wsu:Id="MyPolicy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part">
<wssp:Integrity SignToken="false">
<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetBody(.)
</wssp:MessageParts>
</wssp:Target>
</wssp:Integrity>
<wssp:MessageAge Age="300"/>
</wsp:Policy>
After taking a few days to figure out the necessary app.config file settings for the System.ServiceModel configuration section here is what I ended up with.
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="secureBehaviour">
<clientCredentials>
<clientCertificate findValue="appkey"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
<serviceCertificate>
<defaultCertificate findValue="serverkey"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="CustomBindingForX509">
<security allowSerializedSigningTokenOnReply="true"
authenticationMode="MutualCertificateDuplex"
requireDerivedKeys="false"
securityHeaderLayout="LaxTimestampLast"
includeTimestamp="true"
keyEntropyMode="ClientEntropy"
messageProtectionOrder="SignBeforeEncrypt"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<secureConversationBootstrap />
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpTransport />
</binding>
</bindings>
<client>
<endpoint address="http://txslaquafp1.nss.vzwnet.com:7001/HelloWorldProxy"
binding="customBinding" behaviorConfiguration="secureBehaviour"
bindingConfiguration="CustomBindingForX509"
contract="HelloWorld" name="HelloWorldImplServiceSoapBindingQSPort" >
<identity>
<certificateReference findValue="serverkey"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName"/>
</identity>
</endpoint>
</client>
</system.serviceModel>
At this point I attempted my first call to the web service - FAILURE!
Unknown to me at the time was that the .NET Framework 3.5 SP1 by default has a message protection level of Sign and Encrypt, but my WS-Policy only expects the SOAP body to be sign - not encrypted. The result was me having to add an attribute to the Service Contract in the generated proxy client.
Before:
[System.ServiceModel.ServiceContractAttribute(Namespace="http://service.myosbserver.com/", ConfigurationName="HelloWorld")]
After:
[System.ServiceModel.ServiceContractAttribute(Namespace="http://service.myosbserver.com/", ConfigurationName="HelloWorld",
ProtectionLevel = System.Net.Security.ProtectionLevel.Sign )]
Now my outgoing SOAP Header has a signed body without encryption. Oh, but wait, another snag...the SOAP response header is throwing an error! WHAT? .NET also expects the timestamp to be signed on the response as well. (More info) I tried a to get the client application to not require the timestamp to be signed, but after many hours I decided to modify the WS-Policy on the OSB. I added the timestamp signature to the policy to solve this issue. Here is the new WS-Policy on OSB:
<wsp:Policy wsu:Id="MyPolicy"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part">
<wssp:Integrity SignToken="false">
<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetBody(.)
</wssp:MessageParts>
</wssp:Target>
<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetHeader(./wsse:Security/wsu:Timestamp)
</wssp:MessageParts>
</wssp:Target>
</wssp:Integrity>
<wssp:MessageAge Age="300"/>
</wsp:Policy>
Just when I think I was done, I had one last error - The certificates were loaded in my personal certificate store on Windows XP, but they were not included in the Trusted Root certificates. Not sure why this matters, but once I also added them to the Trusted Root certificate store the issue resolved.
Non .NET Issues:
OSB/WebLogic Uses Java Keystores (.jks) to hold the private and public x509 certificates used to sign the SOAP header. I had to convert the JKS files to import into the Windows XP certificate stores. Here is a good post that can help with that. Also available is a Java application called Portecle.
The Java Web Service did not contain @WebMethod and @WebParam annotations in the code. WCF in turn was not able to decipher the method names and parameter names from the WSDL. The outgoing SOAP request in turn did not include the the param or method names in the call. We added the annotations to the web service and this issued was resloved. Here is a link to the blog that described this issue.