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

The Web Services Enhancements for Microsoft .NET Framework (WSE) allow a sender, which can be either an XML Web service or its client, to encrypt portions of the SOAP message by using the public key for the recipient's X.509 certificate. A receiver, which can be either a Web service or its client, then decrypts the message by using the private key for its X.509 certificate. If the recipient is the only one with the private key, it is highly likely that only the recipient can decrypt the message. Therefore, to encrypt the message, the sender must have access to the public key for the recipient's X.509 certificate and the recipient must have access to the private key for the recipient's X.509 certificate.

Optionally, the SOAP message can be signed before or after encrypting the SOAP message. If the SOAP message is signed, the sender signs the SOAP message with an X.509 certificate that it owns and has the private key for. This allows the recipient Web service to verify that the client really sent the message through the use of the public key for the sender's X.509 certificate. If the signature can be verified using the public key, the recipient can assume that the message came from the client. Therefore, to sign the message, the sender must have access to the sender's X.509 certificate and private key and the recipient must have access to the sender's X.509 certificate and public key.

The following procedures detail how to use code to encrypt a SOAP message using an X.509 certificate.

To use code to encrypt a SOAP message by using an X.509 certificate

  1. Obtain the recipient's X.509 certificate. If you are signing the X.509 certificate, obtain the receiver's X.509 certificate.

    1. Export the recipient's certificate on their computer.
      To export the certificate, open the Certificates snap-in of the MMC, and then run the Certificate Export Wizard.
    2. Obtain the client X.509 certificate 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 or later all include certificate services that support public key infrastructure (PKI).
    • Set up your own certificate service. In this case, the certificate will not be signed.
      Whichever approach you take, the recipient of the SOAP request containing the X.509 certificate must trust the 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.
      For more information, see Managing X.509 Certificates.
      In order to use the X.509 certificate for encryption, the Key Usage property of the certificate must include the Data Encipherment attribute.
  2. Install the recipient's X.509 certificate in a sender-accessible certificate store on the sender's computer.

    • To import the certificate, open the Certificates snap-in of the MMC, and then run the Certificate Import Wizard.
      The code example that follows (in step 6) assumes that the certificate is imported into the CurrentUser certificate store.
  3. Create a custom policy assertion.

    For more details about creating custom policy assertions, see How to: Create a Custom Policy Assertion that Secures SOAP Messages.

  4. Override the SecureMessage method in the output SOAP filter for the client or the Web service that signs SOAP messages.

    The following code example overrides the SecureMessage method for the client output SOAP filter.

    Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security)
    
    public override void SecureMessage(SoapEnvelope envelope, Security security)
    {
    
  5. Add references to the Microsoft.Web.Services3 and System.Web.Services assemblies.

    1. On the Project menu, click Add Reference.
    2. Click the .NET tab, select Microsoft.Web.Services3.dll, and then click Select.
    3. On the .NET tab, select System.Web.Services.dll, and then click Select.
    4. Click OK.
  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.

      Imports System
      Imports System.Collections.Generic
      Imports System.Text
      Imports System.Xml
      Imports System.Security.Cryptography.X509Certificates
      
      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 Microsoft.Web.Services3;
      using Microsoft.Web.Services3.Design;
      using Microsoft.Web.Services3.Security;
      using Microsoft.Web.Services3.Security.Tokens;
      
  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.

      Dim store As X509Store = New X509Store(StoreName.My, _
        StoreLocation.CurrentUser)
      store.Open(OpenFlags.ReadOnly)
      
      X509Store store = new X509Store(StoreName.My,
        StoreLocation.CurrentUser);
      store.Open(OpenFlags.ReadOnly);
      
    2. Select a certificate from the certificate store.
      The certificate can be chosen programmatically by iterating over the System.Security.Cryptography.X509Certificates.X509Certificate2Collection collection or by invoking the System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Find method.
      The following code example retrieves the certificate from the certificate store by using the subject name for the certificate.

      Dim certs As X509Certificate2Collection = _
        store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, _
        subjectName, False)
      
      X509Certificate2Collection certs =
          store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
          subjectName, false);
      
    3. Verify that a certificate exists in the store that matches the specified criteria.
      When the certificate is used to encrypt the SOAP message, the certificate must support data encryption.
      The following code example determines whether the certificate store contains a certificate that matches the specified subject name.

      Dim cert As X509Certificate2
      If certs.Count = 1 Then
          cert = certs(0)
          securityToken = New X509SecurityToken(cert)
      
      X509Certificate2 cert;
      if (certs.Count == 1)
      {
          cert = certs[0];
          securityToken = new X509SecurityToken(cert);
      }
      

      The following code example defines a GetSecurityToken method that searches for a specific X.509 certificate based on its subject name in the current user's certificate store.

      Public Function GetSecurityToken(ByVal subjectName As String) As X509SecurityToken
          Dim securityToken As X509SecurityToken = Nothing
          Dim store As X509Store = New X509Store(StoreName.My, _
            StoreLocation.CurrentUser)
          store.Open(OpenFlags.ReadOnly)
          Try
              Dim certs As X509Certificate2Collection = _
                store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, _
                subjectName, False)
              Dim cert As X509Certificate2
              If certs.Count = 1 Then
                  cert = certs(0)
                  securityToken = New X509SecurityToken(cert)
              Else
                  securityToken = Nothing
              End If
          Catch ex As Exception
              securityToken = Nothing
          Finally
              If Not (store Is Nothing) Then
                  store.Close()
              End If
          End Try
          Return securityToken
      End Function
      
      public X509SecurityToken GetSecurityToken(string subjectName)
      {
          X509SecurityToken securityToken = null;
          X509Store store = new X509Store(StoreName.My,
            StoreLocation.CurrentUser);
          store.Open(OpenFlags.ReadOnly);
          try
          {
              X509Certificate2Collection certs =
                  store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
                  subjectName, false);
      
              X509Certificate2 cert;
              if (certs.Count == 1)
              {
                  cert = certs[0];
                  securityToken = new X509SecurityToken(cert);
              }
              else
                  securityToken = null;
          }
          catch (Exception ex)
          {
              securityToken = null;
          }
          finally
          {
              if (store != null)
                  store.Close();
          }
          return securityToken;
      }
      
  8. In the SecureMessage method, encrypt and optionally sign the SOAP message using an X.509 certificate.

    Note

    Even though you typically sign the SOAP message when encrypting it, this is not required. Therefore, steps a through d are optional.

    1. If the SOAP message is being signed, get an X.509 certificate to sign the SOAP message.
      The following code example calls 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.

      ' Get an X.509 certificate for signing the SOAP message.
      Dim signatureToken As X509SecurityToken = GetSecurityToken("CN=WSE2QuickStartClient")
      
      // Get an X.509 certificate for signing the SOAP message.
      X509SecurityToken signatureToken = GetSecurityToken("CN=WSE2QuickStartClient");
      
    2. If the SOAP message is being signed, add the sender's X.509 certificate to the WS-Security SOAP header.

      ' Add the X.509 certificate to the header.
      security.Tokens.Add(signatureToken)
      
      // Add the X.509 certificate to the header.
      security.Tokens.Add(signatureToken);
      
    3. If the SOAP message is being signed, create a new instance of the MessageSignature class by using the X.509 certificate just added to the WS-Security SOAP header.
      For information about signing portions of the SOAP message other than the defaults, see Signing Custom SOAP Headers.

      Dim sig As New MessageSignature(signatureToken)
      
      MessageSignature sig = new MessageSignature(signatureToken);
      
    4. If the SOAP message is being signed, add the MessageSignature class to the WS-Security SOAP header.

      security.Elements.Add(sig)
      
      security.Elements.Add(sig);
      
    5. Get an X.509 certificate to encrypt the SOAP message.
      The following code example calls the method defined in the previous step to get the server's X.509 certificate, which has only a public key, to encrypt the SOAP message.

      ' Get an X.509 certificate for encrypting the SOAP message.
      Dim encryptionToken As X509SecurityToken = _
        GetSecurityToken("CN=WSE2QuickStartServer")
      
      // Get an X.509 certificate for encrypting the SOAP message.
      X509SecurityToken encryptionToken = GetSecurityToken("CN=WSE2QuickStartServer");
      
    6. Create a new instance of the EncryptedData class by using the X.509 certificate just added to the WS-Security SOAP header.

      Dim enc As New EncryptedData(encryptionToken)
      
      EncryptedData enc = new EncryptedData(encryptionToken);
      
    7. Add the EncryptedData class to the WS-Security SOAP header.

      security.Elements.Add(enc)
      
      security.Elements.Add(enc);
      

