How to: Sign a SOAP Message by Using a User Name and Password

The Web Services Enhancements for Microsoft .NET (WSE) define the UsernameToken class for passing security credentials in the form of a user name and password. Once an instance of the UsernameToken is created, it can be used to sign SOAP messages.

The following procedures detail how to use policy or code to sign a SOAP request by using a UsernameToken. A Web service can also sign the SOAP response by using similar steps.

To use policy to sign a SOAP message by using a user name and password

  1. Define a policy assertion and digital signature requirements by adding <Policy> Element (WSE for Microsoft .NET) (1) and <Integrity> Element elements.

    1. 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. 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. The value of the Id attribute must be unique to the policy file.
    2. 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" 
    
  2. Specify the token type by adding <TokenInfo> Element, <SecurityToken> Element, and <TokenType> Element elements.

    1. Add a <TokenInfo> Element child element to the <Integrity> Element element.
    2. Add a <SecurityToken> Element child element to the <TokenInfo> Element element.
    3. Add a <TokenType> Element child element to the <SecurityToken> Element element and set its value to http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken.
      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 UsernameToken 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>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</wssp:TokenType>      </wssp:TokenType>
    
  3. Optionally, specify requirements for the token by adding <Claims> Element, <SubjectName> Element, and <UsePassword> Element elements

    1. Add a <Claims> Element child element to the <SecurityToken> Element element.

    2. Add a <SubjectName> Element child element to the <Claims> Element element.
      The value of the <SubjectName> Element element specifies the user name portion of a UsernameToken. The MatchType attribute of the <SubjectName> Element element specifies how to compare the value of the <SubjectName> Element element with the user name portion of a UsernameToken. The following table lists the valid values for the MatchType attribute.

      Value Description

      wsse:Exact

      The values must be exactly the same, including case.

      wsse:Prefix

      The value of the <SubjectName> Element element must be a prefix of the   Username property.

      wsse:Regexp

      The value of the <SubjectName> Element element is a regular expression that must match the value of the Username property. For more details about regular expressions, see the .NET Framework Regular Expressions topic in the .NET Framework SDK documentation.

    3. Add a <UsePassword> Element child element to the <Claims> Elementelement.
      The Type attribute of the <UsePassword> Element element specifies how the password for a UsernameToken is passed in a SOAP message
      The following table lists the possible values for the Type attribute.

      Value Description

      wssp:PasswordDigest

      The password must be sent hashed. Use PasswordOption.SendHashed when sending the password.

      wssp:PasswordText

      The password must be sent in clear text. Use PasswordOption.SendPlainText when sending the password.

      The following code example specifies that a UsernameToken must be used to sign SOAP messages. Furthermore, the UsernameToken must be for the user name someone and the password must be sent hashed.

      <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>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</wssp:TokenType>
            <wssp:Claims>        <wssp:SubjectName MatchType="wssp:Exact">          someone</wssp:SubjectName>        <wssp:UsePassword Type="wssp:PasswordDigest"          wsp:Usage="wsp:Required" />       </wssp:Claims>
            </SecurityToken>
          </wssp:TokenInfo>
      
  4. 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.

    1. 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.
    2. Add a <defaultOperation> Element child element to the <endpoint> Element.
      The <defaultOperation> Element element specifies the default policy for all operations at the URI specified in the uri attribute.
    3. 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>
    
  5. Specify the XML elements to be signed by adding a <MessageParts> Element for <Integrity> Element element to the <Integrity> Element element.

    1. 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".
    2. 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) wse:Timestamp()
    </wssp:MessageParts>
    
  6. Add a UsernameToken security token that meets the policy requirements to the PolicyEnforcementSecurityTokenCache of the application where you are sending the SOAP message.

    Unlike other security tokens natively supported by WSE, this step is not optional. This is because you cannot specify claims about the UsernameToken (such as the password for the UsernameToken) in policy that are sufficient for WSE to create a new UsernameToken instance.

    The following code example adds a UsernameToken to the PolicyEnforcementSecurityTokenCache.

    PolicyEnforcementSecurityTokenCache.GlobalCache.Add( token )
    
    PolicyEnforcementSecurityTokenCache.GlobalCache.Add( token );
    

