Wednesday, December 28, 2016

Implementation of WCF message security using self-signed certificates


Implementation of WCF message security using self-signed certificates


We need to create 3 certificates.
1)      Dummy root certificate authority (CA)
2)      Server certificate using above CA.
3)      Client certificate using above CA.

Location of certificates
1)      The Root CA will be in the trusted root folder of server machine ONLY.
2)      The client needs the client certificate pfx (pvt + public key) along with the server certificate’s public key ONLY.
3)      The server needs the server certificate pfx (pvt + public key) ONLY. Nothing from client’s side is required. (Remember the root CA is common).
4)      Make sure the private key is accessible by the required process. Example “NetworkServiceAppPool” accessing server certificate private key.

Commands
1)      Creation of root CA
makecert -r -pe -n "CN=Subhasis Certificate Authority" -a sha1 -sky signature -cy authority -sv SubhasisCAPrivateKey.pvk SubhasisCA.cer
////////keep pvt key pwd as “root”

2)      Creation of server cert using above CA
makecert -n "CN=WebService1.com" -ic "SubhasisCA.cer" -iv "SubhasisCAPrivateKey.pvk" -a sha1 -sky exchange -pe -sv "WebService1PrivateKey.pvk" "WebService1.com.cer"
//////////keep the private key password of the above “webservice1pvtkeypwd”
pvk2pfx -pvk "WebService1PrivateKey.pvk" -spc "WebService1.com.cer" -pfx "WebService1.pfx" -pi webservice1pvtkeypwd

3)      Creation of client cert using same CA
makecert -n "CN=Client2.com" -ic "SubhasisCA.cer" -iv "SubhasisCAPrivateKey.pvk" -a sha1 -sky exchange -pe -sv "Client2PrivateKey.pvk" "Client2.com.cer"
//////////keep the private key password of the above “client2pvtkeypwd”

pvk2pfx -pvk "Client2PrivateKey.pvk" -spc "Client2.com.cer" -pfx "Client2.pfx" -pi client2pvtkeypwd


With the above settings, we can test the WCF client and service using the below configuration (key parts with yellow highlight) (Remember only chaintrust will work in this case) –
For peertrust, check the end of the post.
Server
<system.serviceModel>
    <client />
    <services>
      <service name="CertSecurityDemoService.Service1" behaviorConfiguration="customBehavior">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="customWsHttpBinding" contract="CertSecurityDemoService.IService1" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="customBehavior">
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>

          <serviceCredentials>
            <serviceCertificate findValue="WebService1.com" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
            <clientCertificate>
              <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <bindings>
      <wsHttpBinding>
        <binding name="customWsHttpBinding" sendTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">
          <security mode="Message">
            <message clientCredentialType="Certificate" negotiateServiceCredential="false" establishSecurityContext="false"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Client
<system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="customBehavior">
          <clientCredentials>
             <clientCertificate findValue="Client2.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>  
             <serviceCertificate>
              <defaultCertificate findValue="WebService1.com"
                                  storeLocation="LocalMachine"
                                  storeName="My"
                                  x509FindType="FindBySubjectName" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IService1" sendTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00">
          <security mode="Message">
            <message clientCredentialType="Certificate" negotiateServiceCredential="false" establishSecurityContext="false" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://abc.domain.co.in/WebService1/Service1.svc"
          binding="wsHttpBinding" contract="ServiceReference1.IService1" name="WSHttpBinding_IService1" behaviorConfiguration="customBehavior" bindingConfiguration="WSHttpBinding_IService1">
        <identity>
             <dns value ="WebService1.com"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>



With the above approach, we get both following –
1)      Mutual authentication (as both certs are from same CA).
2)      Confidentiality  - Encryption done using server cert’s private and public key. (by default Symmetric binding – derived key scenario is used. For more details, see this post
3)      Integrity – Messages signed using certs trusted by the Root CA.

Difference between chaintrust and peertrust –
PeerTrust forces a public key of the client certificate to be present in the 'Trusted People' certificate store on the service side and ChainTrust requests that the client cert can be validated against the root certificates on the server side. ChainOrPeerTrust just executes the OR operator on the last two.

Remark: PeerTrust and ChainOrPeerTrust are also subjected to another attribute called trustedStoreLocation. If peer trust is demanded, one can specify where the public keys are present, meaning either in LocalMachine or CurrentUser store.

The above implementation can be converted to “peertrust” using the following steps –
1)      Delete the root CA from the server machine trusted root folder (as it wont be needed)
2)      Copy the client machine certificate public key ONLY to server machine trusted people folder (not trusted roots).
3)      Change service config from “chaintrust” to “peertrust”.
As you might have noticed, with peertrust there is no involvement of CAs in any machine’s trusted root.

No comments:

Post a Comment