Securing Web Services with X.509 Certificates

Last week's exercise began teaching you about the security and policy enhancements for Web Services 2.0. This article, covering the second exercise in the lab, picks up where last week's left off. It was written by MSDN Virtual Labs.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 9
July 20, 2006
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Exercise 2 Securing Web Services with X.509 Certificates

Scenario

In the last exercise you were able to secure SecureInvoiceServiceA by requiring UsernameToken authentication, a message signature, and encryption.

Using UsernameToken for signing and encrypting messages is not the most secure option. Using a binary security token, such as an X.509 certificate, offers a higher level of security. In this exercise, you'll walk through the process of installing some sample certificates and configuring your application to use them for signature and encryption purposes.

You'll be working in the Exercises\B\before directory.

        Tasks              Detailed steps

Installing the Sample Certificates

WSE 2.0 provides two sample certificates for you to use while testing your WSE 2.0 applications. These two certificates were generated by the makecert tool provided in the Microsoft Platform Software Development Kit. In order to begin using these certificates in your applications, you must first install them on your computer.

Note: you should not use these sample certificates in a production environment. You must contact a certificate authority, request your own certificate, and follow the procedures shown here to use it.

  1. Open an MMC console by pressing Start, press Run, type mmc, and then click OK
  2. On the File menu, click Add/Remove Snap-in 
  3. Click Add, under Snap-in, double-click Certificates
  4. Click My user account to add the certificates for the current user. Click Finish
  5. Click Add, under Snap-in, double-click Certificates.
  6. Click Computer account for the local machines certificates. 
  7. Click Next
  8. Click Finish.
  9. Click Close.
  10. Click OK.
  11. Your MMC window should now look something like this:
     

  12. In the console tree, click Certificates - Current User | Personal
  13. Open the Certificate Import wizard by clicking Action | All Tasks | Import…
  14. Click Next.
  15. In the File Name field, type C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Client Private.pfx
  16. Click Next
  17. In the Password field, type wse2qs.
  18. Click Next.
  19. Click Next.
  20. Click Finish
  21. Click OK.

    Note: this certificate will be used by our client application to sign messages sent to the service. It could also be used to identify the client for authentication purposes. 
  22. In the console tree, click Certificates (Local Computer) | Personal
  23. Open the Certificate Import wizard by clicking Action | All Tasks | Import…
  24. Click Next.
  25. In the File Name field, type C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Server Private.pfx
  26. Click Next.
  27. In the Password field, type wse2qs.
  28. Click Next
  29. Click Next
  30. Click Finish.
  31. Click OK.

    Note: this certificate will be used to encrypt messages between the applications. The client application will use the public key to encrypt the message and the service will use the private key to decrypt the message. The client needs to have the public portion of the certificate available in the Current User store. 
  32. In the console tree, click Certificates - Current User | Other People.

    Note: if you don't have an Other People store under Current User, open Internet Explorer, click Tools | Internet Options | Content, and click the Certificates button. Click the Other People tab in the certificates dialog. You can import the certificate by clicking Import….and then following steps hh-mm.
  33. If importing in the mmc, open the Certificate Import wizard by clicking Action | All Tasks | Import….
  34. Click Next.
  35. In the File Name field, type C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Server Public.cer.
  36. Click Next
  37. Click Next
  38. Click Finish
  39. Click OK
  40. If importing through Internet Explorer, click Close, click OK, close Internet Explorer, and return to the mmc
  41. Close the mmc
  42. If prompted to save settings, click No.

    Note: this certificate only contains the public portion of Server Private.pfx. The client will use this to encrypt messages and the server will use the private key installed in the Local Machine store to decrypt the messages.

Signing with a Certificate