To use code to sign a SOAP message by using a user name and password

  1. Open the Web service client project in Visual Studio .NET 2003.

  2. Add references to the Microsoft.Web.Services2 and System.Web.Services assemblies.

    1. In Solution Explorer, right-click References, and then click Add Reference.
    2. On the .NET tab, select Microsoft.Web.Services2.dll, and then click Select.
    3. On the .NET tab, select System.Web.Services.dll, and then click Select.
    4. Click OK.
  3. Add a Web reference to the Web service that is to receive the SOAP message signed using a user name and password.

    1. On the Project menu, choose Add Web Reference.
    2. In the Add Web Reference dialog box, type the URL for the Web service in the Address box, and then click the Arrow icon.
    3. Verify that the items in the Available References box are the items you want to reference in your project, and then choose Add Reference.
  4. Edit the proxy class to derive from WebServicesClientProtocol.

    1. In Solution Explorer, right-click the Reference.cs file for the Web reference just added, and then click View Code.

      Note

      If the Reference.cs file containing the proxy class is not visible, click Show All Files on the Solution Explorer toolbar, and then expand the Reference.map node.

    2. Change the base class of the proxy class to Microsoft.Web.Services2.WebServicesClientProtocol.
      This modifies the proxy class to emit WS-Addressing and WS-Security SOAP headers when communicating with the Web service. The following code example modifies a proxy class named Service1 to derive from Microsoft.Web.Services2.WebServicesClientProtocol.
      [C#]

      public class Service1 : 
          Microsoft.Web.Services2.WebServicesClientProtocol {
      
      Public Class Service1
          Inherits Microsoft.Web.Services2.WebServicesClientProtocol
      

      Note

      If you select Update Web Reference in Visual Studio .NET 2003, the proxy class is regenerated, the base class is reset to SoapHttpClientProtocol , and you must repeat this step.

  5. Add the following using or Imports directives to the top of the file containing the Web service client code:

    using Microsoft.Web.Services2;
    using Microsoft.Web.Services2.Security;
    using Microsoft.Web.Services2.Security.Tokens;
    
    Imports Microsoft.Web.Services2
    Imports Microsoft.Web.Services2.Security
    Imports Microsoft.Web.Services2.Security.Tokens
    
  6. Edit the method that communicates with the Web service to sign the SOAP message by using the UsernameToken.

    For more information about signing portions of the SOAP message other than the defaults, see How to: Specify the Parts of a SOAP Message That Are Signed or Encrypted.

    1. Create a new UsernameToken instance, specifying the user name, password, and how the password is sent in the SOAP message.
      The Web service provides a class that derives from the UsernameTokenManager class, which includes the AuthenticateToken method. The AuthenticateToken method is given the user name in the SOAP message, but no form of the user name's password. The AuthenticateToken method is expected to return the user name's password or password equivalent. WSE calls the AuthenticateToken method, and then compares the password or password equivalent to the one received in the SOAP message. The following table explains the options for passing the password in the SOAP message.

      PasswordOption enumeration member Description

      SendNone

      The password is never sent in any form in the SOAP message, but WSE does use the password to sign the SOAP message. A recipient would then need to provide a password to WSE during the signature verification stage.

      SendHashed

      The SHA-1 hash of the password is sent in the SOAP message. This is the best way to help protect the password. When a SOAP message is received with a UsernameToken, WSE calls the AuthenticateToken method of the class deriving from UsernameTokenManager that is registered in the configuration file. The AuthenticateToken method returns a password or password equivalent, which WSE creates a SHA-1 hash from. That SHA-1 hash is compared to the one in the SOAP message and if they are identical, the hashed password is deemed valid.

      SendPlainText

      The password is always sent in plain text in the SOAP message. This option is recommended only when the UsernameToken is encrypted using a security token or certificate obtained from the target Web service or SSL or a similar protocol is used to connect to the Web service; otherwise, the password could be intercepted. WSE running on the recipient's computer compares the plain text password or password equivalent in the SOAP message to the one returned from the AuthenticateToken method of the class deriving from UsernameTokenManager. If they are identical, the password is deemed valid.

      The following code example does not send the password in the SOAP request.

      UsernameToken userToken = new UsernameToken(userName,password,
                                                  PasswordOption.SendNone)
      
      Dim userToken As New UsernameToken(userName, password, _
                                         PasswordOption.SendNone)
      
    2. Get the SoapContext for the SOAP request that is being made to the Web service.
      Service1 is the name of the proxy class for the Web service in the following code example.

      Service1 serviceProxy = new Service1();
      SoapContext requestContext = serviceProxy.RequestSoapContext;
      
      Dim serviceProxy As New Service1()
      Dim requestContext As SoapContext = serviceProxy.RequestSoapContext
      
    3. Add the UsernameToken to the WS-Security SOAP header.

      requestContext.Security.Tokens.Add(userToken);
      
      requestContext.Security.Tokens.Add(userToken)
      
    4. Encrypt the UsernameToken in the WS-Security SOAP header.
      This step is optional. However, if the password is being sent in plain text, it is recommended that the UsernameToken, which contains the password, be encrypted or that SSL or a similar protocol be used to connect to the Web service; otherwise, the password could be intercepted. If the target Web service is authorizing the SOAP request based on the UsernameToken, the password must be sent in plain text.
      The following code example encrypts the UsernameToken by using an X.509 certificate obtained from the target Web service. To see the code that obtains the X.509 certificate in the GetSecurityToken user-defined method, see How to: Encrypt a SOAP Message Using an X.509 Certificate.

      X509SecurityToken serverToken = GetSecurityToken();
      requestContext.Security.Elements.Add(new
          EncryptedData(serverToken,"#" + userToken.Id));
      
      X509SecurityToken serverToken = GetSecurityToken() 
      requestContext.Security.Elements.Add(new _
          EncryptedData(serverToken,"#" + userToken.Id))
      
    5. Create a new instance of the MessageSignature class by using the UsernameToken just added to the WS-Security SOAP header.

      MessageSignature sig = new MessageSignature(userToken);
      
      Dim sig As New MessageSignature(userToken)
      
    6. Add the digital signature to the WS-Security SOAP header.

      requestContext.Security.Elements.Add(sig);
      
      requestContext.Security.Elements.Add(sig)
      
    7. Specify the time-to-live (TTL) for the SOAP message to diminish the chance of someone intercepting the message and replaying it.
      The following code example sets the TTL to 1 minute.

      requestContext.Security.Timestamp.TtlInSeconds = 60;
      
      requestContext.Security.Timestamp.TtlInSeconds = 60
      
    8. Call the Web service.

      serviceProxy.sayHello();
      
      serviceProxy.sayHello()
      

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 UsernameToken. Furthermore, the UsernameToken must be for the user name someone and the password must be sent hashed.

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">
  xmlns:wse="https://schemas.microsoft.com/wse/2003/06/Policy"
  <mappings>
    <endpoint uri="http://www.cohowinery.com/SaleWidgets.asmx">
      <defaultOperation>
        <request policy="#policy-c0a22319-6b89-49ff-9b82-bdbac5f04618" />
        <response policy="" />
        <fault policy="" />
      </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" >
      <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>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</wssp:TokenType>
            <wssp:Claims>
                <wssp:SubjectName MatchType="wssp:Exact">
                  someone</wssp:SubjectName> 
                <wssp:UsePassword Type="wssp:PasswordDigest"
                  wsp:Usage="wsp:Required" />
            </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) wse:Timestamp()
        </wssp:MessageParts>
      </wssp:Integrity>
    </wsp:Policy>
  </policies>
</policyDocument>

The following code example signs a SOAP message by using a UsernameToken.

UsernameToken userToken;
userToken = new UsernameToken(userName, userName,
    PasswordOption.SendHashed);
try
{
    // Create the Web service proxy.
    AddNumbers serviceProxy = new AddNumbers();
    // Get the SoapContext associated with the SOAP request.
    SoapContext requestContext = serviceProxy.RequestSoapContext;
    // Set the TTL to 1 minute.
    requestContext.Security.Timestamp.TtlInSeconds = 60;
 
    // Add the token to the SOAP header.
    requestContext.Security.Tokens.Add(userToken);
    // Sign the SOAP message by using the UsernameToken.
    RequestContext.Security.Elements.Add(new
      MessageSignature(userToken));

    // Call the Web service.
    int sum = serviceProxy.AddInt(a, b);
    }
catch (System.Web.Services.Protocols.SoapException se) 
{
    MessageBox.Show(se.ToString());
}
catch (Exception ex) 
{
    Error("Exception caught while invoking a Web service.", ex);
    return;
}
Dim userToken As UsernameToken
userToken = New UsernameToken(userName, userName, PasswordOption.SendHashed)
Try
    ' Create the Web service proxy.
    Dim serviceProxy As New AddNumbers()
    ' Get the SoapContext associated with the SOAP request.
    Dim requestContext As SoapContext = serviceProxy.RequestSoapContext
    ' Set the TTL to 1 minute.
    requestContext.Security.Timestamp.TtlInSeconds = 60
   
    ' Add the token to the SOAP header.
    requestContext.Security.Tokens.Add(userToken)

    ' Sign the SOAP message by using the UsernameToken.
    RequestContext.Security.Elements.Add(new
      MessageSignature(userToken))

    ' Call the Web service.
    Dim sum As Integer 
    sum = serviceProxy.AddInt(a, b)

Catch se As System.Web.Services.Protocols.SoapException
    MessageBox.Show(se.ToString())
Catch ex As Exception
    MessageBox.Show("Exception caught while invoking a Web service.")
    Return
End Try

See Also

Tasks

How to: Verify Digital Signatures of SOAP Messages Signed Using a User Name and Password
How to: Specify the Parts of a SOAP Message That Are Signed or Encrypted
How to: Digitally Sign a SOAP Message

Reference

UsernameToken