none
How can I access the client certificate in my web service when using SSL

    Question

  • I have a requirement to create a WCF web service that can authenticate both the calling user and the business partner. The data also needs to be signed and encrypted. One of my partners can only use the usernameToken part of WS-Security so I can’t use a message based security solution for interoperability reasons. This leads me to transport security with message credential – but how do I authenticate the business partner using a certificate?

     

    I have briefly looked into using client certificate validation using IIS … if I do use this how is the certificate sent over the wire (in an http header or is it negotiated) and is the certificate available in the web service so I can identify the business partner as I need this in my business logic?

     

    Any guidance on this would be much appreciated… or point me in the right direction

    Tuesday, March 09, 2010 9:04 PM

Answers

  • Hi Steven,

    Thanks very much for the reply. I have done quite a bit of research on this over the last few days and have come up with a really nice solution that I think will be interoperable with my business partners.

    For the benefit of anyone who is trying to achieve the same I will put down a quick explanation to save you some time.

     

    My basic requirement is to create a WCF web service that receives two tokens, one to identify the user calling the web service and the other to identify the business partner. It would make sense to use a UsernameToken to identify the user and a certificate to identify the business partner. I also need all traffic to be signed and encrypted. I have the limitation of not being able to use most of the features of WS-Security due to interoperability reasons with specific business partners, but I can use the usernameToken (because it’s so simple). As a result of this I need to use transport rather than message security.

     

    The solution I chose was to use a WCF web service hosted under IIS, I configured IIS to require SSL and also require client certificates. I then created a custom binding (see below) which allowed the username to be sent as a usernameToken in the soap security header and the client certificate to be sent as part of the SSL protocol (check out client certificate under the SSL specification). The benefit of this is that it doesn’t require any bespoke code or custom headers, it is reusing what is already available in the protocols. I need to authenticate the user and the certificate against a custom database so I created a custom UserNamePasswordValidator and ServiceAuthorizationManager classes for this. On the server the certificate thumbprint is available in the ClaimsSet (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets) and the username token is available in the supporting tokens collection (OperationContext.Current.SupportingTokens).

     

    It’s also worth pointing out that in this solution two certificates are used by SSL, the client certificate is used to identify the business partner and the service certificate is used for signing and encrypting during transport – both are sent as part of the SSL handshake. You can see all this working by using Fiddler to monitor the http traffic. You’ll see the client and service certificate in the http handshake and the UsernameToken in the soap message.

     

    This works great for me but if you have slightly different requirements to me then it might not be suitable for you.

     

     

    This is the custom binding

          <customBinding>

            <binding name="customTransportBinding">

              <security

                authenticationMode="UserNameOverTransport"

                includeTimestamp="false">

              </security>

              <httpsTransport requireClientCertificate="true"/>

            </binding>

          </customBinding>

     

     

    This is how the client code specifies the tokens

     

    using (var proxy = new MyServiceClient())

    {

        // Set username

        proxy.ClientCredentials.UserName.UserName = "testuser1";

        //proxy.ClientCredentials.UserName.Password = "password";

     

        // Set the certificate

        proxy.ClientCredentials.ClientCertificate.SetCertificate("CN=MyDevClientCert", StoreLocation.CurrentUser, StoreName.My)

     

        // Ignore SSL certificate validation errors (for development only)

        ServicePointManager.ServerCertificateValidationCallback +=

            new System.Net.Security.RemoteCertificateValidationCallback(customCertificateValidation)

     

        etc…   

     

    A few references that I found really useful (cheers Dominick for you great blog):

     

    Certificate based Authentication and WCF

    http://www.leastprivilege.com/CertificateBasedAuthenticationAndWCF.aspx

     

    Certificate based Authentication and WCF (Mode independent)

    http://www.leastprivilege.com/CertificateBasedAuthenticationAndWCFModeIndependent.aspx

     

    How to: Use Certificate Authentication and Transport Security in WCF Calling from Windows Forms.

    http://msdn.microsoft.com/en-us/library/cc949005.aspx

     

    • Marked as answer by CyberJam Thursday, March 11, 2010 6:33 PM
    Thursday, March 11, 2010 6:32 PM

All replies

  • Hi Cyber,

    Regarding on your problem scenario, you need to send two authentication token/credentials to service for authentication, correct?

    As for WCF security through TransportWithMessageCredentials mode, it will use https/ssl to secure the message at transport protocol layer(sign and encrypt it), and authentication credentials(at messgae layer) are stored in cleartext in SOAP message. For your case, if you want to use UsernameToken as the message credentials type, then you will have to pass an additional certificate token. For passing multiple tokens, WCF has provide a supportingToken approach , however, it is not guaranteed that other non-WCF platform will support this(require message security):

    #WCF Supporting Tokens
    http://blogs.msdn.com/appsec/archive/2007/03/04/wcf-supporting-tokens.aspx

    another possible approach is that you choose certificate as the credential type of your TransportWithMessageCredential mode, and then you can use some custom SOAP header to send your username/password credentials and manually extract the credentials from header at service-side.


    #WCF Custom Message Headers
    http://blogs.microsoft.co.il/blogs/bursteg/archive/2006/04/23/141.aspx

    #Handling custom SOAP headers via WCF Behaviors
    http://weblogs.asp.net/paolopia/archive/2008/02/25/handling-custom-soap-headers-via-wcf-behaviors.aspx

     


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Thursday, March 11, 2010 3:56 AM
    Moderator
  • Hi Steven,

    Thanks very much for the reply. I have done quite a bit of research on this over the last few days and have come up with a really nice solution that I think will be interoperable with my business partners.

    For the benefit of anyone who is trying to achieve the same I will put down a quick explanation to save you some time.

     

    My basic requirement is to create a WCF web service that receives two tokens, one to identify the user calling the web service and the other to identify the business partner. It would make sense to use a UsernameToken to identify the user and a certificate to identify the business partner. I also need all traffic to be signed and encrypted. I have the limitation of not being able to use most of the features of WS-Security due to interoperability reasons with specific business partners, but I can use the usernameToken (because it’s so simple). As a result of this I need to use transport rather than message security.

     

    The solution I chose was to use a WCF web service hosted under IIS, I configured IIS to require SSL and also require client certificates. I then created a custom binding (see below) which allowed the username to be sent as a usernameToken in the soap security header and the client certificate to be sent as part of the SSL protocol (check out client certificate under the SSL specification). The benefit of this is that it doesn’t require any bespoke code or custom headers, it is reusing what is already available in the protocols. I need to authenticate the user and the certificate against a custom database so I created a custom UserNamePasswordValidator and ServiceAuthorizationManager classes for this. On the server the certificate thumbprint is available in the ClaimsSet (OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets) and the username token is available in the supporting tokens collection (OperationContext.Current.SupportingTokens).

     

    It’s also worth pointing out that in this solution two certificates are used by SSL, the client certificate is used to identify the business partner and the service certificate is used for signing and encrypting during transport – both are sent as part of the SSL handshake. You can see all this working by using Fiddler to monitor the http traffic. You’ll see the client and service certificate in the http handshake and the UsernameToken in the soap message.

     

    This works great for me but if you have slightly different requirements to me then it might not be suitable for you.

     

     

    This is the custom binding

          <customBinding>

            <binding name="customTransportBinding">

              <security

                authenticationMode="UserNameOverTransport"

                includeTimestamp="false">

              </security>

              <httpsTransport requireClientCertificate="true"/>

            </binding>

          </customBinding>

     

     

    This is how the client code specifies the tokens

     

    using (var proxy = new MyServiceClient())

    {

        // Set username

        proxy.ClientCredentials.UserName.UserName = "testuser1";

        //proxy.ClientCredentials.UserName.Password = "password";

     

        // Set the certificate

        proxy.ClientCredentials.ClientCertificate.SetCertificate("CN=MyDevClientCert", StoreLocation.CurrentUser, StoreName.My)

     

        // Ignore SSL certificate validation errors (for development only)

        ServicePointManager.ServerCertificateValidationCallback +=

            new System.Net.Security.RemoteCertificateValidationCallback(customCertificateValidation)

     

        etc…   

     

    A few references that I found really useful (cheers Dominick for you great blog):

     

    Certificate based Authentication and WCF

    http://www.leastprivilege.com/CertificateBasedAuthenticationAndWCF.aspx

     

    Certificate based Authentication and WCF (Mode independent)

    http://www.leastprivilege.com/CertificateBasedAuthenticationAndWCFModeIndependent.aspx

     

    How to: Use Certificate Authentication and Transport Security in WCF Calling from Windows Forms.

    http://msdn.microsoft.com/en-us/library/cc949005.aspx

     

    • Marked as answer by CyberJam Thursday, March 11, 2010 6:33 PM
    Thursday, March 11, 2010 6:32 PM