Example

The following code example signs and encrypts a SOAP request to a Web service.

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Xml
Imports System.Security.Cryptography.X509Certificates

Imports Microsoft.Web.Services3
Imports Microsoft.Web.Services3.Design
Imports Microsoft.Web.Services3.Security
Imports Microsoft.Web.Services3.Security.Tokens

    
    
    ...
    
    
            Public Overrides Sub SecureMessage(ByVal envelope As SoapEnvelope, ByVal security As Security)
            ' Get an X.509 certificate for signing the SOAP message.
            Dim signatureToken As X509SecurityToken = GetSecurityToken("CN=WSE2QuickStartClient")
            If signatureToken Is Nothing Then
                Throw New SecurityFault("Message Requirements could not be satisfied.")
            End If

            ' Add the X.509 certificate to the header.
            security.Tokens.Add(signatureToken)

            ' Specify that the SOAP message is signed using this X.509
            ' certifcate.
            Dim sig As New MessageSignature(signatureToken)
            security.Elements.Add(sig)

            ' Get an X.509 certificate for encrypting the SOAP message.
            Dim encryptionToken As X509SecurityToken = _
              GetSecurityToken("CN=WSE2QuickStartServer")

            If encryptionToken Is Nothing Then
                Throw New SecurityFault("Message Requirements could not be satisfied.")
            End If

            ' Specify that the SOAP message is encrypted using 
            ' this X.509 certificate.
            Dim enc As New EncryptedData(encryptionToken)
            security.Elements.Add(enc)
        End Sub 'SecureMessage
        Public Function GetSecurityToken(ByVal subjectName As String) As X509SecurityToken
            Dim securityToken As X509SecurityToken = Nothing
            Dim store As X509Store = New X509Store(StoreName.My, _
              StoreLocation.CurrentUser)
            store.Open(OpenFlags.ReadOnly)
            Try
                Dim certs As X509Certificate2Collection = _
                  store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, _
                  subjectName, False)
                Dim cert As X509Certificate2
                If certs.Count = 1 Then
                    cert = certs(0)
                    securityToken = New X509SecurityToken(cert)
                Else
                    securityToken = Nothing
                End If
            Catch ex As Exception
                securityToken = Nothing
            Finally
                If Not (store Is Nothing) Then
                    store.Close()
                End If
            End Try
            Return securityToken
        End Function
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;