Once you have the certificates installed on your computer, you can begin using them to sign and encrypt messages. In this step, you're going to sign messages sent from the client application using the client certificate (found in the Current User store). 

  1. Return to Visual Studio .NET 2003
  2. Click File | Open | Project.
  3. Navigate to C:\Microsoft Hands-on-Lab\DEV-HOL34\VB \ Exercises\B\before
  4. Select SecureInvoiceB.sln and click Open.

    Note: this solution is equivalent to the one you implemented in the last exercise. It uses a UsernameToken to authenticate, sign, and encrypt message. 
  5. Open the code view of InvoiceManagerForm.vb in the SecureInvoiceClient project.
  6. Add an Imports statement for Microsoft.Web.Services2. Security.X509:

    ' InvoiceManagerForm.vb
    ...
    Imports
    Microsoft.Web.Services2. Security. X509
    ...

  7. Add a new method to the InvoiceManagerForm class named GetX509Token. The method should take two strings (one to represent certificate's key identifier and another to represent the certificate store name that you're going to retrieve the certificate from), and return an X509SecurityToken as illustrated here:

    ...
    Private Function GetX509Token(ByVal keyId As
    String,
        ByVal storeId As String) As X509SecurityToken
        ...
    End Function 'GetX509Token
    ...

  8. Within the GetX509Token method, you need to retrieve an X509CertificateStore object for the specified store, open the store for reading, and find the certificate based on its key identifier. Then, return the identified certificate as a new X509SecurityToken object. Here's one way to write this code:

    ...
    Private Function GetX509Token(ByVal keyId As String,
        ByVal storeId As String) As X509SecurityToken
        Dim store As X509CertificateStore = X509CertificateStore.Current UserStore(storeId) 
        store.OpenRead()
        Dim certs As X509CertificateCollection =    
           store.FindCertificate ByKeyIdentifier( 
             Convert.FromBase64 String(keyId))
        store.Close()
        Return New X509SecurityToken(CType(certs(0),
    X509Certificate))
    End Function 'GetX509Token
    ...
     
  9. Go to the ConfigureProxy method in InvoiceManagerForm. This is where we need to modify the code to use the client certificate (found in the Current User store) for signing the message.
  10. Within ConfigureProxy, remove all code within the function. You'll be replacing this code with certificate signing and encryption. You'll be replacing this code with certificate signing and encryption.
  11. Within ConfigureProxy, call your new GetX509Token method. For the key identifier, specify "gBfo0147lM6cKnTbbMSuMVvm FY4=", and for the store name, specify X509CertificateStore.MyStore as illustrated here:

     ...
    ' Retreive client certificate for signing Dim clientToken As X509SecurityToken = GetX509Token(   
     "gBfo0147lM6cKnTbbMSuMVvm
    FY4=", X509CertificateStore.MyStore)
    ...


    Note: You can use the X509 Certificate Tool (called WseCertificate2.exe) to determine the key identifier for a given certificate. This tool ships with WSE 2.0. You can find it in the following directory: C:\Program
    Files\Microsoft WSE\v2.0\Tools\Certificates
    . Here's what it looks like:

     

  12. Add the returned X509SecurityToken to the proxy's RequestSoapContext.Security.Tokens collection as illustrated here:

    ...
    ' Retreive client certificate for signing
    Dim clientToken As X509SecurityToken = GetX509Token( _  
       "gBfo0147lM6cKnTbbMSuMVvm FY4=", X509CertificateStore.MyStore)
    ' Add UsernameToken for authentication purposes proxy.RequestSoapContext. Security.Tokens.Add(login.
    Token)
    ' Must add client token to message for signature processing proxy.RequestSoapContext. Security.Tokens.Add(client
    Token)
    ...


    Note: You should continue to send the UsernameToken as well. The UsernameToken will still be used for authentication and authorization purposes. Now you're going to use the certificate for signing the message. 

  13. Instantiate a new MessageSignature based on the X509SecurityToken and add it to the proxy's RequestSoapContext.Security. Elements collection as illustrated here:

     ...
    ' Retreive client certificate for signing
    Dim clientToken As X509SecurityToken = GetX509Token( _  
       "gBfo0147lM6cKnTbbMSuMVvm FY4=", X509CertificateStore.MyStore) ' Add UsernameToken for authentication purposes proxy.RequestSoapContext. Security.Tokens.Add(login.
    Token)
    ' Must add client token to message for signature processing proxy.RequestSoapContext. Security.Tokens.Add(client
    Token)
    proxy.RequestSoapContext. Security.Elements.Add( _  
       new MessageSignature(clientToken))
    ...
     
  14. Open WseSecurityHelpers.vb in the SecureInvoiceServiceB project and comment out the call to CheckForEncryption in GetUsernameToken. This makes it so the service doesn't require encryption for the time being.

    Note: you'll add X.509-based encryption in the next step and add this line of code back in.

  15. Right-click on the SecureInvoiceServiceB project in Solution Explorer and click WSE Settings 2.0. Navigate to the Security tab and check Allow Test Roots in the X.509 Certificate Settings section.
  16. Click Yes to confirm the enabling of test roots. 
  17. Press OK to close the tool.
  18. Build the solution and run the client application. Verify that if everything works as before (except for requiring encryption). 
  19. Open OutputTrace.webinfo in the SecureInvoiceClient output directory (bin).

    Notice that the message now contains a BinarySecurityToken element in addition to the UsernameToken from before.

 

