In the business-to-business scenario, the service and its clients are disparate business entities. They do not share credentials or accounts, and the communication between them is typically closed to the public. There are relatively few clients interacting with the service, and the client can only interact with the service after an elaborate business agreement has been established and other conditions have been met. Instead of Windows accounts or usernames, the clients identify themselves to the service using X509 certificates. These certificates are usually known a priori to the service. The client or service may not necessarily be using WCF, or even Windows. Therefore, if you are writing a service or a client, you cannot assume the use of WCF at the other end. The client calls originate from outside the firewall, and you need to rely on HTTP for transport. Also, multiple intermediaries are possible.
For the business-to-business scenario, you should use the Internet bindings; namely,
BasicHttpBinding
, WSHttpBinding
, and WSDualHttpBinding
. You
must use Message security for the transfer security mode, to provide for end-to-end
security across all intermediaries. The message will be protected using a service-side
certificate, just as with the Internet scenario. However, unlike with the Internet
scenario, here the clients provide credentials in the form of a certificate. This is done
uniformly across these bindings by selecting MessageCredentialType.Certificate
for the client credentials type to be used
with the Message security mode. You need to configure this on both the client and the
service. For example, to configure the WSHttpBinding
programmatically, you would write:
WSHttpBinding binding = new WSHttpBinding( );
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate
;
Or with a config file:
<bindings>
<wsHttpBinding>
<binding name = "WSCertificateSecurity">
<security mode = "Message">
<message clientCredentialType = "Certificate"
/>
</security>
</binding>
</wsHttpBinding>
</bindings>
The service administrator has a number of options as to how to authenticate the certificates sent by the clients. If its certificate is validated, the client is considered authenticated. If no validation is done on the service side, merely sending a certificate will do. If the validation mode is set to use a chain of trust and a trusted root authority issued the certificate, the client will be considered authenticated. However, the best way of validating the client's certificate is to use peer trust. With this approach, the service administrator installs the certificates of all the clients allowed to interact with the service in the Trusted People store on the service's local machine. When the service receives the client's certificate, it verifies that the certificate is in the trusted store, and if so, the client is considered authenticated. I recommend using peer trust in the business-to-business scenario.
The ServiceCredentials
class offers the ClientCertificate
property of the type X509CertificateInitiatorServiceCredential
:
public class ServiceCredentials : ...,IServiceBehavior { public X509CertificateInitiatorServiceCredential ClientCertificate {get;} //More members }
X509CertificateInitiatorServiceCredential
provides
the Authentication
property of the type X509ClientCertificateAuthentication
, which lets you configure
the certificate validation mode:
public sealed class X509CertificateInitiatorServiceCredential { public X509ClientCertificateAuthentication Authentication {get;} //More members } public class X509ClientCertificateAuthentication { public X509CertificateValidationMode CertificateValidationMode {get;set;} //More members }
Example 10-16 demonstrates the settings required in the host config file for the business-to-business scenario. Note in Example 10-16 that the host still needs to provide its own certificate for Message security.
Example 10-16. Configuring the host for business-to-business security
<services> <service name = "MyService" behaviorConfiguration = "BusinessToBusiness"> ... </service> </services> <behaviors> <serviceBehaviors> <behavior name = "BusinessToBusiness"> <serviceCredentials> <serviceCertificate ... /><clientCertificate>
<authentication certificateValidationMode = "PeerTrust"/>
</clientCertificate>
</serviceCredentials> </behavior> </serviceBehaviors> </behaviors>
The client needs to reference the certificate to use by including its location, name,
and lookup method. This is done by accessing the ClientCredentials
property of the proxy, which offers the ClientCertificate
property of the type X509CertificateInitiatorClientCredential
:
public class ClientCredentials : ...,IEndpointBehavior { public X509CertificateInitiatorClientCredential ClientCertificate {get;} //More members } public sealed class X509CertificateInitiatorClientCredential { public void SetCertificate(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue); //More members }
However, the client will typically set these values in its config file, as shown in Example 10-17.
Example 10-17. Setting the client's certificate
<client> <endpoint behaviorConfiguration ="BusinessToBusiness"
... /> </client> ... <behaviors> <endpointBehaviors> <behavior name ="BusinessToBusiness"
> <clientCredentials> <clientCertificate findValue = "MyClientCert" storeLocation = "LocalMachine" storeName = "My" x509FindType = "FindBySubjectName" /> ... </clientCredentials> </behavior> </endpointBehaviors> </behaviors>
The config file must also indicate the service certificate validation mode. When using
the BasicHttpBinding
, since that binding cannot
negotiate the service certificate, the client's config file needs to contain in the
service certificate section of the endpoint behavior the location of the service
certificate to use. Note that when using a service test certificate, as with the Internet
scenario, the client's config file must still include the information regarding the
endpoint's identity.
If the client is required to always provide the same certificate, the client developer can encapsulate setting the certificate in the proxy constructors:
class MyContractClient: ClientBase<...>,... { public MyContractClient( ) { SetCertificate( ); } /* More constructors */ void SetCertificate( ) { ClientCredentials.ClientCertificate.SetCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyClientCert"); } //Rest of the proxy }
Once the client certificate is configured, there is no need to do anything special with the proxy class:
MyContractClient proxy = new MyContractClient( ); proxy.MyMethod( ); proxy.Close( );
By default, the service cannot employ principal-based, role-based security. The reason
is that the credentials provided—namely, the client's certificate—do not map to either
Windows or ASP.NET user accounts. Because
business-to-business endpoints and services are often dedicated to a small set of clients
or even a particular client, this lack of authorization support may not pose a problem. If
that is indeed your case, you should set the PrincipalPermissionMode
property to PrincipalPermissionMode.None
, so that WCF will attach a generic principal
with a blank identity as opposed to a WindowsIdentity
instance with a blank identity.
If, on the other hand, you would still like to authorize the clients, you can actually achieve just that. In essence, all you need to do is deploy some credentials store, add each client's certificate name—that is, its common name and its thumbprint—to that repository, and then perform access checks against that store as needed.
In fact, nothing prevents you from taking advantage of the ASP.NET role provider for authorization, even if you didn't use the membership provider for authentication. This ability to use the providers separately was a core design goal for the ASP.NET provider model.
First, you need to enable the role provider in the host config file and configure the application name as in Example 10-14 (or provide the application name programmatically).
Next, add the client certificate and thumbprint to the membership store as a user, and assign roles to it. For example, when using a certificate whose common name is MyClientCert, you need to add a user by that name (such as "CN=MyClientCert; 12A06153D25E94902F50971F68D86DCDE2A00756") to the membership store, and provide a password. The password, of course, is irrelevant and will not be used. Once you have created the user, assign it to the appropriate roles in the application.
Most importantly, set the PrincipalPermissionMode
property to PrincipalPermissionMode.UseAspNetRoles
.
Example 10-18 lists the required settings in the
host config file.
Example 10-18. ASP.NET role-based security for the business-to-business scenario
<system.web>
<roleManager enabled = "true" defaultProvider = "...">
...
</roleManager>
</system.web>
<system.serviceModel> <services> <service name = "MyService" behaviorConfiguration = "BusinessToBusiness"> ... </service> </services> <behaviors> <serviceBehaviors> <behavior name = "BusinessToBusiness"> <serviceCredentials> <serviceCertificate ... /> <clientCertificate> <authentication certificateValidationMode = "PeerTrust"/> </clientCertificate> </serviceCredentials><serviceAuthorization principalPermissionMode = "UseAspNetRoles"/>
</behavior> </serviceBehaviors> </behaviors> <bindings> ... </bindings> </system.serviceModel>
Now you can use role-based security, just as in Example 10-15.
If the PrincipalPermissionMode
property is set to
PrincipalPermissionMode.None
, then the principal
identity will be a GenericIdentity
with a blank
username. The security call context's primary identity will be of the type X509Identity
and will contain the client certificate's common
name and its thumbprint. The security call context's Windows identity will have a blank
username, since no valid Windows credentials were provided. If the PrincipalPermissionMode
property is set to PrincipalPermissionMode.UseAspNetRoles
, then both the
principal identity and the security call context's primary identity will be set to an
instance of X509Identity
containing the client
certificate and thumbprint. The security call context's Windows identity will have a blank
username, as before. Table 10-6 details this
setup.
Table 10-6. Identity management in the business-to-business scenario with ASP.NET role providers
Identity |
Type |
Value |
Authenticated |
---|---|---|---|
Thread principal |
|
Client cert name |
Yes |
Security context primary |
|
Client cert name |
Yes |
Security context Windows |
|
- |
No |
In the business-to-business scenario callbacks behave just as they do in the Internet scenario, since in both cases the same transfer security mechanism is used and the service identifies itself using a certificate. As with the Internet callback scenario, avoid sensitive work in the callback, since you cannot use role-based security.
While Figure 10-8 is not specific to the business-to-business scenario, having covered this scenario, this is the first point in this chapter where I can show all the pieces of the service host pertaining to security.
18.223.170.63