using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Tokens;

    
    
    ...
    
    
            public override void SecureMessage(SoapEnvelope envelope, Security security)
        {
            // Get an X.509 certificate for signing the SOAP message.
            X509SecurityToken signatureToken = GetSecurityToken("CN=WSE2QuickStartClient");
            if (signatureToken == null)
            {
                throw new SecurityFault("Message Requirements could not be satisfied.");
            }

            // Add the X.509 certificate to the header.
            security.Tokens.Add(signatureToken);

            // Specify that the SOAP message is signed using this X.509
            // certifcate.
            MessageSignature sig = new MessageSignature(signatureToken);
            security.Elements.Add(sig);

            // Get an X.509 certificate for encrypting the SOAP message.
            X509SecurityToken encryptionToken = GetSecurityToken("CN=WSE2QuickStartServer");
            if (encryptionToken == null)
            {
                throw new SecurityFault("Message Requirements could not be satisfied.");
            }

            // Specify that the SOAP message is encrypted using 
            // this X.509 certificate.
            EncryptedData enc = new EncryptedData(encryptionToken);
            security.Elements.Add(enc);
        }
        public X509SecurityToken GetSecurityToken(string subjectName)
        {
            X509SecurityToken securityToken = null;
            X509Store store = new X509Store(StoreName.My,
              StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            try
            {
                X509Certificate2Collection certs =
                    store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
                    subjectName, false);

                X509Certificate2 cert;
                if (certs.Count == 1)
                {
                    cert = certs[0];
                    securityToken = new X509SecurityToken(cert);
                }
                else
                    securityToken = null;
            }
            catch (Exception ex)
            {
                securityToken = null;
            }
            finally
            {
                if (store != null)
                    store.Close();
            }
            return securityToken;
        }

See Also

Tasks

How to: Decrypt a SOAP Message Encrypted with an X.509 Certificate

Concepts

Encrypting a SOAP Message

Other Resources

Signing Custom SOAP Headers
Managing X.509 Certificates
Brokered Authentication - X.509 PKI
X.509 Technical Supplement