matteo rulli
matteo rulli

Reputation: 1493

.net WCF - CXF/WSS4j interoperability

I would like to consume a CXF web-service from a .net c# client. We are currently working with java-to-java requests and we protect SOAP envelopes through ws-security (WSS4J library).

My question is: how can I implement a C# WS-client which produces the same SOAP requests as the following client-side java code?

//doc is the original SOAP envelope to process with WSS4J
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);

//add username token with password digest
WSSecUsernameToken usrNameTok = new WSSecUsernameToken();
usrNameTok.setPasswordType(WSConstants.PASSWORD_DIGEST);
usrNameTok.setUserInfo("guest",psw_guest);
usrNameTok.prepare(doc);
usrNameTok.appendToHeader(secHeader);

//sign the envelope body with client key
WSSecSignature sign = new WSSecSignature();
sign.setUserInfo("clientx509v1", psw_clientx509v1);
sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);

Document signedDoc = null;      
sign.prepare(doc, sigCrypto, secHeader);
signedDoc = sign.build(doc, sigCrypto, secHeader);

//encrypt envelope body with server public key
WSSecEncrypt encrypt = new WSSecEncrypt();
encrypt.setUserInfo("serverx509v1");

// build the encrypted SOAP part
String out = null;  
Document encryptedDoc = encrypt.build(signedDoc, encCrypto, secHeader);
return encryptedDoc;

Does anybody know where I could find a microsoft how-to or a .net working example?

================================ EDIT ====================================

Thank you Ladislav! I applied your suggestions and I came up with something like:

X509Certificate2 client_pk, server_cert;
client_pk = new X509Certificate2(@"C:\x509\clientKey.pem", "blablabla");
server_cert = new X509Certificate2(@"C:\x509\server-cert.pfx", "blablabla");

// Create the binding.
System.ServiceModel.WSHttpBinding myBinding = new WSHttpBinding();    
myBinding.TextEncoding = ASCIIEncoding.UTF8;
myBinding.MessageEncoding = WSMessageEncoding.Text;            
myBinding.Security.Mode = SecurityMode.Message;
myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
myBinding.Security.Message.AlgorithmSuite =                                          
            System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128;

// Disable credential negotiation and the establishment of 
// a security context.
myBinding.Security.Message.NegotiateServiceCredential = false;
myBinding.Security.Message.EstablishSecurityContext = false;                

// Create the endpoint address. 
EndpointAddress ea =
    new EndpointAddress(new Uri("http://bla.bla.bla"), 
            EndpointIdentity.CreateDnsIdentity("issuer"));

// configure the username credentials on the channel factory 
UsernameClientCredentials credentials = new UsernameClientCredentials(new 
                                    UsernameInfo("superadmin", "secret"));

// Create the client. 
PersistenceClient client = new PersistenceClient(myBinding, ea);

client.Endpoint.Contract.ProtectionLevel = 
            System.Net.Security.ProtectionLevel.EncryptAndSign;

// replace ClientCredentials with UsernameClientCredentials
client.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
client.Endpoint.Behaviors.Add(credentials);

// Specify a certificate to use for authenticating the client.
client.ClientCredentials.ClientCertificate.Certificate = client_pk;

// Specify a default certificate for the service.
client.ClientCredentials.ServiceCertificate.DefaultCertificate = server_cert;

// Begin using the client.
client.Open();
clientProxyNetwork[] response = client.GetAllNetwork();

As a result I get (server-side) the following CXF exception:

 java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:421)
at sun.security.provider.certpath.BasicChecker.verifySignature(BasicChecker.java:133)
at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:112)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate  (PKIXMasterCertPathValidator.java:117)

Therefore it seems a key jks->pem conversion problem... Or am I am missing something in the client-code above?

Upvotes: 3

Views: 5095

Answers (2)

matteo rulli
matteo rulli

Reputation: 1493

Well, in the end the solution is to encrypt and sign the whole username token. As for the interoperability, the ws addressing must be activated in cxf and a custom binding in c# is needed. The custom binding that did the trick is basically

AsymmetricSecurityBindingElement abe =
    (AsymmetricSecurityBindingElement)SecurityBindingElement.
CreateMutualCertificateBindingElement(MessageSecurityVersion.
WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
               

Wcf signs each ws addressing element, therefore the same must be done server side.

Upvotes: 1

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364389

This is usually pretty big problem because WCF does not support UserNameToken Profile with Digested password. I needed it few months ago and we had to implement our own custom binding but that code is not ready for publishing. Fortunatelly this blog article describes other implementation and contains sample code with new UserNameClientCredentials class supporting digested password.

Btw. same security configuration should be possible with older API called WSE 3.0. It was replaced by WCF but still some WS-* stack configuration are much simpler with that API and old ASMX services.

Upvotes: 0

Related Questions