Message Security with a Windows Client without Credential Negotiation
The following scenario shows a Windows Communication Foundation (WCF) client and service secured by the Kerberos protocol.
Both the service and the client are in the same domain or trusted domains.
Note
The difference between this scenario and Message Security with a Windows Client is that this scenario does not negotiate the service credential with the service prior to sending the application message. Additionally, because this requires the Kerberos protocol, this scenario requires a Windows domain environment.
Characteristic | Description |
---|---|
Security Mode | Message |
Interoperability | Yes, WS-Security with Kerberos token-profile compatible clients |
Authentication (Server) | Mutual authentication of the server and client |
Authentication (Client) | Mutual authentication of the server and client |
Integrity | Yes |
Confidentiality | Yes |
Transport | HTTP |
Binding | WSHttpBinding |
Service
The following code and configuration are meant to run independently. Do one of the following:
Create a stand-alone service using the code with no configuration.
Create a service using the supplied configuration, but do not define any endpoints.
Code
The following code creates a service endpoint that uses message security. The code disables service credential negotiation, and the establishment of a security context token (SCT).
Note
To use the Windows credential type without negotiation, the service's user account must have access to service principal name (SPN) that is registered with the Active Directory domain. You can do this in two ways:
Use the
NetworkService
orLocalSystem
account to run your service. Because those accounts have access to the machine SPN that is established when the machine joins the Active Directory domain, WCF automatically generates the proper SPN element inside the service's endpoint in the service's metadata (Web Services Description Language, or WSDL).Use an arbitrary Active Directory domain account to run your service. In this case, you need to establish an SPN for that domain account. One way of doing this is to use the Setspn.exe utility tool. Once the SPN is created for the service's account, configure WCF to publish that SPN to the service's clients through its metadata (WSDL). This is done by setting the endpoint identity for the exposed endpoint, either though an application configuration file or code. The following example publishes the identity programmatically.
For more information about SPNs, the Kerberos protocol, and Active Directory, see Kerberos Technical Supplement for Windows. For more information about endpoint identities, see SecurityBindingElement Authentication Modes.
// Create the service host.
ServiceHost myServiceHost = new ServiceHost(typeof(Calculator));
// Create the binding.
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType =
MessageCredentialType.Windows;
// Disable credential negotiation and establishment of the
// security context.
binding.Security.Message.NegotiateServiceCredential = false;
binding.Security.Message.EstablishSecurityContext = false;
// Create a URI for the endpoint address.
Uri httpUri = new Uri("http://localhost/Calculator");
// Create the EndpointAddress with the SPN for the Identity.
EndpointAddress ea = new EndpointAddress(httpUri,
EndpointIdentity.CreateSpnIdentity("service_spn_name"));
// Get the contract from the ICalculator interface (not shown here).
// See the sample applications for an example of the ICalculator.
ContractDescription contract = ContractDescription.GetContract(
typeof(ICalculator));
// Create a new ServiceEndpoint.
ServiceEndpoint se = new ServiceEndpoint(contract, binding, ea);
// Add the service endpoint to the service.
myServiceHost.Description.Endpoints.Add(se);
// Open the service.
myServiceHost.Open();
Console.WriteLine("Listening...");
Console.ReadLine();
// Close the service.
myServiceHost.Close();
' Create the service host.
Dim myServiceHost As New ServiceHost(GetType(ServiceModel.Calculator))
' Create the binding.
Dim binding As New WSHttpBinding()
binding.Security.Mode = SecurityMode.Message
binding.Security.Message.ClientCredentialType = _
MessageCredentialType.Windows
' Disable credential negotiation and establishment of the
' security context.
binding.Security.Message.NegotiateServiceCredential = False
binding.Security.Message.EstablishSecurityContext = False
' Create a URI for the endpoint address.
Dim httpUri As New Uri("http://localhost/Calculator")
' Create the EndpointAddress with the SPN for the Identity.
Dim ea As New EndpointAddress(httpUri, _
EndpointIdentity.CreateSpnIdentity("service_spn_name"))
' Get the contract from the ICalculator interface (not shown here).
' See the sample applications for an example of the ICalculator.
Dim contract As ContractDescription = ContractDescription.GetContract(GetType(ICalculator))
' Create a new ServiceEndpoint.
Dim se As New ServiceEndpoint(contract, binding, ea)
' Add the service endpoint to the service.
myServiceHost.Description.Endpoints.Add(se)
' Open the service.
myServiceHost.Open()
Console.WriteLine("Listening...")
Console.ReadLine()
' Close the service.
myServiceHost.Close()
Configuration
The following configuration can be used instead of the code.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors />
<services>
<service behaviorConfiguration="" name="ServiceModel.Calculator">
<endpoint address="http://localhost/Calculator"
binding="wsHttpBinding"
bindingConfiguration="KerberosBinding"
name="WSHttpBinding_ICalculator"
contract="ServiceModel.ICalculator"
listenUri="net.tcp://localhost/metadata" >
<identity>
<servicePrincipalName value="service_spn_name" />
</identity>
</endpoint>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="KerberosBinding">
<security>
<message negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client />
</system.serviceModel>
</configuration>
Client
The following code and configuration are meant to run independently. Do one of the following:
Create a stand-alone client using the code (and client code).
Create a client that does not define any endpoint addresses. Instead, use the client constructor that takes the configuration name as an argument. For example:
CalculatorClient cc = new CalculatorClient("EndpointConfigurationName");
Dim cc As New CalculatorClient("EndpointConfigurationName")
Code
The following code configures the client. The security mode is set to Message, and the client credential type is set to Windows. Note that the NegotiateServiceCredential and EstablishSecurityContext properties are set to false
.
Note
To use Windows credential type without negotiation, the client must be configured with the service's account SPN prior to commencing the communication with the service. The client uses the SPN to get the Kerberos token to authenticate and secure the communication with the service. The following sample shows how to configure the client with the service's SPN. If you are using the ServiceModel Metadata Utility Tool (Svcutil.exe) to generate the client, the service's SPN will be automatically propagated to the client from the service's metadata (WSDL), if the service's metadata contains that information. For more information about how to configure the service to include its SPN in the service's metadata, see the "Service" section later in this topic.
For more information about SPNs, Kerberos, and Active Directory, see Kerberos Technical Supplement for Windows. For more information about endpoint identities, see SecurityBindingElement Authentication Modes topic.
// Create the binding.
WSHttpBinding myBinding = new WSHttpBinding();
myBinding.Security.Mode = SecurityMode.Message;
myBinding.Security.Message.ClientCredentialType =
MessageCredentialType.Windows;
// Disable credential negotiation and the establishment of
// a security context.
myBinding.Security.Message.NegotiateServiceCredential = false;
myBinding.Security.Message.EstablishSecurityContext = false;
// Create the endpoint address and set the SPN identity.
// The SPN must match the identity of the service's SPN.
// If using SvcUtil to generate a configuration file, the SPN
// will be published as the <servicePrincipalName> element under the
// <identity> element.
EndpointAddress ea = new EndpointAddress(
new Uri("http://machineName/Calculator"),
EndpointIdentity.CreateSpnIdentity("service_spn_name"));
// Create the client.
CalculatorClient cc =
new CalculatorClient(myBinding, ea);
// Begin using the client.
try
{
cc.Open();
Console.WriteLine(cc.Add(200, 1111));
Console.ReadLine();
// Close the client.
cc.Close();
}
' Create the binding.
Dim myBinding As New WSHttpBinding()
myBinding.Security.Mode = SecurityMode.Message
myBinding.Security.Message.ClientCredentialType = _
MessageCredentialType.Windows
' Disable credential negotiation and the establishment of
' a security context.
myBinding.Security.Message.NegotiateServiceCredential = False
myBinding.Security.Message.EstablishSecurityContext = False
' Create the endpoint address and set the SPN identity.
' The SPN must match the identity of the service's SPN.
' If using SvcUtil to generate a configuration file, the SPN
' will be published as the <servicePrincipalName> element under the
' <identity> element.
Dim ea As New EndpointAddress(New Uri("http://machineName/calculator"), _
EndpointIdentity.CreateSpnIdentity("service_spn_name"))
' Create the client.
Dim cc As New CalculatorClient(myBinding, ea)
' Begin using the client.
Try
cc.Open()
Console.WriteLine(cc.Add(100, 11))
Console.ReadLine()
' Close the client.
cc.Close()
Catch tex As TimeoutException
Console.WriteLine(tex.Message)
cc.Abort()
Catch cex As CommunicationException
Console.WriteLine(cex.Message)
cc.Abort()
Finally
Console.WriteLine("Closed the client")
Console.ReadLine()
End Try
Configuration
The following code configures the client. Note that the <servicePrincipalName> element must be set to match the service's SPN as registered for the service's account in the Active Directory domain.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" >
<security mode="Message">
<message clientCredentialType="Windows"
negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/Calculator"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ICalculator"
contract="ICalculator"
name="WSHttpBinding_ICalculator">
<identity>
<servicePrincipalName value="service_spn_name" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>