WCF: An attempt to create two-way mutual SSL authentication with self-signed certificates and "PeerTrust"

I am trying to configure the WCF service and the client on the same machine with mutual SSL authentication.

I have:

  • The certificates for the server and client have been created and put them in the LocalMachine certificate store. The private keys of the server and clients are in the Personal store, and the public keys are in the Trusted people store.

  • I set up the WCF service and the client, each of which indicated its own certificate link from the repository, and also set a link to the certificate of other parties, which will be verified using

<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" />

Note. Server certificates are issued in the name Machine, and the URL of the service called by the client is "https: \ tokenservice \ tokenservice.svc

In this configuration, I expect the client to connect to the service reliably, either with the end of the resolution of certificates from the Trusted Persons store, but I get the following error, which indicates a failed certificate verification:

[AuthenticationException: The remote certificate is not valid according to the verification procedure.]

So this does not work as I expected. Can anyone point out any errors? Or are my expectations wrong?

WCF configuration below:

  <?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <bindings> <wsHttpBinding> <binding name="CertificateForClient"> <security mode="Transport"> <transport clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="CertificateBehaviour"> <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> <serviceMetadata httpGetEnabled="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="true"/> <serviceCredentials> <clientCertificate> <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" /> </clientCertificate> <serviceCertificate findValue="CN='ServerCertificate which is machine name'" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <services> <service name="TokenService.TokenService" behaviorConfiguration="CertificateBehaviour"> <endpoint contract="TokenService.ITokenService" binding="wsHttpBinding" /> <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"> </endpoint> <host> <baseAddresses> <add baseAddress="https://tokenservice" /> </baseAddresses> </host> </service> </services> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration> 

Client Configuration:

  <system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="ClientBehaviour"> <clientCredentials> <clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" findValue="CN=TokenClient"/> <serviceCertificate> <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine"></authentication> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> <bindings> <wsHttpBinding> <binding name="ClientBinding"> <security mode="Transport"> <transport clientCredentialType="Certificate"/> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="https://tokenservice/TokenService.svc" behaviorConfiguration="ClientBehaviour" binding="wsHttpBinding" bindingConfiguration="ClientBinding" contract="TokenService.ITokenService" name="ToolClient"> <identity> <dns value="MachineName" /> </identity> </endpoint> </client> 

+4
source share
2 answers

The built-in authorization provided by PeerTrust and ChainTrust does not work when authentication is performed at the transport level using mutual SSL.

And frankly, PeerTrust in many cases does not give control over the authorization process.

A very common way to solve this problem is to connect a custom ServiceAuthorizationManager and override its OnAccess method.

 <behavior name="ServerCertificateBehavior"> <serviceCredentials> <serviceCertificate .... /> </serviceCredentials> <serviceAuthorization serviceAuthorizationManagerType="MyCustomCertificateAuthorizationManager, MyWCFExtensions.Security" /> </behavior> 

The ServiceAuthorizationManager can be executed in a few lines of code for very static simple certificate verification or more complex based on needs.

This simple evidence-based concept will hopefully help you get started:

 public class MyCustomCertificateAuthorizationManager : ServiceAuthorizationManager { public override bool CheckAccess(OperationContext operationContext, ref Message message) { base.CheckAccess(operationContext, ref message); string action = operationContext.IncomingMessageHeaders.Action; List<string> approvedActions = new List<string> { "http://kramerica.lan/namespace/MySpecialMethod", "http://kramerica.lan/namespace/AnotherMethod" }; List<string> approvedThumbprints = new List<string> { "‎1aaffe105b31b79b66c31de3389203d42351683a", "‎f1bcfbc6383bcbfa736473bcaf109987bbc2121a" }; //One way is do the authorization based on the action if the endpoint is used for more than one operation with different ACL needs if (approvedActions.Contains(action)) { foreach (ClaimSet claimSet in OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets) { X509CertificateClaimSet certificateClaimSet = claimSet as X509CertificateClaimSet; if (certificateClaimSet != null) { //Get the actual certificate used by the client X509Certificate2 certificate = certificateClaimSet.X509Certificate; //Here a real validation of certificate issuer chain etc. could be made if (certificate != null) { //This proof-of-concept does authorization based on a static list of thumbprints but about anything os possible here. //One could easily check if this certificate //is present in the TrustedPeople store or some database backend if (approvedThumbprints.Contains(certificate.Thumbprint)) return true; } } } } return false; } } 
+1
source

The base URL of the service must be the name of the server certificate.

For instance:

If my server certificate name was test.cer, then my service URL should be https: //test/MyService/MyService.svc .

How did you set up your service?

0
source

All Articles