How to: Create a Custom Policy Assertion That Supports Secure Conversations
To establish a secure conversation, either set the EstablishSecurityContext attribute to true for a turnkey security assertion or use a custom policy assertion that supports secure conversations. A custom policy assertion that supports secure conversations has filters that derive from the secure conversation filter classes, such as the SecureConversationServiceReceiveSecurityFilter class. This topic details how to create a custom policy assertion that supports secure conversations. For more information on the turnkey security assertions, see Turnkey Security Assertions.
To create a custom policy assertion that supports secure conversations
Open the project containing the Web service or Web service client using Visual Studio 2005.
Add a new class to the project.
- In Solution Explorer, right-click the project name, and then click Add New Item….
- Select Class and provide a name for the class.
- Click Add to close the dialog box and add the class to the project.
Add references to the Microsoft.Web.Services3, System.Web.Services, System.Security, and System.Xml assemblies.
- In Solution Explorer, right-click the project name, and then click Add Reference.
- Click the .NET tab, press and hold the CTRL key, and click Microsoft.Web.Services3.dll, System.Web.Services.dll, System.Security.dll, and System.Xml.dll.
- Click OK to close the dialog box.
Add the Imports statements or using directives that are shown in the following code example to the top of the file for the new class.
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Security.Cryptography.X509Certificates Imports System.Xml Imports Microsoft.Web.Services3 Imports Microsoft.Web.Services3.Design Imports Microsoft.Web.Services3.Security Imports Microsoft.Web.Services3.Security.Tokens
using System; using System.Collections.Generic; using System.Text; using System.Security.Cryptography.X509Certificates; using System.Xml; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; using Microsoft.Web.Services3.Security.Tokens;
Create a custom input filter for the Web service.
Add a class that derives from the SecureConversationServiceReceiveSecurityFilter class.
The following code example defines a class that derives from the SecureConversationServiceReceiveSecurityFilter class.Class CustomSecurityServerInputFilter Inherits SecureConversationServiceReceiveSecurityFilter
class CustomSecurityServerInputFilter : SecureConversationServiceReceiveSecurityFilter {
Specify the security requirements for incoming SOAP requests to the Web service that are not secured using a secure conversation by overriding the ValidateMessageSecurity method.
When the EstablishSecurityContext property is set to true, a secure conversation is established between the client and the Web service. Security for security token requests are validated using the ValidateMessageSecurity method (takes 3 parameters). Other SOAP requests are validated using the ValidateMessageSecurity method (2 parameters), which cannot be overridden.
When a secure conversation is not established, the ValidateMessageSecurity method (3 parameters) validates SOAP requests received by the Web service.
The following code example specifies that SOAP messages that are sent to the Web service that are not part of a secure conversation must be signed and encrypted. The security tokens that are used to sign and encrypt the SOAP message are saved in the OperationState property, which can be accessed later by a Web service's custom output filter if a SOAP response is returned to the client.Public Overrides Sub ValidateMessageSecurity(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal response As MessageProtectionRequirements) Dim clientToken As SecurityToken = Nothing Dim serverToken As SecurityToken = Nothing ' Ensure incoming SOAP messages are signed and encrypted. Dim elem As ISecurityElement For Each elem In security.Elements If TypeOf elem Is MessageSignature Then Dim sig As MessageSignature = CType(elem, MessageSignature) clientToken = sig.SigningToken End If If TypeOf elem Is EncryptedData Then Dim enc As EncryptedData = CType(elem, EncryptedData) serverToken = enc.SecurityToken End If Next elem If clientToken Is Nothing OrElse serverToken Is Nothing Then Throw New Exception("Incoming message did not meet security requirements") End If Dim state As New RequestState(clientToken, serverToken) envelope.Context.OperationState.Set(state) End Sub 'ValidateMessageSecurity
public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security, MessageProtectionRequirements response) { SecurityToken clientToken = null; SecurityToken serverToken = null; // Ensure incoming SOAP messages are signed and encrypted. foreach (ISecurityElement elem in security.Elements) { if (elem is MessageSignature) { MessageSignature sig = (MessageSignature)elem; clientToken = sig.SigningToken; } if (elem is EncryptedData) { EncryptedData enc = (EncryptedData)elem; serverToken = enc.SecurityToken; } } if (clientToken == null || serverToken == null) throw new Exception("Incoming message did not meet security requirements"); RequestState state = new RequestState(clientToken, serverToken); envelope.Context.OperationState.Set(state); }
Create a custom output filter for the Web service.
Add a class that derives from the SecureConversationServiceSendSecurityFilter class.
The following example defines a class that derives from the SecureConversationServiceSendSecurityFilter class.Class CustomSecurityServerOutputFilter Inherits SecureConversationServiceSendSecurityFilter
class CustomSecurityServerOutputFilter : SecureConversationServiceSendSecurityFilter {
Secure SOAP responses that are sent from the Web service by overriding the SecureMessage method.
When the EstablishSecurityContext property is set to true, a secure conversation is established between the client and the Web service. Security for security token responses are secured using the SecureMessage method (takes 3 parameters). Other SOAP responses are secured using the SecureMessage method (2 parameters), which cannot be overridden.
When a secure conversation is not established, the SecureMessage method (3 parameters) validates SOAP responses sent by the Web service.
The following code example signs and encrypts SOAP responses using the security tokens that encrypted and signed the SOAP request, respectively. The security tokens are retrieved from the OperationState property that was set by the Web service's custom input filter.Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements) Dim state As RequestState = envelope.Context.OperationState.Get(Of RequestState)() ' Sign the message with the Web service's security token. security.Tokens.Add(state.ServerToken) security.Elements.Add(New MessageSignature(state.ServerToken)) ' Encrypt the message with the client's security token. security.Elements.Add(New EncryptedData(state.ClientToken)) End Sub 'SecureMessage
public override void SecureMessage(SoapEnvelope envelope, Security security, MessageProtectionRequirements request) { RequestState state = envelope.Context.OperationState.Get<RequestState>(); // Sign the message with the Web service's security token. security.Tokens.Add(state.ServerToken); security.Elements.Add(new MessageSignature(state.ServerToken)); // Encrypt the message with the client's security token. security.Elements.Add(new EncryptedData(state.ClientToken)); }
Create a custom input filter for the client.
Add a class that derives from the SecureConversationClientReceiveSecurityFilter class.
The following code example defines a class that derives from the SecureConversationClientReceiveSecurityFilter class.Class CustomSecurityClientInputFilter Inherits SecureConversationClientReceiveSecurityFilter
class CustomSecurityClientInputFilter : SecureConversationClientReceiveSecurityFilter {
Enforce the security requirements for SOAP responses that are received by the client by overriding the ValidateMessageSecurity method.
When the EstablishSecurityContext property is set to true, a secure conversation is established between the client and the Web service. Security for security token responses are validated using the ValidateMessageSecurity method (takes 3 parameters). Other SOAP responses are secured using the ValidateMessageSecurity method (2 parameters), which cannot be overridden.
When a secure conversation is not established, the ValidateMessageSecurity method (3 parameters) validates SOAP responses that are received by the client.
The following code example ensures that SOAP responses are signed using the security token that is used to encrypt the SOAP request and that is encrypted by the security token that signed the SOAP request. The security tokens are retrieved from the OperationState property that was set by the client's custom output filter.Public Overrides Sub ValidateMessageSecurity(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements) Dim state As RequestState Dim signed As Boolean = False Dim encrypted As Boolean = False ' Get the request state out of the operation state. state = envelope.Context.OperationState.Get(Of RequestState)() ' Make sure the message was signed with the server's security token. Dim elem As ISecurityElement For Each elem In security.Elements If TypeOf elem Is MessageSignature Then Dim sig As MessageSignature = CType(elem, MessageSignature) If sig.SigningToken.Equals(state.ServerToken) Then signed = True End If End If If TypeOf elem Is EncryptedData Then Dim enc As EncryptedData = CType(elem, EncryptedData) If enc.SecurityToken.Equals(state.ClientToken) Then encrypted = True End If End If Next elem If Not signed OrElse Not encrypted Then Throw New Exception("Response message does not meet security requirements") End If End Sub 'ValidateMessageSecurity
public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security, MessageProtectionRequirements request) { RequestState state; bool signed = false; bool encrypted = false; // Get the request state out of the operation state. state = envelope.Context.OperationState.Get<RequestState>(); // Make sure the message was signed with the server's security token. foreach (ISecurityElement elem in security.Elements) { if (elem is MessageSignature) { MessageSignature sig = (MessageSignature)elem; if (sig.SigningToken.Equals(state.ServerToken)) signed = true; } if (elem is EncryptedData) { EncryptedData enc = (EncryptedData)elem; if (enc.SecurityToken.Equals(state.ClientToken)) encrypted = true; } } if (!signed || !encrypted) throw new Exception("Response message does not meet security requirements"); }
Override the GetUnattachedTokensCore method.
The GetUnattachedTokensCore method retrieves security tokens that a Web service did not include in SOAP responses that are not secured using a secure conversation.
The following code example returns the security tokens that were used to protect the SOAP request.Public Overrides Function GetUnattachedTokensCore(ByVal context As SoapContext) As IEnumerable(Of SecurityToken) Return New SecurityToken() {Me.serviceX509Token, Me.clientX509Token} End Function
public override IEnumerable<SecurityToken> GetUnattachedTokensCore(SoapContext context) { return new SecurityToken[] {this.serviceX509Token, this.clientX509Token }; }
Create a custom output filter for the client.
Add a class that derives from the SecureConversationClientSendSecurityFilter class.
The following code example defines a class that derives from the SecureConversationClientSendSecurityFilter class.Class CustomSecurityClientOutputFilter Inherits SecureConversationClientSendSecurityFilter
class CustomSecurityClientOutputFilter : SecureConversationClientSendSecurityFilter {
Secure SOAP responses that are sent from the client by overriding the SecureMessage method.
When the EstablishSecurityContext property is set to true, a secure conversation is established between the client and the Web service. Security for security token requests are secured using the SecureMessage method (takes 3 parameters). Other SOAP requests are secured using the SecureMessage method (2 parameters), which cannot be overridden.
When a secure conversation is not established, the SecureMessage method (3 parameters) secures SOAP responses sent by the Web service.
The following code example signs and encrypts SOAP responses using the security tokens that are created in the custom filter's constructor. The security tokens that are used to sign and encrypt the SOAP message are saved in the OperationState property, which can be accessed later by a client's custom output filter to verify security requirements on SOAP responses that are returned by the Web service.Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements) If envelope Is Nothing Then Throw New ArgumentNullException("envelope") End If If security Is Nothing Then Throw New ArgumentNullException("security") End If If request Is Nothing Then Throw New ArgumentNullException("request") End If ' Sign the SOAP message with the client's security token. security.Tokens.Add(clientToken) security.Elements.Add(New MessageSignature(clientToken)) ' Encrypt the SOAP message with the client's security token. security.Elements.Add(New EncryptedData(serverToken)) ' Encrypt the client's security token with the server's security token. security.Elements.Add(New EncryptedData(serverToken, "#" + clientToken.Id)) ' Store the client and server security tokens in the request state. Dim state As New RequestState(clientToken, serverToken) ' Store the request state in the proxy's operation state. ' This makes these tokens accessible when SOAP responses are ' verified to have sufficient security requirements. envelope.Context.OperationState.Set(state) End Sub 'SecureMessage
public override void SecureMessage(SoapEnvelope envelope, Security security, MessageProtectionRequirements request) { if (envelope == null) throw new ArgumentNullException("envelope"); if (security == null) throw new ArgumentNullException("security"); if (request == null) throw new ArgumentNullException("request"); // Sign the SOAP message with the client's security token. security.Tokens.Add(clientToken); security.Elements.Add(new MessageSignature(clientToken)); // Encrypt the SOAP message with the client's security token. security.Elements.Add(new EncryptedData(serverToken)); // Encrypt the client's security token with the server's security token. security.Elements.Add(new EncryptedData(serverToken, "#" + clientToken.Id)); // Store the client and server security tokens in the request state. RequestState state = new RequestState(clientToken, serverToken); // Store the request state in the proxy's operation state. // This makes these tokens accessible when SOAP responses are // verified to have sufficient security requirements. envelope.Context.OperationState.Set(state); }
Create a class that represents the custom policy assertion.
Create a class that derives from the SecurityPolicyAssertion class.
The following code example defines a class that derives from the SecurityPolicyAssertion class.Public Class CustomSecurityAssertion Inherits SecurityPolicyAssertion
public class CustomSecurityAssertion : SecurityPolicyAssertion {
Specify the Web service's custom input filter by overriding the CreateServiceInputFilter method.
When a custom policy assertion does not enforce security on SOAP requests that are sent to the Web service, set the return value of the CreateServiceInputFilter method to a null reference (Nothing in Visual Basic).
The following code example adds a custom input filter for the Web service.Public Overrides Function CreateServiceInputFilter(ByVal context As FilterCreationContext) As SoapFilter Return New CustomSecurityServerInputFilter(Me) End Function 'CreateServiceInputFilter
public override SoapFilter CreateServiceInputFilter(FilterCreationContext context) { return new CustomSecurityServerInputFilter(this); }
Specify the Web service's custom output filter by overriding the CreateServiceOutputFilter method.
When a custom policy assertion does not enforce security on SOAP responses that are sent by the Web service, set the return value of the CreateServiceOutputFilter method to a null reference (Nothing in Visual Basic).
The following code example adds a custom output filter for the Web service.Public Overrides Function CreateServiceOutputFilter(ByVal context As FilterCreationContext) As SoapFilter Return New CustomSecurityServerOutputFilter(Me) End Function 'CreateServiceOutputFilter
public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context) { return new CustomSecurityServerOutputFilter(this); }
Specify the client's custom input filter by overriding the CreateClientInputFilter method.
When a custom policy assertion does not enforce security on SOAP responses that are received by the client, set the return value of the CreateClientInputFilter method to a null reference (Nothing in Visual Basic)
The following code example adds a custom input filter for the client.Public Overrides Function CreateClientInputFilter(ByVal context As FilterCreationContext) As SoapFilter Return New CustomSecurityClientInputFilter(Me) End Function 'CreateClientInputFilter
public override SoapFilter CreateClientInputFilter(FilterCreationContext context) { return new CustomSecurityClientInputFilter(this); }
Specify the client's custom output filter by overriding the CreateClientOutputFilter method.
When a custom policy assertion does not enforce security on SOAP requests that are sent by the client, set the return value of the CreateClientOutputFilter method to a null reference (Nothing in Visual Basic).
The following code example adds a custom output filter for the client.Public Overrides Function CreateClientOutputFilter(ByVal context As FilterCreationContext) As SoapFilter Return New CustomSecurityClientOutputFilter(Me) End Function 'CreateClientOutputFilter
public override SoapFilter CreateClientOutputFilter(FilterCreationContext context) { return new CustomSecurityClientOutputFilter(this); }
Add code to parse the custom XML elements and attributes that can be placed in a policy file for this custom policy assertion by overriding the ReadXml method.
Like the turnkey security assertions, custom policy assertions can optionally provide an XML element that is used to specify the custom policy assertion's options in a policy file. The XML element can contain child elements and attributes that specify the options for the custom policy assertion.
A System.Xml.XmlReader is passed into the ReadXml method with the reader positioned on the XML element for the custom policy assertion. The name of the XML element is specified when the custom policy assertion is registered in the policy file using an <extension> Element. Use the System.Xml.XmlReader to retrieve the custom policy assertion's options that are set in the policy file.
In the ReadXml method call the ReadAttributes method to have the SecurityPolicyAssertion class parse the XML attributes natively supported by that class, including the EstablishSecurityContext attribute.
Typically, it is best to provide support for reading an XML element from a policy file for the custom policy assertion, so that policy for the application can be configured at deployment time to match the application's environment.
The following code example verifies that the current element is namedCustomSecurityAssertion
and then reads it. To see an example policy file with the custom policy assertion, see How to: Secure an Application Using a Custom Policy Assertion.Public Overrides Sub ReadXml(ByVal reader As System.Xml.XmlReader, ByVal extensions As IDictionary(Of String, Type)) If (True) Then Dim isEmpty As Boolean = reader.IsEmptyElement MyBase.ReadAttributes(reader) reader.ReadStartElement("CustomSecurityAssertion") If Not isEmpty Then If reader.MoveToContent() = XmlNodeType.Element AndAlso reader.Name = "clientToken" Then reader.ReadStartElement() reader.MoveToContent() ' Get the registed security token provider for X.509 certificate security credentials. Dim type As Type = extensions(reader.Name) Dim instance As Object = Activator.CreateInstance(type) If instance Is Nothing Then Throw New InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName)) End If Dim clientProvider As TokenProvider(Of X509SecurityToken) = CType(instance, TokenProvider(Of X509SecurityToken)) ' Read the child elements that provide the details about the client's X.509 certificate. clientProvider.ReadXml(reader, extensions) Me.ClientX509TokenProvider = clientProvider reader.ReadEndElement() End If If reader.MoveToContent() = XmlNodeType.Element AndAlso reader.Name = "serviceToken" Then reader.ReadStartElement() reader.MoveToContent() ' Get the registed security token provider for X.509 certificate security credentials. Dim type As Type = extensions(reader.Name) Dim instance As Object = Activator.CreateInstance(type) If instance Is Nothing Then Throw New InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName)) End If Dim serviceProvider As TokenProvider(Of X509SecurityToken) = CType(instance, TokenProvider(Of X509SecurityToken)) ' Read the child elements that provide the details about the Web service's X.509 certificate. serviceProvider.ReadXml(reader, extensions) Me.ServiceX509TokenProvider = serviceProvider reader.ReadEndElement() End If MyBase.ReadElements(reader, extensions) reader.ReadEndElement() End If End If End Sub
public override void ReadXml(System.Xml.XmlReader reader, IDictionary<string, Type> extensions) { bool isEmpty = reader.IsEmptyElement; base.ReadAttributes(reader); reader.ReadStartElement("CustomSecurityAssertion"); if (!isEmpty) { if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "clientToken") { reader.ReadStartElement(); reader.MoveToContent(); // Get the registed security token provider for X.509 certificate security credentials. Type type = extensions[reader.Name]; object instance = Activator.CreateInstance(type); if (instance == null) throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName)); TokenProvider<X509SecurityToken> clientProvider = instance as TokenProvider<X509SecurityToken>; // Read the child elements that provide the details about the client's X.509 certificate. clientProvider.ReadXml(reader, extensions); this.ClientX509TokenProvider = clientProvider; reader.ReadEndElement(); } if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "serviceToken") { reader.ReadStartElement(); reader.MoveToContent(); // Get the registed security token provider for X.509 certificate security credentials. Type type = extensions[reader.Name]; object instance = Activator.CreateInstance(type); if (instance == null) throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName)); TokenProvider<X509SecurityToken> serviceProvider = instance as TokenProvider<X509SecurityToken>; // Read the child elements that provide the details about the Web service's X.509 certificate. serviceProvider.ReadXml(reader, extensions); this.ServiceX509TokenProvider = serviceProvider; reader.ReadEndElement(); } base.ReadElements(reader, extensions); reader.ReadEndElement(); } }
Add code to return the set of policy extensions supported by the custom policy assertion by overriding the GetExtensions method.
Override the GetExtensions method to associate the name of the XML element that is used to represent the policy assertion in a policy file with the type that implements the policy assertion.Public Overrides Function GetExtensions() As IEnumerable(Of KeyValuePair(Of String, Type)) ' Add the CustomSecurityAssertion custom policy assertion to the list of registered ' policy extensions. Dim extensions As New List(Of KeyValuePair(Of String, Type)) extensions.Add(New KeyValuePair(Of String, Type)("CustomSecurityAssertion", Me.GetType())) If Not (serviceX509TokenProviderValue Is Nothing) Then Dim innerExtensions As IEnumerable(Of KeyValuePair(Of String, Type)) = serviceX509TokenProviderValue.GetExtensions() ' Add any policy extensions that read child elements of the <serviceToken> element ' to the list of registered policy extensions. If Not (innerExtensions Is Nothing) Then Dim extension As KeyValuePair(Of String, Type) For Each extension In innerExtensions extensions.Add(extension) Next End If End If If Not (clientX509TokenProviderValue Is Nothing) Then ' Add any policy extensions that read child elements of the <clientToken> element ' to the list of registered policy extensions. Dim innerExtensions As IEnumerable(Of KeyValuePair(Of String, Type)) = clientX509TokenProviderValue.GetExtensions() If Not (innerExtensions Is Nothing) Then Dim extension As KeyValuePair(Of String, Type) For Each extension In innerExtensions extensions.Add(extension) Next End If End If Return extensions End Function
public override IEnumerable<KeyValuePair<string, Type>> GetExtensions() { // Add the CustomSecurityAssertion custom policy assertion to the list of registered // policy extensions. List<KeyValuePair<string, Type>> extensions = new List<KeyValuePair<string, Type>>(); extensions.Add(new KeyValuePair<string, Type>("CustomSecurityAssertion", this.GetType())); if (serviceX509TokenProviderValue != null) { // Add any policy extensions that read child elements of the <serviceToken> element // to the list of registered policy extensions. IEnumerable<KeyValuePair<string, Type>> innerExtensions = serviceX509TokenProviderValue.GetExtensions(); if (innerExtensions != null) { foreach (KeyValuePair<string, Type> extension in innerExtensions) { extensions.Add(extension); } } } if (clientX509TokenProviderValue != null) { // Add any policy extensions that read child elements of the <clientToken> element // to the list of registered policy extensions. IEnumerable<KeyValuePair<string, Type>> innerExtensions = clientX509TokenProviderValue.GetExtensions(); if (innerExtensions != null) { foreach (KeyValuePair<string, Type> extension in innerExtensions) { extensions.Add(extension); } } } return extensions; }
Example
The following code example demonstrates how to create a custom policy assertion that supports secure conversations.
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Security.Cryptography.X509Certificates
Imports System.Xml
Imports Microsoft.Web.Services3
Imports Microsoft.Web.Services3.Design
Imports Microsoft.Web.Services3.Security
Imports Microsoft.Web.Services3.Security.Tokens
Namespace CustomPolicyAssertions
Public Class CustomSecurityAssertion
Inherits SecurityPolicyAssertion
Dim serviceX509TokenProviderValue As TokenProvider(Of X509SecurityToken)
Dim clientX509TokenProviderValue As TokenProvider(Of X509SecurityToken)
Public Property ClientX509TokenProvider() As TokenProvider(Of X509SecurityToken)
Get
Return clientX509TokenProviderValue
End Get
Set(ByVal value As TokenProvider(Of X509SecurityToken))
clientX509TokenProviderValue = value
End Set
End Property
Property ServiceX509TokenProvider() As TokenProvider(Of X509SecurityToken)
Get
Return serviceX509TokenProviderValue
End Get
Set(ByVal value As TokenProvider(Of X509SecurityToken))
serviceX509TokenProviderValue = value
End Set
End Property
Public Sub New()
End Sub 'New
Public Overrides Function CreateClientOutputFilter(ByVal context As FilterCreationContext) As SoapFilter
Return New CustomSecurityClientOutputFilter(Me)
End Function 'CreateClientOutputFilter
Public Overrides Function CreateClientInputFilter(ByVal context As FilterCreationContext) As SoapFilter
Return New CustomSecurityClientInputFilter(Me)
End Function 'CreateClientInputFilter
Public Overrides Function CreateServiceInputFilter(ByVal context As FilterCreationContext) As SoapFilter
Return New CustomSecurityServerInputFilter(Me)
End Function 'CreateServiceInputFilter
Public Overrides Function CreateServiceOutputFilter(ByVal context As FilterCreationContext) As SoapFilter
Return New CustomSecurityServerOutputFilter(Me)
End Function 'CreateServiceOutputFilter
Public Overrides Sub ReadXml(ByVal reader As System.Xml.XmlReader, ByVal extensions As IDictionary(Of String, Type))
If (True) Then
Dim isEmpty As Boolean = reader.IsEmptyElement
MyBase.ReadAttributes(reader)
reader.ReadStartElement("CustomSecurityAssertion")
If Not isEmpty Then
If reader.MoveToContent() = XmlNodeType.Element AndAlso reader.Name = "clientToken" Then
reader.ReadStartElement()
reader.MoveToContent()
' Get the registed security token provider for X.509 certificate security credentials.
Dim type As Type = extensions(reader.Name)
Dim instance As Object = Activator.CreateInstance(type)
If instance Is Nothing Then
Throw New InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName))
End If
Dim clientProvider As TokenProvider(Of X509SecurityToken) = CType(instance, TokenProvider(Of X509SecurityToken))
' Read the child elements that provide the details about the client's X.509 certificate.
clientProvider.ReadXml(reader, extensions)
Me.ClientX509TokenProvider = clientProvider
reader.ReadEndElement()
End If
If reader.MoveToContent() = XmlNodeType.Element AndAlso reader.Name = "serviceToken" Then
reader.ReadStartElement()
reader.MoveToContent()
' Get the registed security token provider for X.509 certificate security credentials.
Dim type As Type = extensions(reader.Name)
Dim instance As Object = Activator.CreateInstance(type)
If instance Is Nothing Then
Throw New InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName))
End If
Dim serviceProvider As TokenProvider(Of X509SecurityToken) = CType(instance, TokenProvider(Of X509SecurityToken))
' Read the child elements that provide the details about the Web service's X.509 certificate.
serviceProvider.ReadXml(reader, extensions)
Me.ServiceX509TokenProvider = serviceProvider
reader.ReadEndElement()
End If
MyBase.ReadElements(reader, extensions)
reader.ReadEndElement()
End If
End If
End Sub
Public Overrides Function GetExtensions() As IEnumerable(Of KeyValuePair(Of String, Type))
' Add the CustomSecurityAssertion custom policy assertion to the list of registered
' policy extensions.
Dim extensions As New List(Of KeyValuePair(Of String, Type))
extensions.Add(New KeyValuePair(Of String, Type)("CustomSecurityAssertion", Me.GetType()))
If Not (serviceX509TokenProviderValue Is Nothing) Then
Dim innerExtensions As IEnumerable(Of KeyValuePair(Of String, Type)) = serviceX509TokenProviderValue.GetExtensions()
' Add any policy extensions that read child elements of the <serviceToken> element
' to the list of registered policy extensions.
If Not (innerExtensions Is Nothing) Then
Dim extension As KeyValuePair(Of String, Type)
For Each extension In innerExtensions
extensions.Add(extension)
Next
End If
End If
If Not (clientX509TokenProviderValue Is Nothing) Then
' Add any policy extensions that read child elements of the <clientToken> element
' to the list of registered policy extensions.
Dim innerExtensions As IEnumerable(Of KeyValuePair(Of String, Type)) = clientX509TokenProviderValue.GetExtensions()
If Not (innerExtensions Is Nothing) Then
Dim extension As KeyValuePair(Of String, Type)
For Each extension In innerExtensions
extensions.Add(extension)
Next
End If
End If
Return extensions
End Function
Public Overrides Sub WriteXml(ByVal writer As System.Xml.XmlWriter)
If writer Is Nothing Then
Throw New ArgumentNullException("writer")
End If
writer.WriteStartElement("CustomSecurityAssertion")
MyBase.WriteAttributes(writer)
If Not (Me.clientX509TokenProviderValue Is Nothing) Then
writer.WriteStartElement("clientToken")
Me.clientX509TokenProviderValue.WriteXml(writer)
writer.WriteEndElement()
End If
If Not (Me.serviceX509TokenProviderValue Is Nothing) Then
writer.WriteStartElement("serviceToken")
Me.serviceX509TokenProviderValue.WriteXml(writer)
writer.WriteEndElement()
End If
MyBase.WriteElements(writer)
writer.WriteEndElement()
End Sub 'WriteXml
Class RequestState
Private clientTokenValue As SecurityToken
Private serverTokenValue As SecurityToken
Public Sub New(ByVal cToken As SecurityToken, ByVal sToken As SecurityToken)
clientTokenValue = cToken
serverTokenValue = sToken
End Sub 'New
Public ReadOnly Property ClientToken() As SecurityToken
Get
Return clientTokenValue
End Get
End Property
Public ReadOnly Property ServerToken() As SecurityToken
Get
Return serverTokenValue
End Get
End Property
End Class 'RequestState
Class CustomSecurityServerInputFilter
Inherits SecureConversationServiceReceiveSecurityFilter
Public Sub New(ByVal parentAssertion As CustomSecurityAssertion)
MyBase.New(parentAssertion)
End Sub 'New
Public Overrides Sub ValidateMessageSecurity(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal response As MessageProtectionRequirements)
Dim clientToken As SecurityToken = Nothing
Dim serverToken As SecurityToken = Nothing
' Ensure incoming SOAP messages are signed and encrypted.
Dim elem As ISecurityElement
For Each elem In security.Elements
If TypeOf elem Is MessageSignature Then
Dim sig As MessageSignature = CType(elem, MessageSignature)
clientToken = sig.SigningToken
End If
If TypeOf elem Is EncryptedData Then
Dim enc As EncryptedData = CType(elem, EncryptedData)
serverToken = enc.SecurityToken
End If
Next elem
If clientToken Is Nothing OrElse serverToken Is Nothing Then
Throw New Exception("Incoming message did not meet security requirements")
End If
Dim state As New RequestState(clientToken, serverToken)
envelope.Context.OperationState.Set(state)
End Sub 'ValidateMessageSecurity
End Class 'CustomSecurityServerInputFilter
Class CustomSecurityServerOutputFilter
Inherits SecureConversationServiceSendSecurityFilter
Public Sub New(ByVal parentAssertion As CustomSecurityAssertion)
MyBase.New(parentAssertion)
End Sub 'New
Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements)
Dim state As RequestState = envelope.Context.OperationState.Get(Of RequestState)()
' Sign the message with the Web service's security token.
security.Tokens.Add(state.ServerToken)
security.Elements.Add(New MessageSignature(state.ServerToken))
' Encrypt the message with the client's security token.
security.Elements.Add(New EncryptedData(state.ClientToken))
End Sub 'SecureMessage
End Class 'CustomSecurityServerOutputFilter
Class CustomSecurityClientInputFilter
Inherits SecureConversationClientReceiveSecurityFilter
Private serviceX509Token As X509SecurityToken
Private clientX509Token As X509SecurityToken
Public Sub New(ByVal assertion As CustomSecurityAssertion)
MyBase.New(assertion)
If Not (assertion.ServiceX509TokenProvider Is Nothing) Then
Me.serviceX509Token = assertion.ServiceX509TokenProvider.GetToken()
End If
If Not (assertion.ClientX509TokenProvider Is Nothing) Then
Me.clientX509Token = assertion.ClientX509TokenProvider.GetToken()
End If
End Sub 'New
Public Overrides Sub ValidateMessageSecurity(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements)
Dim state As RequestState
Dim signed As Boolean = False
Dim encrypted As Boolean = False
' Get the request state out of the operation state.
state = envelope.Context.OperationState.Get(Of RequestState)()
' Make sure the message was signed with the server's security token.
Dim elem As ISecurityElement
For Each elem In security.Elements
If TypeOf elem Is MessageSignature Then
Dim sig As MessageSignature = CType(elem, MessageSignature)
If sig.SigningToken.Equals(state.ServerToken) Then
signed = True
End If
End If
If TypeOf elem Is EncryptedData Then
Dim enc As EncryptedData = CType(elem, EncryptedData)
If enc.SecurityToken.Equals(state.ClientToken) Then
encrypted = True
End If
End If
Next elem
If Not signed OrElse Not encrypted Then
Throw New Exception("Response message does not meet security requirements")
End If
End Sub 'ValidateMessageSecurity
Public Overrides Function GetUnattachedTokensCore(ByVal context As SoapContext) As IEnumerable(Of SecurityToken)
Return New SecurityToken() {Me.serviceX509Token, Me.clientX509Token}
End Function
End Class 'CustomSecurityClientInputFilter
Class CorrelationState
Inherits SerializableTokenWrapper(Of EncryptedKeyToken)
Public Sub New()
MyBase.New()
End Sub 'New
Public Sub New(ByVal token As EncryptedKeyToken)
MyBase.New(token)
End Sub 'New
End Class 'CorrelationState
Class CustomSecurityClientOutputFilter
Inherits SecureConversationClientSendSecurityFilter
Private clientToken As SecurityToken
Private serverToken As SecurityToken
Public Sub New(ByVal parentAssertion As CustomSecurityAssertion)
MyBase.New(parentAssertion)
' Get the client security token.
Dim clientX509Provider As New X509TokenProvider(StoreLocation.CurrentUser, StoreName.My, "CN=WSE2QuickStartClient")
clientToken = clientX509Provider.GetToken()
' Get the server security token.
Dim serverX509Provider As New X509TokenProvider(StoreLocation.LocalMachine, StoreName.My, "CN=WSE2QuickStartServer")
serverToken = serverX509Provider.GetToken()
End Sub 'New
Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security, ByVal request As MessageProtectionRequirements)
If envelope Is Nothing Then
Throw New ArgumentNullException("envelope")
End If
If security Is Nothing Then
Throw New ArgumentNullException("security")
End If
If request Is Nothing Then
Throw New ArgumentNullException("request")
End If
' Sign the SOAP message with the client's security token.
security.Tokens.Add(clientToken)
security.Elements.Add(New MessageSignature(clientToken))
' Encrypt the SOAP message with the client's security token.
security.Elements.Add(New EncryptedData(serverToken))
' Encrypt the client's security token with the server's security token.
security.Elements.Add(New EncryptedData(serverToken, "#" + clientToken.Id))
' Store the client and server security tokens in the request state.
Dim state As New RequestState(clientToken, serverToken)
' Store the request state in the proxy's operation state.
' This makes these tokens accessible when SOAP responses are
' verified to have sufficient security requirements.
envelope.Context.OperationState.Set(state)
End Sub 'SecureMessage
End Class 'CustomSecurityClientOutputFilter
End Class 'CustomSecurityAssertion
End Namespace
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Tokens;
namespace CustomPolicyAssertions
{
public class CustomSecurityAssertion : SecurityPolicyAssertion
{
TokenProvider<X509SecurityToken> serviceX509TokenProviderValue;
TokenProvider<X509SecurityToken> clientX509TokenProviderValue;
public TokenProvider<X509SecurityToken> ClientX509TokenProvider
{
get
{
return clientX509TokenProviderValue;
}
set
{
clientX509TokenProviderValue = value;
}
}
public TokenProvider<X509SecurityToken> ServiceX509TokenProvider
{
get
{
return serviceX509TokenProviderValue;
}
set
{
serviceX509TokenProviderValue = value;
}
}
public CustomSecurityAssertion()
: base()
{
}
public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
{
return new CustomSecurityClientOutputFilter(this);
}
public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
{
return new CustomSecurityClientInputFilter(this);
}
public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
{
return new CustomSecurityServerInputFilter(this);
}
public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
{
return new CustomSecurityServerOutputFilter(this);
}
public override void ReadXml(System.Xml.XmlReader reader, IDictionary<string, Type> extensions)
{
bool isEmpty = reader.IsEmptyElement;
base.ReadAttributes(reader);
reader.ReadStartElement("CustomSecurityAssertion");
if (!isEmpty)
{
if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "clientToken")
{
reader.ReadStartElement();
reader.MoveToContent();
// Get the registed security token provider for X.509 certificate security credentials.
Type type = extensions[reader.Name];
object instance = Activator.CreateInstance(type);
if (instance == null)
throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName));
TokenProvider<X509SecurityToken> clientProvider = instance as TokenProvider<X509SecurityToken>;
// Read the child elements that provide the details about the client's X.509 certificate.
clientProvider.ReadXml(reader, extensions);
this.ClientX509TokenProvider = clientProvider;
reader.ReadEndElement();
}
if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "serviceToken")
{
reader.ReadStartElement();
reader.MoveToContent();
// Get the registed security token provider for X.509 certificate security credentials.
Type type = extensions[reader.Name];
object instance = Activator.CreateInstance(type);
if (instance == null)
throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, "Unable to instantiate policy extension of type {0}.", type.AssemblyQualifiedName));
TokenProvider<X509SecurityToken> serviceProvider = instance as TokenProvider<X509SecurityToken>;
// Read the child elements that provide the details about the Web service's X.509 certificate.
serviceProvider.ReadXml(reader, extensions);
this.ServiceX509TokenProvider = serviceProvider;
reader.ReadEndElement();
}
base.ReadElements(reader, extensions);
reader.ReadEndElement();
}
}
public override IEnumerable<KeyValuePair<string, Type>> GetExtensions()
{
// Add the CustomSecurityAssertion custom policy assertion to the list of registered
// policy extensions.
List<KeyValuePair<string, Type>> extensions = new List<KeyValuePair<string, Type>>();
extensions.Add(new KeyValuePair<string, Type>("CustomSecurityAssertion", this.GetType()));
if (serviceX509TokenProviderValue != null)
{
// Add any policy extensions that read child elements of the <serviceToken> element
// to the list of registered policy extensions.
IEnumerable<KeyValuePair<string, Type>> innerExtensions = serviceX509TokenProviderValue.GetExtensions();
if (innerExtensions != null)
{
foreach (KeyValuePair<string, Type> extension in innerExtensions)
{
extensions.Add(extension);
}
}
}
if (clientX509TokenProviderValue != null)
{
// Add any policy extensions that read child elements of the <clientToken> element
// to the list of registered policy extensions.
IEnumerable<KeyValuePair<string, Type>> innerExtensions = clientX509TokenProviderValue.GetExtensions();
if (innerExtensions != null)
{
foreach (KeyValuePair<string, Type> extension in innerExtensions)
{
extensions.Add(extension);
}
}
}
return extensions;
}
public override void WriteXml(System.Xml.XmlWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
writer.WriteStartElement("CustomSecurityAssertion");
base.WriteAttributes(writer);
if (this.clientX509TokenProviderValue != null)
{
writer.WriteStartElement("clientToken");
this.clientX509TokenProviderValue.WriteXml(writer);
writer.WriteEndElement();
}
if (this.serviceX509TokenProviderValue != null)
{
writer.WriteStartElement("serviceToken");
this.serviceX509TokenProviderValue.WriteXml(writer);
writer.WriteEndElement();
}
base.WriteElements(writer);
writer.WriteEndElement();
}
}
class RequestState
{
SecurityToken clientToken;
SecurityToken serverToken;
public RequestState(SecurityToken cToken, SecurityToken sToken)
{
clientToken = cToken;
serverToken = sToken;
}
public SecurityToken ClientToken
{
get { return clientToken; }
}
public SecurityToken ServerToken
{
get { return serverToken; }
}
}
class CustomSecurityServerInputFilter : SecureConversationServiceReceiveSecurityFilter
{
public CustomSecurityServerInputFilter(CustomSecurityAssertion parentAssertion)
: base(parentAssertion)
{
}
public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security, MessageProtectionRequirements response)
{
SecurityToken clientToken = null;
SecurityToken serverToken = null;
// Ensure incoming SOAP messages are signed and encrypted.
foreach (ISecurityElement elem in security.Elements)
{
if (elem is MessageSignature)
{
MessageSignature sig = (MessageSignature)elem;
clientToken = sig.SigningToken;
}
if (elem is EncryptedData)
{
EncryptedData enc = (EncryptedData)elem;
serverToken = enc.SecurityToken;
}
}
if (clientToken == null || serverToken == null)
throw new Exception("Incoming message did not meet security requirements");
RequestState state = new RequestState(clientToken, serverToken);
envelope.Context.OperationState.Set(state);
}
}
class CustomSecurityServerOutputFilter : SecureConversationServiceSendSecurityFilter
{
public CustomSecurityServerOutputFilter(CustomSecurityAssertion parentAssertion)
: base(parentAssertion)
{
}
public override void SecureMessage(SoapEnvelope envelope, Security security, MessageProtectionRequirements request)
{
RequestState state = envelope.Context.OperationState.Get<RequestState>();
// Sign the message with the Web service's security token.
security.Tokens.Add(state.ServerToken);
security.Elements.Add(new MessageSignature(state.ServerToken));
// Encrypt the message with the client's security token.
security.Elements.Add(new EncryptedData(state.ClientToken));
}
}
class CustomSecurityClientInputFilter : SecureConversationClientReceiveSecurityFilter
{
X509SecurityToken serviceX509Token;
X509SecurityToken clientX509Token;
public CustomSecurityClientInputFilter(CustomSecurityAssertion assertion)
: base(assertion)
{
if (assertion.ServiceX509TokenProvider != null)
this.serviceX509Token = assertion.ServiceX509TokenProvider.GetToken();
if (assertion.ClientX509TokenProvider != null)
this.clientX509Token = assertion.ClientX509TokenProvider.GetToken();
}
public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security, MessageProtectionRequirements request)
{
RequestState state;
bool signed = false;
bool encrypted = false;
// Get the request state out of the operation state.
state = envelope.Context.OperationState.Get<RequestState>();
// Make sure the message was signed with the server's security token.
foreach (ISecurityElement elem in security.Elements)
{
if (elem is MessageSignature)
{
MessageSignature sig = (MessageSignature)elem;
if (sig.SigningToken.Equals(state.ServerToken))
signed = true;
}
if (elem is EncryptedData)
{
EncryptedData enc = (EncryptedData)elem;
if (enc.SecurityToken.Equals(state.ClientToken))
encrypted = true;
}
}
if (!signed || !encrypted)
throw new Exception("Response message does not meet security requirements");
}
public override IEnumerable<SecurityToken> GetUnattachedTokensCore(SoapContext context)
{
return new SecurityToken[] {this.serviceX509Token, this.clientX509Token };
}
}
class CorrelationState : SerializableTokenWrapper<EncryptedKeyToken>
{
public CorrelationState() : base() { }
public CorrelationState(EncryptedKeyToken token) : base(token) { }
}
class CustomSecurityClientOutputFilter : SecureConversationClientSendSecurityFilter
{
SecurityToken clientToken;
SecurityToken serverToken;
public CustomSecurityClientOutputFilter(CustomSecurityAssertion parentAssertion)
: base(parentAssertion )
{
// Get the client security token.
X509TokenProvider clientX509Provider = new X509TokenProvider(StoreLocation.CurrentUser, StoreName.My, "CN=WSE2QuickStartClient");
clientToken = clientX509Provider.GetToken();
// Get the server security token.
X509TokenProvider serverX509Provider = new X509TokenProvider(StoreLocation.LocalMachine, StoreName.My, "CN=WSE2QuickStartServer");
serverToken = serverX509Provider.GetToken();
}
public override void SecureMessage(SoapEnvelope envelope, Security security, MessageProtectionRequirements request)
{
if (envelope == null)
throw new ArgumentNullException("envelope");
if (security == null)
throw new ArgumentNullException("security");
if (request == null)
throw new ArgumentNullException("request");
// Sign the SOAP message with the client's security token.
security.Tokens.Add(clientToken);
security.Elements.Add(new MessageSignature(clientToken));
// Encrypt the SOAP message with the client's security token.
security.Elements.Add(new EncryptedData(serverToken));
// Encrypt the client's security token with the server's security token.
security.Elements.Add(new EncryptedData(serverToken, "#" + clientToken.Id));
// Store the client and server security tokens in the request state.
RequestState state = new RequestState(clientToken, serverToken);
// Store the request state in the proxy's operation state.
// This makes these tokens accessible when SOAP responses are
// verified to have sufficient security requirements.
envelope.Context.OperationState.Set(state);
}
}
}
See Also
Tasks
How to: Secure an Application Using a Custom Policy Assertion