Search code examples
wcfsoapx509certificatews-security

wcf wss1.1 BindarySecurityToken UsernameToken configuration for client


I want to both sign the message with the certificate and include the username/password. I can get WCF to do either of these but not both at the same time.

I'm trying to setup a client to connect to a SOAP service (WS-Security 2004) that is using both x509 certificates and a username/password using WCF.

I have certificates for both the server and client and SSL URI (https://) for the service endpoint. The client messages needs to be signed:

<wsse:BinarySecurityToken 
   EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" 
   ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" 
   wsu:Id="CertId-1317568">
MIICejCCAeOgAwIBAgIRAKxcIy5wGNq2XWsruqtiXY4wDQYJKoZIhvcNAQEF
...
</wsse:BinarySecurityToken>

and the header also needs to include the UsernameToken:

<wsse:UsernameToken>
   <wsse:Username>VendorName0001</wsse:Username>
   <wsse:Password 
      Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
      password
   </wsse:Password>
</wsse:UsernameToken>

I tried Rick Strahl's solution but I don't need NONCE and I still need to x509 signature.

My code (which I know isn't quite right), because the BasicHttpSecurityMode.TransportWithMessageCredential seems to override the username setting for the message.

var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

var uri = "https://company.com/service.svc";
var client = new ServiceNamespace.ServiceWrapperClient(binding, new EndpointAddress(uri));
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";

client.ClientCredentials.ClientCertificate.SetCertificate(
    StoreLocation.LocalMachine,
    StoreName.My,
    X509FindType.FindByThumbprint,
    "1946c826c9bfb0b25b437da763a7c637eb19f963");

client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
    StoreLocation.LocalMachine,
    StoreName.TrustedPublisher,
    X509FindType.FindBySubjectName,
    "TEST subject name");

Solution

  • This worked for me:

    Update the base class on the web reference

    //public partial class FooServiceWrapper : System.Web.Services.Protocols.SoapHttpClientProtocol {
    public partial class FooServiceWrapper : Microsoft.Web.Services3.WebServicesClientProtocol {
    

    Use Soap Context Security to add the tokens I needed

    client.Url = ServiceUrl;
    client.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap11;
    client.RequestSoapContext.Security.Tokens.Add(
                new UsernameToken("username", "password", PasswordOption.SendPlainText));
    client.RequestSoapContext.Security.MustUnderstand = false;
    
    var certToken = new X509SecurityToken(new X509Certificate2(clientCert));
    client.RequestSoapContext.Security.Tokens.Add(certToken);
    client.RequestSoapContext.Security.Elements.Add(new MessageSignature(certToken));