How to: Verify Digital Signatures of SOAP Messages Signed Using a Kerberos Ticket
WSE validates a digital signature for cryptographic correctness, but user code or policy should be used to verify that a signature exists and that the signature applies to the expected set of XML elements. When WSE is configured to run with the recipient, signature validation is done by WSE before recipient code executes.
The following procedure can be used with both the KerberosToken and KerberosToken2 security tokens. To use the procedure for KerberosToken2 security tokens, do the following:
- In code examples, replace KerberosToken with KerberosToken2.
- In policy examples, change the value of the <TokenType> Element element from https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5ST to https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5_AP_REQ.
For more details about the difference between the KerberosToken and KerberosToken2 security tokens, see Differences between KerberosToken and KerberosToken2.
To configure WSE to validate digital signatures for incoming SOAP messages
In the Web.config file for the Web service, include an <add> Element for <soapExtensionTypes> (WSE for Microsoft .NET) element in the <soapExtensionTypes> section.
When the SOAP message recipient is a Web service client, this configuration entry is not required. Instead, the base class that the proxy class derives from must be changed to derive from the WebServicesClientProtocol.
The following code example is the configuration entry that must be placed in the Web.config file for WSE to run with a Web service. The type attribute of the <add> Element for <soapExtensionTypes> (WSE for Microsoft .NET) section must be on one line, even though the following sample shows it split across multiple lines for readability.
<configuration> <system.web> <webServices> <soapExtensionTypes> <add type="Microsoft.Web.Services2.WebServicesExtension, Microsoft.Web.Services2,Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" priority="1" group="0"/> </soapExtensionTypes> </webServices> </system.web> </configuration>
To use policy to require incoming SOAP messages to be signed using a Kerberos token and to verify that the required XML elements are signed
Define a policy assertion and digital signature requirements by adding <Policy> Element (WSE for Microsoft .NET) (1) and <Integrity> Element elements.
- Add a <Policy> Element (WSE for Microsoft .NET) (1) element to the policy file for the application. Add the <Policy> Element (WSE for Microsoft .NET) (1) element as a child element of the <policies> Element element.
The <Policy> Element (WSE for Microsoft .NET) (1) element defines criteria that a SOAP message must meet. The criteria are specified as child elements of the <Policy> Element (WSE for Microsoft .NET) (1) element. The Id attribute value provides a name that is used by the <request> Element (WSE for Microsoft .NET), <response> Element (WSE for Microsoft .NET), and <fault> Element elements to refer to the policy assertion when applying the policy to an endpoint. - Add an <Integrity> Element child element to the <Policy> Element (WSE for Microsoft .NET) (1) element.
The <Integrity> Element element defines digital signature requirements. The Usage attribute value of "Required" specifies that a signature is required, and additional requirements are specified in child elements.
The following code example defines a policy assertion named
policy-c0a22319-6b89-49ff-9b82-bdbac5f04618
and specifies that there are digital signature requirements.<wsp:Policy wsu:Id="policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" xmlns:wsp="https://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsa="https://schemas.xmlsoap.org/ws/2004/03/addressing"> <wssp:Integrity wsp:Usage="wsp:Required"
- Add a <Policy> Element (WSE for Microsoft .NET) (1) element to the policy file for the application. Add the <Policy> Element (WSE for Microsoft .NET) (1) element as a child element of the <policies> Element element.
Specify the token type by adding <TokenInfo> Element, <SecurityToken> Element, and <TokenType> Element elements.
- Add a <TokenInfo> Element child element to the <Integrity> Element element.
- Add a <SecurityToken> Element child element to the <TokenInfo> Element element.
- Add a <TokenType> Element child element to the <SecurityToken> Element element and set its value to
https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5ST
.
The <TokenType> Element element specifies the type of security token that must be used to create the digital signature.
The following code example specifies that a KerberosToken security token must be used to digitally sign the SOAP message.
<wssp:Integrity wsp:Usage="wsp:Required" xmlns:wssp="https://schemas.xmlsoap.org/ws/2002/12/secext"> <wssp:TokenInfo> <wssp:SecurityToken> <wssp:TokenType>https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5ST</wssp:TokenType>
Optionally, specify requirements about the token by adding <Claims> Element, <Role> Element, and <TokenIssuer> Element (WSE for Microsoft .NET) (1) elements
Add a <Claims> Element child element to the <SecurityToken> Element element.
Add a <Role> Element child element to the <Claims> Element element.
The <Role> Element element specifies the role the principal name specified in the Kerberos service ticket must be a member of.Note
Do not specify a <ServiceName> Element element for policy assertions that involve incoming SOAP messages and Kerberos service tickets. WSE does not verify that a KerberosToken security token matches the specified service principal name.
Add a <TokenIssuer> Element (WSE for Microsoft .NET) (1) child element to the <SecurityToken> Element element
The <TokenIssuer> Element (WSE for Microsoft .NET) (1) element specifies the domain, also known as the Kerberos realm, which issued the Kerberos ticket. WSE does not support Kerberos delegation, so this must be the same realm the recipient is a member of.Note
The value of the <TokenIssuer> Element (WSE for Microsoft .NET) (1) must be in uppercase.
The following code example specifies that a Kerberos service ticket must be used to sign SOAP messages. Furthermore, the Kerberos service ticket must be obtained from a KDC in the
COHOWINERY
realm for the computer namedcomputer1
.<wssp:Integrity wsp:Usage="wsp:Required" xmlns:wssp="https://schemas.xmlsoap.org/ws/2002/12/secext"> <wssp:TokenInfo> <SecurityToken xmlns="https://schemas.xmlsoap.org/ws/2002/12/secext"> <wssp:TokenType>https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5ST</wssp:TokenType> <wssp:TokenIssuer>COHOWINERY</wssp:TokenIssuer> <wssp:Claims> <wssp:ServiceName>host/computer1@cohowinery.com</wssp:ServiceName> </wssp:Claims> </SecurityToken> </wssp:TokenInfo>
Map the policy assertion to an endpoint by adding an <endpoint> Element element. Optionally, designate the policy as the default for all SOAP messages by adding the <defaultOperation> Element element.
- Add an <endpoint> Element element to the <mappings> Element element, and set the uri attribute value to the URI of the application.
The <endpoint> Element element maps a policy assertion to an endpoint. The uri attribute value specifies the URI of the service to which the policy is mapped. - Add a <defaultOperation> Element child element to the <endpoint> Element element.
The <defaultOperation> Element element specifies the default policy for all operations at the URI specified in the uri attribute. - Add <request> Element (WSE for Microsoft .NET), <response> Element (WSE for Microsoft .NET), and <fault> Element child elements to the <defaultOperation> Element element. The value of the policy attribute must match the value of the Id attribute of the <Policy> Element (WSE for Microsoft .NET) (1) element that defines the policy assertion.
The following code example sets the default policy for all SOAP messages to the
policy-c0a22319-6b89-49ff-9b82-bdbac5f04618
policy assertion.<mappings xmlns:wse="https://schemas.microsoft.com/wse/2003/06/Policy"> <endpoint uri="http://www.cohowinery.com/SaleWidgets.asmx"> <defaultOperation> <request policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" /> <response policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" /> <fault policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" /> </defaultOperation> </endpoint> </mappings>
- Add an <endpoint> Element element to the <mappings> Element element, and set the uri attribute value to the URI of the application.
Specify the XML elements to be signed by adding a <MessageParts> Element for <Integrity> Element element to the <Integrity> Element element.
- Add a <MessageParts> Element for <Integrity> Element child element to the <Integrity> Element element in the policy file for the application, and set the Dialect attribute value to
"https://schemas.xmlsoap.org/2002/12/wsse#part"
. - Specify the parts of the message to be signed by listing them, separated by spaces, as the value of the <MessageParts> Element for <Integrity> Element element.
The following code example specifies that the <Body> element and the To, Action, MessageID, and From headers are signed.
<wssp:MessageParts Dialect="https://schemas.xmlsoap.org/2002/12/wsse#part"> wsp:Body() wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) wsp:Header(wsa:From) </wssp:MessageParts>
- Add a <MessageParts> Element for <Integrity> Element child element to the <Integrity> Element element in the policy file for the application, and set the Dialect attribute value to
Specify that the XML elements that must be signed, exist in incoming SOAP messages.
Add a <MessagePredicate> Element child element to the <Policy> Element (WSE for Microsoft .NET) (1) element in the policy file for the application.
When using policy to require digital signatures on incoming SOAP messages, always use the <MessagePredicate> element. The <MessagePredicate> element ensures that the XML elements that must be signed actually exist in the SOAP message. Just adding an <Integrity> Element element to a policy file specifies that a digital signature must exist, if the XML elements specified in the <Integrity> element exist in the SOAP messages. If the SOAP message does not contain the XML elements specified in the <Integrity> element, the SOAP message satisfies the requirements of the policy and is allowed to access the Web service. To remedy this potential problem, add a <MessagePredicate> element specifying the XML elements that must exist in the incoming SOAP message.
Set the Dialect attribute value to"https://schemas.xmlsoap.org/2002/12/wsse#part"
.Specify the parts of the message that must exist by listing them, separated by spaces, as the value of the <MessagePredicate> Element element.
The following code example specifies that the <Body> element and the To, Action, MessageID, and From headers exist in the SOAP message.<wsp:MessagePredicate Dialect="https://schemas.xmlsoap.org/2002/12/wsse#part"> wsp:Body() wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) wsp:Header(wsa:From) </wsp:MessagePredicate>
To use code to require that incoming SOAP messages are signed using a Kerberos token and that the required XML elements are signed
Add a Web service method that processes the security context token.
In Solution Explorer, right-click Service1.asmx, and then click View Code.
Add the Imports or using directives to the Service1.asmx code-behind file as shown in the following code example.
Imports Microsoft.Web.Services2 Imports Microsoft.Web.Services2.Security Imports Microsoft.Web.Services2.Security.Tokens
using Microsoft.Web.Services2; using Microsoft.Web.Services2.Security; using Microsoft.Web.Services2.Security.Tokens;
In the
Service1
class, add code to verify that the <Body> element of the SOAP request was signed using a KerberosToken security token.
The following code example is a Web service method namedsayHello
that verifies the request was made using SOAP and calls the method namedIsMessageSigned
defined in the next step.<WebMethod()> _ Public Function sayHello() As String Dim requestContext As SoapContext = _ RequestSoapContext.Current ' Verify that a SOAP request was received. If requestContext Is Nothing Then Throw New ApplicationException( "Either a non-SOAP " + _ "request was received or WSE is not properly " + _ "installed for the Web application hosting the " + _ "Web service.") End If If Not IsMessageSigned(requestContext) Then Throw New ApplicationException("The request is not " + _ "signed using an acceptable signature.") End If Return "Hello" End Function
[WebMethod()] public string sayHello() { SoapContext requestContext = RequestSoapContext.Current; // Verify that a SOAP request was received. if (requestContext == null) { throw new ApplicationException( "Either a non-SOAP " + "request was received or WSE is not properly " + "installed for the Web application hosting the " + "Web service."); } // Verify that the SOAP message is signed. if (!IsMessageSigned(requestContext)) { throw new ApplicationException( "The request is not signed using an acceptable signature."); } return "Hello"; }
Define a method to verify a KerberosToken security token signed the expected XML elements of the SOAP message.
In Solution Explorer, right-click Service1.asmx, and then click View Code.
In the
Service1
class, add the code shown in the following code example.
The following code example verifies that a digital signature exists for a SoapContext and that it signed the <Body> element.Private Function IsMessageSigned(ByVal context As SoapContext) As Boolean Dim element As ISecurityElement For Each element In context.Security.Elements If (TypeOf (element) Is MessageSignature) Then ' The SoapContext contains a Signature element. Dim sign As MessageSignature = element If ((sign.SignatureOptions And _ (SignatureOptions.IncludeSoapBody Or _ SignatureOptions.IncludeTo Or _ SignatureOptions.IncludeAction Or _ SignatureOptions.IncludeMessageId Or _ SignatureOptions.IncludeFrom))) Then If TypeOf sign.SigningToken Is KerberosToken Then ' The SOAP message is signed using a Kerberos ' token. Return True End If End If End If Next Return False End Function
private bool IsMessageSigned(SoapContext context) { foreach (ISecurityElement element in context.Security.Elements) { if (element is MessageSignature) {
MessageSignature sign = element as MessageSignature; if ((sign.SignatureOptions & (SignatureOptions.IncludeSoapBody | SignatureOptions.IncludeTo | SignatureOptions.IncludeAction | SignatureOptions.IncludeMessageId | SignatureOptions.IncludeFrom))) { // The SOAP message is signed. if (sign.SigningToken is KerberosToken) // The SOAP message is signed by a KerberosToken. return true; } } } return false; }
Example
The following code example is a policy file specifying that all SOAP messages sent to the http://www.cohowinery.com/SaleWidgets.asmx
endpoint must have the <Body> element and the To, Action, MessageID, and From headers signed by a Kerberos service ticket issued from the COHOWINERY
realm for communication between the current user and the computer named computer1@cohowinery.com
.
Note
This code example is designed to demonstrate WSE features and is not intended for production use.
<?xml version="1.0" encoding="utf-8"?>
<policyDocument xmlns="https://schemas.microsoft.com/wse/2003/06/Policy">
<mappings xmlns:wse="https://schemas.microsoft.com/wse/2003/06/Policy">
<endpoint uri="http://www.cohowinery.com/SaleWidgets.asmx">
<defaultOperation>
<request policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" />
<response policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" />
<fault policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" />
</defaultOperation>
</endpoint>
</mappings>
<policies xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsp:Policy wsu:Id="policy-c0a22319-6b89-49ff-9b82-bdbac5f04618"
xmlns:wsp="https://schemas.xmlsoap.org/ws/2002/12/policy"
xmlns:wsa="https://schemas.xmlsoap.org/ws/2004/03/addressing" >
<wsp:MessagePredicate
Dialect="https://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body() wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) wsp:Header(wsa:From)
</wsp:MessagePredicate>
<wssp:Integrity wsp:Usage="wsp:Required"
xmlns:wssp="https://schemas.xmlsoap.org/ws/2002/12/secext">
<wssp:TokenInfo>
<SecurityToken xmlns="https://schemas.xmlsoap.org/ws/2002/12/secext">
<wssp:TokenType>https://schemas.xmlsoap.org/ws/2003/12/kerberos/Kerberosv5ST</wssp:TokenType>
<wssp:TokenIssuer>COHOWINERY</wssp:TokenIssuer>
<wssp:Claims>
<wssp:ServiceName>host/computer1@cohowinery.com</wssp:ServiceName>
</wssp:Claims>
</SecurityToken>
</wssp:TokenInfo>
<wssp:MessageParts Dialect="https://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body() wsp:Header(wsa:To) wsp:Header(wsa:Action) wsp:Header(wsa:MessageID) wsp:Header(wsa:From)
</wssp:MessageParts>
</wssp:Integrity>
</wsp:Policy>
</policies>
</policyDocument>
The following code example defines a Web service method that verifies that requests are made using SOAP and that the <Body> element and the To, Action, MessageID, and From headers are signed using a KerberosToken security token
Imports System
Imports System.Web.Services
Imports Microsoft.Web.Services2
Imports Microsoft.Web.Services2.Security
Imports Microsoft.Web.Services2.Security.Tokens
<WebService([Namespace] := "https://www.contoso.com")> _
Public Class StockService
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function sayHello() As String
Dim requestContext As SoapContext = _
RequestSoapContext.Current
' Verify that a SOAP request was received.
If requestContext Is Nothing Then
Throw New ApplicationException("Either a non-SOAP " + _
"request was received or WSE is not properly " + _
"installed for the Web application hosting the " + _
"Web service.")
End If
If Not IsMessageSigned(requestContext) Then
Throw New ApplicationException("The request is not " + _
"signed using an acceptable signature.")
End If
Return "Hello"
End Function
Private Function IsMessageSigned(ByVal context As SoapContext) As Boolean
Dim element As ISecurityElement
For Each element In context.Security.Elements
If (TypeOf (element) Is MessageSignature) Then
' The SoapContext contains a Signature element.
Dim sign As MessageSignature = element
If ((sign.SignatureOptions And _
(SignatureOptions.IncludeSoapBody Or _
SignatureOptions.IncludeTo Or _
SignatureOptions.IncludeAction Or _
SignatureOptions.IncludeMessageId Or _
SignatureOptions.IncludeFrom)) = _
(SignatureOptions.IncludeSoapBody Or _
SignatureOptions.IncludeTo Or _
SignatureOptions.IncludeAction Or _
SignatureOptions.IncludeMessageId Or _
SignatureOptions.IncludeFrom)) Then
If TypeOf sign.SigningToken Is KerberosToken Then
' The SOAP message is signed using a Kerberos token.
Return True
End If
End If
End If
Next
Return False
End Function
End Class
using System;
using System.Web.Services;
using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Security;
using Microsoft.Web.Services2.Security.Tokens;
public class HelloService : System.Web.Services.WebService
{
[WebMethod]
public string sayHello()
{
SoapContext requestContext = RequestSoapContext.Current;
// Verify that a SOAP request was received.
if (requestContext == null)
{
throw new ApplicationException( "Either a non-SOAP " +
"request was received or WSE is not properly " +
"installed for the Web application hosting the " +
"Web service.");
}
// Verify that the SOAP message is signed.
if (!IsMessageSigned(requestContext))
{
throw new ApplicationException(
"The request is not signed using an acceptable signature.");
}
return "Hello";
}
private bool IsMessageSigned(SoapContext context)
{
foreach (ISecurityElement element in context.Security.Elements)
{
if (element is MessageSignature)
{
// The given context contains a Signature element.
MessageSignature sign = element as MessageSignature;
if ((sign.SignatureOptions &
(SignatureOptions.IncludeSoapBody |
SignatureOptions.IncludeTo |
SignatureOptions.IncludeAction |
SignatureOptions.IncludeMessageId |
SignatureOptions.IncludeFrom)) ==
(SignatureOptions.IncludeSoapBody |
SignatureOptions.IncludeTo |
SignatureOptions.IncludeAction |
SignatureOptions.IncludeMessageId |
SignatureOptions.IncludeFrom))
{
// The SOAP message is signed.
if (sign.SigningToken is KerberosToken)
// The SOAP message is signed by a KerberosToken.
return true;
}
}
}
return false;
}
}
See Also
Tasks
How to: Sign a SOAP Message By Using a Kerberos Ticket