Encrypting the Body with a Certificate

Now you're going to use a certificate to encrypt the data sent in the body of the message. You'll use the public certificate found in Current User\Other People to encrypt the message and WSE 2.0 will use the private certificate found on Local Machine\Personal to decrypt the message.

You'll continue working in SecureInvoiceB.sln for this step.

  1. Open InvoiceManagerForm.vb in the SecureInvoiceClient project.
  2. Return to the ConfigureProxy method in InvoiceManagerForm.vb. You're going to add code to this method to encrypt the body of the message. The first thing you need to do is call GetX509Token to retrieve the server token from the Current User\Other People certificate store as illustrated here:

    ...
    ' Retreive client certificate for signing
    Dim clientToken As X509SecurityToken = GetX509Token( _  
       "gBfo0147lM6cKnTbbMSuMVvm FY4=", X509CertificateStore.MyStore)
    Dim serverToken As X509SecurityToken = GetX509Token(
       "bBwPfItvKp3b6TNDq+14qs58 VJQ=",  
      X509CertificateStore.Other People)
    ...
     
  3. Then, towards the end of the method, instantiate an EncryptedData object based on the serverToken you retrieved from the certificate store and add it to the proxy's RequestSoapContext.Security. Elements collection as illustrated here:

    ... ' ConfigureProxy
    ' Retreive client certificate for signing
    Dim clientToken As X509SecurityToken = GetX509Token( _ 
       "gBfo0147lM6cKnTbbMSuMVvm FY4=", X509CertificateStore.MyStore) Dim serverToken As X509SecurityToken = GetX509Token( _  
     "bBwPfItvKp3b6TNDq+14qs58VJQ
    =", _
      X509CertificateStore. OtherPeople)
    ' Add UsernameToken for authentication purposes proxy.RequestSoapContext. Security.Tokens.Add(login. Token)
    ' Must add client token to message for signature processing proxy.RequestSoapContext. Security.Tokens.Add(client Token) proxy.RequestSoapContext. Security.Elements.Add( _
       new MessageSignature(clientToken))
    ' Encrypt the body proxy.RequestSoapContext. Security.Elements.Add(
       new EncryptedData(serverToken))
    ...

  4. When the Web service receives the encrypted message, it needs to know which certificate store to look for the certificate in. Open web.config in the SecureInvoiceB project and add a storeLocation="LocalMachine" attribute to the x509 element in configuration/microsoft.web. services2/security.

    ... 
      <microsoft.web. services2>
        <diagnostics>
          <trace enabled="true" input="InputTrace.webinfo" output="OutputTrace.webinfo"
    />
        </diagnostics>
        <security>
          ...
          <x509 storeLocation="Local Machine" allowTestRoot="true" /> 
        </security> 
      </microsoft.web.services2>
    ... 

  5. Open WseSecurityHelpers.vb in the SecureInvoiceServiceB project and uncomment the call to CheckForEncryption in the GetUsernameToken method. This makes it so the service requires encryption again.
  6. Before you can run the application, you have to give the ASPNET account read access to the private key of the server certificate. Otherwise it won't be able to read it during the decryption process. 
  7. Press Start | Run, and enter WseCertificate2.exe to launch the WSE X.509 Certificate Tool.
  8. Change the Certificate Location to Local Computer and Store Name to Personal and press the Open Certificate button.
  9. Select the WSE2QuickStartServer certificate and press OK. Then,
    press the View Private Key File Properties… button.
  10. Navigate to the Security tab and give the local machine's ASPNET account read access to the private key using the Add… button.

    Note: If the Security tab is not present, click Start | Control Panel | Folder Options. In the View tab, click to deselect the Use Simple File Sharing (Recommended) option at the bottom of the Advanced Settings list and click Apply followed by Ok Then, click Cancel and repeat from step i

  11. In the Enter the object names to select box, type ASPNET and click Check Names.
  12. Click OK to close the dialog. 
  13. Click Apply
  14. Click OK
  15. Close the WSE X.509 Certificate Tool
  16. Return to Visual Studio .NET 2003
  17. Build the solution and run the client application. Verify that everything works.
  18. Close the Invoice Manager application.
  19. Refresh and view the OutputTrace.webinfo pane in Visual Studio .NET 2003.

    Notice that the body of the SOAP message is now encrypted, and as a result, you should no longer be able to read it. It should look something like this:

     <soap:Body wsu:Id="Id-79aed0a2-5188-424f-8a2b-db57a98b29f0"> 
      <xenc:EncryptedData Id="EncryptedContent-1c657bca-d574-474f-b84b-1e42cb109943" Type="
    http://www.w3.org/2001
    /04/xmlenc#Content
    " xmlns:xenc="http://www.w3.org/
    2001/04/xmlenc
    #"> 
      <xenc:EncryptionMethod Algorithm="
    http://www.w3.org/
    2001/04/xmlenc#aes128-cbc
    " />
       <xenc:CipherData> <xenc:CipherValue>r6Ef04DoBQzxj
    Wdd8MIioIxKSzn2cJNU0qXVn5DgDS8GD
    6GGAT7w42k757udPXHSRFRhsE4qZspxD
    6LKwhJD
    /A==</xenc:CipherValue>  
       </xenc:CipherData> 
       </xenc:EncryptedData> </soap:Body>

 

