Export (0) Print
Expand All
20 out of 23 rated this helpful - Rate this topic

How to: Sign a SOAP Message Using an X.509 Certificate

The Web Services Enhancements for Microsoft .NET (WSE) supports signing SOAP messages by using X.509 certificates that meet the following criteria:

  • The certificate must not be expired.
  • The certificate must support digital signatures.
  • The issuer of the certificate must be a trusted root, by default.

The following procedures detail how to use Policy or code to digitally sign a SOAP message using an X.509 certificate.

To use Policy to sign a SOAP message with an X.509 certificate

  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.

    Use the <Policy> Element (WSE for Microsoft .NET) (1) element to define criteria that a SOAP message must meet. The criteria are specified as child elements of the <Policy> element. The Id attribute is used to name the policy assertion, which is used by the <request> Element (WSE for Microsoft .NET), <response> Element (WSE for Microsoft .NET) and <fault> Element elements to refer to a policy assertion when applying the policy to an endpoint.

    The following code example defines a policy assertion named policy-c0a22319-6b89-49ff-9b82-bdbac5f04618.

    <wsp:Policy wsu:Id="policy-c0a22319-6b89-49ff-9b82-bdbac5f04618"
      xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
      xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" >
    
  2. Add an <Integrity> Element child element to the <Policy> Element (WSE for Microsoft .NET) (1) element.

    Use the <Integrity> Element element to define digital signature requirements. The requirements are specified in child elements.

    The following code example specifies that there are digital signature requirements.

    <wsp:Policy wsu:Id="policy-c0a22319-6b89-49ff-9b82-bdbac5f04618"
       xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
       xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" >
      <wssp:Integrity wsp:Usage="wsp:Required" xmlns:wssp="http://schemas.xmlsoap.org/ws/2002/12/secext">
    
  3. Add a <TokenInfo> Element child element to the <Integrity> Element element.

    Use the <TokenInfo> Element element to define security token requirements for the digital signature.

    The following code example specifies that there are security token requirements for the digital signature.

    
                  <wssp:Integrity wsp:Usage="wsp:Required"
                    xmlns:wssp="http://schemas.xmlsoap.org/ws/2002/12/secext">
                    <wssp:TokenInfo>
                
    
  4. Add a <SecurityToken> Element child element to the <TokenInfo> Element element.

    Use the <SecurityToken> Element element to define security token requirements for the digital signature.

    The following code example specifies that there are security token requirements for the digital signature.

    <wssp:TokenInfo>
      <wssp:SecurityToken>
    
  5. 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-x509-token-profile-1.0#X509v3.

    Use the <TokenType> Element element to specify the type of security token that must be used to create the digital signature.

    The following code example specifies that an X509SecurityToken must be used to digitally sign the SOAP message.

    <wssp:SecurityToken>
      <wssp:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wssp:TokenType>
    
  6. Optionally, add a <Claims> Element child element to the <SecurityToken> Element element.

    Use the <Claims> Element element to specify requirements specific to an X.509 certificate. The requirements are specified in the <Role> Element or <SubjectName> Element child elements.

    Note:
    It is highly recommended that you specify a subject name, so that you can ensure which X.509 certificate is used by WSE to sign the SOAP message.

    The following code example specifies that there are specific requirements on the X.509 certificate used to digitally sign the SOAP message.

    <wssp:SecurityToken>
      <wssp:Claims>
    
  7. Optionally, add a <SubjectName> Element child element to the <Claims> Element element.

    Use the <SubjectName> Element element to specify that the digital signature must be created using an X.509 certificate with a specific subject name.

    Note:
    When the <SubjectName> element is used to specify the subject name for an X.509 certificate, the value of the element is formatted differently than what appears in the Microsoft Management Console (MMC). The value that must be placed in the <SubjectName> element maps to the Subject field that appears on the Details tab of the Certificates snap-in within the MMC. If you copy the value of the Subject field from the MMC, the value has to be reversed prior to placement in the <SubjectName> element. For example, if the value of the Subject field is CN=WSE2QuickStartServer, O=Coho Winery, L=Woodinville, S=WA, C=US, the value that must be added to the <SubjectName> element is C=US, S=WA, L=Woodinville, O=Contoso, CN=Coho Winery.

    The following code example specifies that the digital signature be created with the X.509 certificate that has the subject name CN=WSE2QuickStartClient.

    <wssp:Claims>
      <wssp:SubjectName>CN=WSE2QuickStartClient</wssp:SubjectName>
    
  8. Optionally, add a <TokenIssuer> Element (WSE for Microsoft .NET) (1) child element to the <SecurityToken> Element element.

    Use the <TokenIssuer> Element (WSE for Microsoft .NET) (1) element to specify the certification authority (CA) that issued the X.509 certificate.

    Note:
    The value of the <TokenIssuer> element is formatted differently than what appears in the MMC. The value that must be placed in the <TokenIssuer> element maps to the Issuer field that appears on the Details tab of the Certificates snap-in within the MMC. If you copy the value of the Issuer field from the MMC, the value has to be reversed prior to placement in the <TokenIssuer> element. For example, if the value of the Issuer field is CN=CertServer DC=corp DC=contoso DC=com, the value that must be added to the <TokenIssuer> element is DC=com DC=contoso DC=corp CN=CertServer.

    The following code example specifies that the X.509 certificate that is used to digitally sign SOAP messages must have been issued by the DC=com DC=contoso DC=corp CN=CertServer CA.

    <wssp:SecurityToken>
      <wssp:TokenIssuer>DC=com DC=contoso DC=corp CN=CertServer</wssp:TokenIssuer>
    
  9. Add a <MessageParts> Element for <Integrity> Element element to the <Integrity> Element element.

    Use the <MessageParts> element to specify the XML elements that must be signed.

    The following code example specifies that the <Body> element and the To, Action, MessageID, and From headers are signed.

    <wssp:MessageParts Dialect="http://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>
    
  10. Add an <endpoint> Element element to the <mappings> Element element.

    Use the <endpoint> Element element to apply a policy assertion to an endpoint.

    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="http://schemas.microsoft.com/wse/2003/06/Policy">
      <endpoint uri="http://localhost/X509SignPolicyService/X509SigningService.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>
    
  11. Optionally, add an X509SecurityToken that meets the policy requirements to the PolicyEnforcementSecurityTokenCache to the code where you are sending the SOAP message.

    This step is optional, as WSE will look through the following caches or stores in the specified order to find an X.509 certificate that matches the Policy:

    1. The Tokens collection of the SoapContext associated with the SOAP message.
    2. The PolicyEnforcementSecurityTokenCache token cache.
    3. The certificate store specified in the application's configuration file.

    WSE uses the first X.509 certificate that it finds that matches the policy. Therefore, if you would like to use a specific X.509 certificate to sign the SOAP message, add an <SubjectName> Element element that specifies the specific X.509 certificate.

    The following code example adds an X509SecurityToken to the PolicyEnforcementSecurityTokenCache.

    X509SecurityToken token = GetToken();
    PolicyEnforcementSecurityTokenCache.GlobalCache.Add( token );
    

To write code to sign a SOAP message by using an X.509 certificate

  1. Obtain the X.509 certificate.

    The client X.509 certificate can be obtained in one of the following ways:

    • Purchase a certificate from a certification authority (CA), such as VeriSign, Inc.
    • Set up your own certificate service and have a CA sign the certificates. Windows 2000 Server, Windows 2000 Advanced Server, and Windows 2000 Datacenter Server all include certificate services that support public key infrastructure (PKI).
    • Set up your own certificate service and do not have the certificates signed.

    Whichever approach you take, the recipient of the SOAP request containing the X.509 certificate must trust the X.509 certificate. This means that the X.509 certificate or an issuer in the certificate chain is in the Trusted People certificate store and that the X.509 certificate is not in the Untrusted Certificates store.

    When the certificate is requested, choose the option to install the certificate in the current user certificate store on the local computer.

    For more information, see Managing X.509 Certificates.

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

  3. 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. Click 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.
  4. Add a Web reference to the Web service that is to receive the SOAP message signed with the X.509 certificate.

    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.
  5. Edit the proxy class to derive from the WebServicesClientProtocol class.

    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 the show all files icon on the Solution Explorer toolbar, and then expand the Reference.map node.

    2. Change the base class of the proxy class to the WebServicesClientProtocol class.
      This modifies the proxy class to emit routing and security SOAP headers when communicating with the Web service. The following code example modifies a proxy class named Service1 to derive from the Microsoft.Web.Services2.WebServicesClientProtocol class.
      public class Service1 : Microsoft.Web.Services2.WebServicesClientProtocol {
      
      Note:
      If you select the Update Web Reference option in Visual Studio .NET 2003, the proxy class is regenerated, the base class is reset to the SoapHttpClientProtocol class, and you must change the class that the proxy class derives from again.

  6. Add Imports or using directives to the top of the file that communicates with the Web service.

    1. In Solution Explorer, right-click the file containing the client code, and then click View Code.
    2. At the top of the file, add the Imports or using directives as shown in the following code example.
      using Microsoft.Web.Services2;
      using Microsoft.Web.Services2.Security;
      using Microsoft.Web.Services2.Security.Tokens;
      using Microsoft.Web.Services2.Security.X509;
      using System.Security.Cryptography;
      
  7. Add code to get an X.509 certificate.

    1. Open the certificate store containing the certificate that will be used to sign the SOAP message.
      The following code example opens the certificate store for the currently logged-in user.
         X509CertificateStore store;
         store = X509CertificateStore.CurrentUserStore(
                 X509CertificateStore.MyStore);
         bool open = store.OpenRead();
      
    2. Select a certificate from the certificate store.
      The certificate can be chosen programmatically by iterating over the X509CertificateStore.Certificates collection or by invoking one of the "Find" methods. The X509CertificateStore class supports the methods listed in the following table for finding a certificate.

      Method Description

      FindCertificateByHash

      Finds an X.509 certificate in the store by using the certificate's (SHA-1) hash value.

      FindCertificateByKeyIdentifier

      Finds an X.509 certificate in the store by using the certificate's authority key identifier.

      FindCertificateBySubjectName

      Finds an X.509 certificate in the store by using the certificate's name value.

      FindCertificateBySubjectString

      Finds an X.509 certificate in the store by using the certificate's name value. This search uses substring matching.

      The QuickStart Samples contain sample code that shows how to display a dialog box for the user to select the certificate. For more information, see the X509CertificateStoreDialog.cs file in the Samples\QuickStart folder in the WSE installation folder.
      The following code example retrieves the certificate from the certificate store by using the hash for the certificate. The hash is the certificate's thumbprint. To obtain a certificate's thumbprint, open the Certificates snap-in of the MMC, and then click the Details tab.
      byte[] certHash = {0x98, 0xec, 0x08, 0x4b, 0xa5 ,0x7a, 0x6c, 0x2f,
          0x39 ,0x26 ,0xb3 ,0x0a ,0x58, 0xbf ,0x65 ,0x25, 0x61, 0xc5, 
          0x64 ,0x59};
      X509CertificateCollection certs =
          store.FindCertificateByHash(certHash);
      
      Microsoft.Web.Services2.Security.X509.X509Certificate cert =
          ((Microsoft.Web.Services2.Security.X509.X509Certificate) certs[0]);
      
    3. Verify that the certificate can be used for signing.
      The certificate must support digital signatures and a private key must be available. The following code example determines whether the certificate supports digital signatures and whether its private key is accessible.
      if (!cert.SupportsDigitalSignature || 
                          (cert.Key == null))
           {
      
      The following code example defines a GetSecurityToken method that displays the certificate store to the current user and allows the user to select an X.509 certificate.
      public X509SecurityToken GetSecurityToken() 
      {
          X509SecurityToken securityToken = null;
          X509CertificateStore store = 
              X509CertificateStore.CurrentUserStore(
              X509CertificateStore.MyStore);
          bool open = store.OpenRead();
      
          try 
          {
             byte[] certHash = {0x98, 0xec, 0x08, 0x4b, 0xa5, 0x7a,
                                0x6c, 0x2f, 0x39, 0x26, 0xb3, 0x0a,
                                0x58, 0xbf, 0x65, 0x25, 0x61, 0xc5,
                                0x64, 0x59};
             X509CertificateCollection certs =
                store.FindCertificateByHash(certHash);
      
      Microsoft.Web.Services2.Security.X509.X509Certificate cert =
          ((Microsoft.Web.Services2.Security.X509.X509Certificate) certs[0]);
      
              if (cert == null) 
              {
                  MessageBox.Show(
                      "You chose not to select an X.509 " + 
                      "certificate for signing your messages.");
                  securityToken = null;
              }
              else if (!cert.SupportsDigitalSignature || 
                          (cert.Key == null)) 
              {  
                  MessageBox.Show(
                      "The certificate must support digital " + 
                      "signatures and have a private key available.");
                  securityToken = null;
              }
              else 
              {
                  securityToken = new X509SecurityToken(cert);
              }
          } 
          finally 
          {
              if (store != null)
                  store.Close();
          }
          return securityToken; 
      }
      
  8. Edit the method that communicates with the Web service to get the X.509 certificate and specify that the SOAP request must be signed by the X.509 certificate.

    For 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. Call the method defined in the previous step to get the client's X.509 certificate, which has a private key, to sign the SOAP message.
      X509SecurityToken signatureToken = GetSecurityToken();
      
    2. Get the SoapContext method for the SOAP request that is being made to the Web service.
      Service1 is the name of the proxy class to the Web service in the following code example.
      Service1 svc = new Service1();
      SoapContext requestContext = svc.RequestSoapContext;
      
    3. Add the client's X.509 certificate to the WS-Security SOAP header.
      requestContext.Security.Tokens.Add(signatureToken);
      
    4. Create a new instance of the MessageSignature class by using the X.509 certificate just added to the WS-Security SOAP header.
      MessageSignature sig = new MessageSignature(signatureToken);
      
    5. Add the digital signature to the WS-Security SOAP header.
      RequestContext.Security.Elements.Add(sig);
      
    6. Specify the time-to-live (TTL) for the SOAP message to minimize 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;
      
    7. Call the Web service.
      svc.sayHello(); 
      

Example

The following code example is a policy file specifying that all SOAP messages sent must be signed using an X.509 certificate.

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="http://schemas.microsoft.com/wse/2003/06/Policy">
  <mappings xmlns:wse="http://schemas.microsoft.com/wse/2003/06/Policy">
    <endpoint uri="http://localhost/X509SignPolicyService/X509SigningService.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="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" >
      <wssp:Integrity wsp:Usage="wsp:Required" xmlns:wssp="http://schemas.xmlsoap.org/ws/2002/12/secext">        <wssp:TokenInfo>          <!-- The SecurityToken element describes the requirements               for the certificate that must be used for the               signature. -->          <wssp:SecurityToken>            <wssp:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3</wssp:TokenType>            <wssp:Claims>                <!-- By specifying the SubjectName claim here, Policy                     can look for a certificate with this subject name                     in the certificate store indicated in the                     application's configuration.                -->                <wssp:SubjectName>CN=WSE2QuickStartClient</wssp:SubjectName>            </wssp:Claims>          </wssp:SecurityToken>        </wssp:TokenInfo>        <wssp:MessageParts Dialect="http://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 calls the GetSecurityToken method to get the X.509 certificate, adds the token to the RequestSoapContext method associated with the Web service proxy, and then sends a SOAP message to the Web service.

try 
{
    X509SecurityToken signatureToken = GetSecurityToken();
    if (signatureToken == null) 
    {
        return;
    }
    Proxy.Service1 svc = new Proxy.Service1();
    SoapContext requestContext = svc.RequestSoapContext;

    // Set the TTL to one minute.
    requestContext.Security.Timestamp.TtlInSeconds = 60;
        
    // Add the security token.                
    requestContext.Security.Tokens.Add(signatureToken);
    // Specify the security token to sign the message with.
    requestContext.Security.Elements.Add(new
    MessageSignature(signatureToken));

    MessageBox.Show( svc.sayHello() );
}
catch (Exception ex) 
{
    MessageBox.Show( ex.ToString() );
}
public X509SecurityToken GetSecurityToken() 
{
    X509SecurityToken securityToken = null;
    X509CertificateStore store = 
        X509CertificateStore.CurrentUserStore(
        X509CertificateStore.MyStore);
    bool open = store.OpenRead();

    try 
    {
       byte[] certHash = {0x98, 0xec, 0x08, 0x4b, 0xa5, 0x7a,
                          0x6c, 0x2f, 0x39, 0x26, 0xb3, 0x0a,
                          0x58, 0xbf, 0x65, 0x25, 0x61, 0xc5,
                          0x64, 0x59};
       X509CertificateCollection certs =
          store.FindCertificateByHash(certHash);

Microsoft.Web.Services2.Security.X509.X509Certificate cert =
    ((Microsoft.Web.Services2.Security.X509.X509Certificate) certs[0]);

        if (cert == null) 
        {
            MessageBox.Show(
                "You chose not to select an X.509 " + 
                "certificate for signing your messages.");
            securityToken = null;
        }
        else if (!cert.SupportsDigitalSignature || 
                    (cert.Key == null)) 
        {  
            MessageBox.Show(
                "The certificate must support digital " + 
                "signatures and have a private key available.");
            securityToken = null;
        }
        else 
        {
            securityToken = new X509SecurityToken(cert);
        }
    } 
    finally 
    {
        if (store != null)
            store.Close();
    }
    return securityToken; 
}

See Also

Show:
© 2014 Microsoft. All rights reserved.