Encrypting a UsernameToken with a Certificate

In the last step, you wrote code to encrypt the body of the SOAP message. You may also wish to encrypt headers on an individual basis. For example, one header that you may wish to encrypt is the UsernameToken header, especially when including the plain text password in the header. Doing this would allow you to leverage Windows authentication without having to write a custom UsernameToken manager or send the messages over HTTPS.

You'll continue working in SecureInvoiceB.sln for this step.

  1. Open InvoiceManagerForm.vb in the SecureInvoiceClient project. 
  2. Return to the ConfigureProxy method in InvoiceManagerForm.vb. You're going to add code to this method to encrypt the UsernameToken header in the message.
  3. At the end of the method, instantiate another EncryptedData element based on the serverToken (like in the last step) but this time you need to specify the reference id of the UsernameToken header as the second parameter to the constructor. Then, add this new EncryptedData element to the proxy's RequestSoapContext.Security. Elements collection as illustrated here:

    ... ' ConfigureProxy
    ' Encrypt the body proxy.RequestSoapContext. Security.Elements.Add( _
       new EncryptedData(serverToken))

    proxy.RequestSoapContext. Security.Elements.Add(
       new EncryptedData(serverToken,
    string.Format("#{0}",
          login.Token.Id)))

  4. Open login.vb in the SecureInvoiceClient project, navigate to the button1_Click method, and change the password option to PasswordOption.SendPlainText when instantiating the UsernameToken
  5. Open web.config in the SecureInvoiceServiceB project and comment out the <securityTokenManager> element in configuration/microsoft.web.services2  /security. Doing this will disable the custom UsernameToken manager that you've been using.

    ...  
      <microsoft.web.services2>
        ...
        <security>
          <!--<securityTokenManager type="SecureInvoiceServiceB. MyUsernameTokenManager, SecureInvoiceServiceB-

    After" xmlns:wsse="
    http://dovb. oasis-

    open.org/wss/2004/01/oasis-200401-wss-wssecurity-

    secext-1.0.xsd
    " qname="wsse:UsernameToken" />-->
        <x509 storeLocation="LocalMachine" allowTestRoot="true" allowRevocationUrlRetrieval= "false" verifyTrust="true" /> 
        </security>  
     </microsoft.web.services2>
    ...


    Note: Now that you've disabled the custom UsernameToken manager, you'll have to provide the passwords that you setup for the local user accounts when logging in. The password “password” will no longer work.

  6. Build the solution and run the client application. Make sure you specify the credentials for the local user accounts (instead of those required by the custom UsernameToken manager) when logging in. Verify that it everything works. 
  7. Close the application. 
  8. Refresh and view the OutputTrace.webinfo file. Notice that the body of the SOAP message is encrypted as well as the UsernameToken header. You should not be able to read the password, which was originally sent in plain text.
  9. Click File | Close Solution.
blog comments powered by Disqus
VISUAL BASIC.NET ARTICLES

- Basic Form Properties and Modality in VB.NET
- Multiple Document Interfaces in Visual Basic
- Visual Basic for Beginners
- ASP.NET Image to PDF with VB.Net
- MySQL in ASP.NET: Mono using VB.NET
- AsyncFileUpload File Type and File Size Vali...
- Visual Studio: Adding Functionality and Style
- Clocks and Countdowns
- User-defined Functions using Visual Basic Ap...
- Understanding Object Binding in VBA
- Mastering the Message Box
- Testing a Windows Forms Application
- Using Visual Basic.NET Features to Code a Wi...
- Correcting Code in a Windows Forms Applicati...
- Write Readable Code and Comments for Windows...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 2 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials