Web Services Enhancements 2.0: Security and Policy (VB.NET)

If you want to use WSE 2.0 to secure SOAP messages, then this lab is for you. It is aimed at developers familiar with .NET and basic Web services concepts. The first of several parts, it was written by Microsoft Virtual Labs.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 5
July 13, 2006
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Objectives

After completing this lab, you will be able to:

  • Secure Web Services with UsernameToken.
  • Secure Web Services with X.509 Certificates.
  • Automate Security with Policies.
  • Optimize Security with Secure Conversation.
  • Route Secure Messages.

    Note: Because this lab focuses on concepts, it may not comply with Microsoft security recommendations.

Scenario  

This lab will help you understand how to use WSE 2.0 to secure SOAP messages with authentication, integrity and confidentiality. You’ll learn how to accomplish this manually using the WSE 2.0 security APIs as well declaratively through a policy.

This lab was designed for developers already familiar with .NET and basic Web services concepts. Specifically, you should have experience working with VB.NET, ASP.NET, Visual Studio .NET solutions, XML and basic security concepts (users, roles, etc.).

If you get stuck on a particular exercise you can self-diagnose the issue by looking at the solution in the after directory of each exercise.

By completing this lab, you will better understand WSE 2.0's overall security features including the support for WS-Security, WS-Trust, and WS-SecureConversation, and the WS-Policy specifications. You'll learn how to implement the most common security needs in your Web services applications, specifically how to handle authentication, integrity, and confidentiality concerns. First you'll implement these features manually in your code and then you'll automatically enable them through policy files. Then, you'll see the benefits of this message-oriented security model first hand when you configure your application to route messages through an intermediary.

Estimated time to complete this lab: 90 minutes

Exercise 1 Securing Web Services with UsernameToken

Scenario

Your first exercise involves securing a Web service. WS-Security defines a message-oriented framework for securing Web services. A message-oriented framework makes it possible to secure Web services in a transport-neutral way. Although Web services can be secured using SSL, such a solution is tied to HTTP for practical purposes.

Many Web services desire message-level access control, integrity and privacy. WS-Security facilitates achieving these goals by defining mechanisms for authenticating, signing, and encrypting SOAP messages respectively. Once a message has been authenticated, it's also possible to authorize access to specific operations based on the message's credentials.

WSE 2.0 provides an implementation of WS-Security in the Microsoft.Web.Services2.Security namespace. The WSE 2.0 implementation makes it possible to authenticate, sign, and encrypt the SOAP messages used in your WebMethods. It also makes it possible to authorize access to specific functionality based on the incoming message's credentials.

You'll be working in the ExercisesAbefore directory. If you get stuck along the way, feel free to check out the solution found in ExercisesAafter.

        Tasks              Detailed steps

Getting Started

 

    1. Click in the virtual machine window. 
    2. Click the Administrator icon.
    3. Logon as Administrator with a password of password
    4. Before you begin, run the setup script found in C:Microsoft Hands-on-LabDEV-HOL34VBExercisessetup.js.
    5. Open Windows Explorer and navigate to C:Microsoft Hands-on-LabDEV-HOL34VBExercisesAbefore and double-click SecureInvoiceA.sln. This solution consists of two projects: SecureInvoiceClient and SecureInvoiceServiceA.
    6. Familiarize yourself with the client and server code. These two projects constitute an invoice management system. Run SecureInvoiceClient and experiment with the different operations. Notice that any user (including no user) can perform any of the supported operations. If you run SecureInvoiceClient application and press View, you should see the following results:
        


    7. Close the Invoice Manager application. 
    8. The SecureInvoiceServiceA virtual directory is not performing any type of HTTP authentication when messages arrive. Go to Internet Information Services and verify this.
    9. Click Start | Administrative Tools | Internet Information Services.
    10. Open CLIENT1 | Web Sites | Default Web Site.
    11. Right click on the SecureInvoiceServiceA virtual directory and select Properties.
    12. Click the Directory Security tab.
    13. Click Edit in the Anonymous access and authentication control area.
    14. If the Anonymous access checkbox is not checked, check it. We're not going to use the built-in HTTP authentication provided by IIS. We're going to use WSE 2.0's WS-Security implementation instead.
    15. Click OK
    16. Click OK
    17. Close Internet Information Services
    18. Your goal is to add security features that control access to the various operations based on the credentials of the incoming message. Assume that the application supports the following groups of users and corresponding rights:

      Group             Rights

      User               View invoices

      Vendor           Submit invoices

      Manager         Approve invoices

      Accounting      Pay invoices

 

Creating User Accounts and Groups

Note: any user should be allowed to view invoices, but only vendors can submit invoices, only managers can approve invoices, and only accounting personnel can pay invoices.

Your job in this exercise is to implement these features using the WSE 2.0 security APIs.

You need to setup up some local user accounts and groups to use in this part of the lab. You're going to create one group for each of the user types described above (e.g., User, Vendor, Manager, and Accounting) along with some user accounts assigned to the different groups.

  1. Click Start | Control Panel | Administrative Tools | Computer Management.
  2. Navigate to System Tools | Local Users and Groups | Users.
  3. Create four new user accounts named admin, vick, mike, and aaron. You can use the same password for all of them to make things easier. Follow these steps for creating each account:
  4. Click Action | New User.
  5. Enter the user name (e.g., admin) and password (e.g., password)
  6. Deselect User must change password at next logon and select Password never expires.
  7. Click Create
  8. Repeat steps e – g for the names vick, mike, and aaron
  9. Close the New User window. 
  10. Navigate to System Tools | Local Users and Groups | Groups. You're going to create the following new groups with the corresponding members:

    Group             Members

    User               admin, vick, mike, aaron

    Vendor           admin, vick

    Manager        admin, mike

    Accounting    admin, aaron

    Create four new groups named User, Vendor, Manager, and Accounting. Follow these steps for creating each group:

  11. Select Action | New Group.
  12. Type in the group name (e.g, User). 
  13. Click Add to select the group members. Simply type the name of the user (e.g., admin) and press OK
  14. Repeat this for each member of the group. 
  15. Click Create.
  16. Repeat steps l - o with the appropriate users for the groups Vendor, Manager, and Accounting
  17. Close the New Group window.
  18. Close Computer Management.

    Now you're ready to start writing code that takes advantage of these user accounts and groups using WSE 2.0.

Enabling WSE 2.0

In this task, you're going to enhance your WebMethod implementation and the corresponding client application with the Web Services Enhancements (WSE) 2.0. We've already installed WSE 2.0 on your machines. Your task will be to configure things so you take advantage of WSE 2.0 security features.

  1. Return to Visual Studio .NET 2003
  2. Right click on the SecureInvoiceServiceA project icon in Solution Explorer, and select WSE Settings 2.0... 
  3. On the General tab, check Enable this project for Web Services Enhancements and Enable Web Services Enhancements Soap Extensions.

    Note: the first option adds a reference to the Microsoft.Web.Services2 assembly while the second option registers a SoapExtension class in your project's web.config file.

  4. On the Diagnostics tab, check Enable Message Trace.

  5. Press OK to apply the settings and close the tool.

    Note: The OK button is on the bottom of the dialog, and may be hidden by the size of the screen.

  6. Verify that your project now contains a reference to the Microsoft.Web.Services2 assembly. 

  7. Verify that the web.config file now contains the following sections:

    ...
    <system.web>
       <webServices> 
          <soapExtensionTypes> 
             <add type= "Microsoft.Web.Services2. WebServicesExtension, Microsoft.Web.Services2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad 364e35" priority="1" group="0"/>
          </soapExtensionTypes>
       </webServices> </system.web> <microsoft.web.services2>
       <diagnostics>
          <trace enabled="true" input="InputTrace.webinfo" 
    output="OutputTrace.webinfo"
    />
        </diagnostics> 
    </microsoft.web.services2> ...
     

  8. Follow the same steps to enable WSE 2.0 and tracing in the SecureInvoiceClient project.

    Note: you can't enable the WSE 2.0 SoapExtension since it's not a Web service project.

  9. Update the project Web References by right clicking on each node under Web References in Solution Explorer (localhost, localhost1, localhost2, and localhost3) and selecting Update Web Reference.

    Note: you can find the Web References by navigating to the Web References folder in Solution Explorer. The Update Web Reference command may take some time. 

  10. Open the Web reference code (found in Reference.vb under each Web reference node) and notice that there are now two proxy classes: one that uses WSE and one that doesn't.

    Note: to do this, select the project in Solution Explorer, and press the Show All Files button on the toolbar (2nd button from left). Then, you'll find the file under Web References | localhost | Reference.map | Reference.vb).

  11. Open InvoiceManagerForm.vb in code view. 

  12. Locate the ViewInvoices() method and change the line

    Dim proxy As New ViewInvoices

    to

    Dim proxy As New ViewInvoicesWse 

  13. Locate the button handler for Approve, and change the code to use the new WSE-based proxy class by changing

    Dim proxy As New ApproveInvoice

    To

    Dim proxy As New ApproveInvoiceWse

  14. Repeat the last step for the button handlers for Pay and Submit. (e.g., change SubmitInvoices to SubmitInvoicesWse, change SubmitInvoice to SubmitInvoiceWse, etc.)

  15. Rebuild the project.

Sending a UsernameToken

Since the SecureInvoiceServiceA project is now configured to use WSE 2.0, it's ready to process security tokens sent by the client application. The first step is to add code to SoapInvoiceClient to send a UsernameToken to the service.

  1. Open login.vb and inspect the code behind the Login button.
  2. Add the following WSE 2.0 namespaces to the top of the file:

    ...
    Imports Microsoft.Web.Services2 Imports Microsoft.Web.Services2.Security Imports Microsoft.Web.Services2.Security.Tokens
    ... 

  3. Add a new public field of type Microsoft.Web.Services2.Security.Tokens.UsernameToken to the Login Form class. It should look something like this:

    ...
    Public Class LoginForm 
       Inherits System.Windows.Forms.Form
     
       Public Token As UsernameToken = Nothing
       ...
    End Class
    ...
     
  4. When the user fills in the form (providing his/her username and password) and presses the Login button, you need to instantiate a UsernameToken object with the supplied username and password. For now, use the PasswordOption.SendPlainText to send the password in plain text. We'll cover other options later.

     ...
    Public Class LoginForm 
       Inherits System.Windows.Forms.Form
       Public Token As UsernameToken = Nothing
       ...
       Private Sub button1_Click(ByVal sender As
    Object, _
           ByVal e As System.EventArgs) Handles button1.Click
          Me.Token = New
    UsernameToken(Me.textBox1.Text, _  
            Me.textBox2.Text, PasswordOption.SendPlain
    Text)
        
     Me.Close()
       End Sub 'button1_Click
    End Class
    ...
     
  5. Return to InvoiceManagerForm.vb.
  6. Inspect the code behind the Set User button (on the InvoiceManagerForm). It simply displays the form defined in login.vb. Add a new line of code to display the username on the form
    as illustrated here (we've provided a label named labelLogin):

    ...
    Private Sub btnLogin_Click(ByVal sender As Object,
    _
        ByVal e As System.EventArgs) Handles btnLogin.Click 
        login.ShowDialog()
        labelLogin.Text = String.Format("Username: {0}", _ 
           login.Token.Username)
    End Sub 'btnLogin_Click
    ...
     

  7. Add the following namespaces to the top of the InvoiceManagerForm.vb file:

    ...
    Imports Microsoft.Web.Services2 Imports Microsoft.Web.Services2.Security Imports Microsoft.Web.Services2.Security. Tokens
    ...
     
  8. Now you need to configure the proxy class with the UsernameToken created for the user. Add a new method to InvoiceManagerForm called ConfigureProxy that looks like this:

    ...
    Private Sub ConfigureProxy(ByVal proxy As  
     WebServicesClientProtocol) proxy.RequestSoapContext. Security.Tokens.Add(login.
    Token)
    End Sub 'ConfigureProxy
    ...

  9. This method adds the UsernameToken to the SOAP request. The UsernameToken allows the service to perform authentication when it receives the message.
  10. Locate the btnApprove_Click method in InvoiceManagerForm and call ConfigureProxy before invoking the Approve operation as illustrated here:

    ...
    Private Sub btnApprove_Click(ByVal sender As
    Object, _
        ByVal e As System.EventArgs) Handles btnApprove.Click
        Try
            Dim proxy As New ApproveInvoiceWse
     
           ConfigureProxy(proxy)
            proxy.Approve(dataGrid1( _  
            dataGrid1.CurrentRow Index,
    0).ToString())
            ViewInvoices()
        Catch ex As Exception 
            MessageBox.Show(ex.Message, "Invoice Error", _  
           MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End Try
    End Sub 'btnApprove_Click
    ...

  11. Build and run SecureInvoiceClient
  12. Press Set User. Specify the admin account's credentials (that you created earlier) and press OK. Press View, select an invoice and press Approve. Verify that it works.

    Note: WSE 2.0 automatically authenticates the supplied UsernameToken against the local machine accounts.
  13. Open the output trace file (OutputTrace.webinfo) in the application directory (SecureInvoiceClientbin) and locate the UsernameToken information in the last SOAP request. Notice the UsernameToken is being sent in plain text, including the password.

    Note: in practice, you don't want to send passwords in plain text, unless you're sending the message over a secure channel like HTTPS. You can also send a hashed version of the password or no password at all, but then you have to write some code to assist in the authentication process. We'll cover more on this later.

  14. Try changing the user account to something else (e.g., username: bob, password: bob), press Approve again, and verify that it doesn't work. You should get an authentication error.

    Note: notice that the View operation worked even though you didn't send a UsernameToken in the request. WSE 2.0 automatically authenticates a UsernameToken when supplied in plain text, but it's not required by default. You can require security tokens programmatically or through a declarative policy file.

 

Requiring a UsernameToken

You can require clients to supply a security token by adding some code to your WebMethods.

 

    1. Open WseSecurityHelpers.vb and add the following namespaces to the top of the file:

      ...
      Imports Microsoft.Web.Services2 Imports Microsoft.Web.Services2.
      Security
      Imports Microsoft.Web.Services2.
      Security.Tokens
      ... 

    2. Open WseSecurityHelpers.vb in the SecureInvoiceServiceA project and locate the WseSecurityHelpers class. Add a single method to the class named GetUsernameToken that looks like this:

      ... ' WseSecurityHelpers.vb Public Class WseSecurityHelpers 
         
      Public Shared Function GetUsernameToken(ByVal context As
             SoapContext) As UsernameToken

          End Function
      End Class
      ...
       
    3. In GetUsernameToken, verify that context is not null and that it contains a UsernameToken object, which needs to be returned. Here's an example of how you can accomplish this:

      ... ' WseSecurityHelpers.vb
      Public Class WseSecurityHelpers 
          Public Shared Function GetUsernameToken(ByVal context _
              As SoapContext) As UsernameToken
              If context Is Nothing Then
                  Throw New Exception(
                   "Only SOAP requests are permitted.")
              End If

              If context.Security.Tokens.
      Count = 0 Then
        throw New SoapException("Missing security token",  
       SoapException.Client
      FaultCode)
              Else
                  Dim tok As UsernameToken
                  For Each tok In context.Security. Tokens 
                   Return tok
               Next tok
               Throw New Exception("UsernameToken not supplied")
              End If

          End Function 'GetUsernameToken
      End Class
      ...
       
    4. Open ViewInvoices.asmx.vb in code view and add the following namespaces to the top of the file:

      ...
      Imports Microsoft.Web.Services2 Imports Microsoft.Web.Services2.
      Security
      Imports Microsoft.Web.Services2.
      Security.Tokens
      ...
       
    5. Within ViewInvoices.asmx.vb, update the View method to call GetUsernameToken before doing anything:

      ...
      <WebService([Namespace] := "
      http://example.org/
      invoices
      ")> _
      Public Class ViewInvoices
          Inherits WebService 
          <WebMethod()> _ 
          Public Function View() As DataSet   
      WseSecurityHelpers.
      GetUsernameToken( _  
           RequestSoapContext
      .
      Current)
          ... ' remainder of method as before
          End Function
      End Class
      ...


    6. Repeat the last two steps and make the same changes to the following files: SubmitInvoice.asmx.vb, ApproveInvoice.asmx.vb, and PayInvoice.asmx.vb.
    7. Build SecureInvoiceServiceA.
    8. Run the client and try invoking View again. View shouldn't work now since the client still isn't sending a UsernameToken in the request.
    9. Return to InvoiceManagerForm.vb and update all of the button handlers to call ConfigureProxy before invoking the operation (like you did earlier for Approve).

      Note that the call to ConfigureProxy for the View operation will be placed in the ViewInvoices() method. 

    10. Build SecureInvoiceClient.
    11. Run the client again and verify that you must provide a valid user account before you can invoke any of the operations.

      Note: in addition to requiring UsernameTokens, you can also programmatically require signature and encryption elements using similar techniques.

Authorization

At this point, SecureInvoiceServiceA is performing (and requiring) message authentication but it isn't distinguishing between different users and what they're authorized to do. Authorizing messages based on the supplied token is made possible by the token's Principal property. WSE 2.0 populates Principal with the Windows account information mapped to the supplied token.

 

    1. Within each WebMethod add a call to Principal.IsInRole to verify that the authenticated user is in the appropriate group for the given operation. 
    2. The following code illustrates how to check for the Accounting group before executing Pay in PayInvoice.asmx in the SecureInvoiceServiceA project:

      ...
      <WebService([Namespace] := "
      http://example.org/invoices")>
      Public Class PayInvoice 
          Inherits WebService 
          <WebMethod()> _
          Public Sub Pay(ByVal id As String)
              Dim tok As UsernameToken = _ 
               WseSecurityHelpers. Get UsernameToken( _  
               RequestSoapContext. Current)
              If Not tok.Principal.IsInRole(
                String.Format("{0}Accounting", Dns.GetHostName())) Then 
                  Throw New Exception("access denied") 
              End If
       
              InvoiceManager.Pay(id)
          End Sub 'Pay
      End Class 'PayInvoice
      ...

    3. Repeat this for each WebMethod specifying the appropriate group (as outlined earlier).
    4. Build SecureInvoiceServiceA.
    5. Run the client again and verify that vick can submit invoices, mike can approve invoices, aaron can pay invoices, and admin can do everything. Try to do something that isn't allowed for the current user and verify that you get "access denied".

Implementing a UsernameTokenManager

In the previous examples, WSE 2.0 was able to automatically authenticate the supplied UsernameToken against local Windows accounts. WSE 2.0 can only do this when the password is sent in plain text (using PasswordOption.SendPlainText). This is not the most common approach in practice because of the obvious security risks. You should only send passwords in plain text when you're sending messages over a secure channel (e.g., HTTPS) or when you're simply not concerned about confidentiality in the application environment. Another approach is to send a hashed password (using PasswordOption.SendHashed) or no password at all. This technique requires you to implement a class that assists in the UsernameToken authentication process. In this step, you're going to write a custom UsernameTokenManager-derived class.

 

    1. Open WseSecurityHelpers.vb and define a new class named MyUsernameTokenManager. It should derive from UsernameTokenManager.
    2. Override the AuthenticateToken method as illustrated here:

      ...
      Public Class MyUsernameTokenManager
          Inherits UsernameTokenManager 
          Protected Overrides Function AuthenticateToken(ByVal token _ 
          As UsernameToken) As String
           ...
          End Function
      End Class ...


      Note: you can use Visual Studio.NET's Class View to automatically override the method. Navigate to the AuthenticateToken method, right click, and select Add | Override. 

    3. Within AuthenticateToken, you can inspect the supplied token to perform authentication. You must return the password for the supplied token, which WSE 2.0 will check against what was supplied in the message. For this example, simply return "password" for all tokens. You'll now need to provide "password" as the password when running the client application.

      Note: You would typically look up the password in a database or some other password store (e.g., an XML file).

    4. In addition, you need to manually set the token's Principal property. Do this by creating a new GenericPrincipal object and assign it to all of the roles that the supplied user belongs to (according to what we defined earlier). Here's an example of AuthenticateToken:

      ...
      Public Class MyUsernameTokenManager 
          Inherits UsernameTokenManager
          Protected Overrides Function
      AuthenticateToken(ByVal token _
             As UsernameToken) As String
             Dim roles As New ArrayList()
             Select Case token.Username
                 Case "admin" 
                     roles.Add(String.Format("{0}User",   
                   Dns.GetHostName()))
      roles.Add(String.Format("{0}Vendor",
                   Dns.GetHostName()))
      roles.Add(String.Format("{0}Manager", _
                   Dns.GetHostName()))
      roles.Add(String.Format("{0}Accounting", 
                   Dns.GetHostName()))
                token.Principal = New
      GenericPrincipal(
                   New GenericIdentity(token.Username), 
                   roles.ToArray(GetType(String)))
            Case "aaron"
                roles.Add(String.Format("{0}User",   
                  Dns.GetHostName()))
      roles.Add(String.Format("{0}Accounting", _  
                  Dns.GetHostName()))
               token.Principal = New
      GenericPrincipal( _
                         New GenericIdentity(token.Username),   
                     roles.ToArray(GetType(String)))
              Case "mike" 
                  roles.Add(String.Format("{0}User",   
                   Dns.GetHostName()))
      roles.Add(String.Format("{0}Manager",
                   Dns.GetHostName()))
                token.Principal = New
      GenericPrincipal( 
                   New GenericIdentity(token.Username), _ 
                   roles.ToArray(GetType(String)))
            Case "vick"
                roles.Add(String.Format("{0}User", 
                  Dns.GetHostName()))
      roles.Add(String.Format("{0}Vendor", 
                  Dns.GetHostName()))
               token.Principal = New
      GenericPrincipal( 
                  New GenericIdentity(token.Username), 
                  roles.ToArray(GetType(String)))
           Case Else  
               MyBase.Authenticate
      Token(token)
           End Select
           Return "password"

        End Function 'AuthenticateToken
      End Class 'MyUsernameTokenManager

      ....

    5. To use this UsernameTokenManager, you need to configure it in the project's Web.config file. The easiest way to accomplish this is to open the WSE Settings 2.0 tool.
    6. Right-click on the SecureInvoiceServiceA project and click WSE Settings 2.0….
    7. Click the Security tab. 
    8. Click Add in the Security Tokens Managers section. The SecurityToken Manager dialog will then open. 
    9. In the Type field, type SecureInvoiceServiceA. MyUsernameTokenManager, SecureInvoiceServiceA
    10. In the Namespace field, enter http://docs.oasis-
      open.org/wss/2004/01/oasis-200401-wss-
      wssecurity-secext-1.0.xsd

    11. In the QName field, enter wsse:UsernameToken
    12. Click OK.
    13. Click OK
    14. Open the project's web.config file and confirm that it contains the following sections:

      <configuration>
          ...
          <configSections> 
              <section name="microsoft.web.services2" type="Microsoft.Web.Services2.
      Configuration.WebServices Configuration, Microsoft.Web.Services2, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
      />
          </configSections>  
         <microsoft.web.services2> 
                <security>   
              <securityTokenManager type="SecureInvoiceServiceA.My
      UsernameTokenManager, SecureInvoiceServiceA" xmlns:wsse="
      http://dovb.oasis-
      open.org/wss/2004/01/oasis-200401-wss-wssecurity
      -secext-1.0.xsd
      " qname="wsse:UsernameToken" />
                 </security>  
        </microsoft.web.services2>
        ...
       
    15. Build SecureInvoiceServiceA.
    16. Run the client again and verify that the security functionality has remained the same, even with our custom UsernameTokenManager class.

      Note: the client application is still sending the password in plain text at this point.
    17. In the Solution Explorer, in the SecureInvoiceClient project, right-click login.vb and click View Code.
    18. In the button1_Click method, replace PasswordOption.SendPlainText with PasswordOption.SendHashed
    19. Rebuild the project and test. Verify that everything still works as before.

Signing with a UsernameToken

In addition to security token authentication, it may also be desirable to sign the message in order to achieve integrity (prevent tampering). In this step, you'll add code to sign the SOAP messages with a DerivedKeyToken, based on the UsernameToken you're already using.

  1. Return to InvoiceManagerForm.vb in the SecureInvoiceClient project.
     
  2. Return to the ConfigureProxy method you wrote earlier and instantiate a DerivedKeyToken supplying the UsernameToken to the constructor. 

  3. Add the DerivedKeyToken to the Tokens collection of the proxy's RequestSoapContext

  4. Instantiate a MessageSignature object, based on the new DerivedKeyToken and add this to the Elements collection of the proxy's RequestSoapContext.

  5. Your ConfigureProxy method should look something like this now:

    ...
    Private Sub ConfigureProxy(ByVal proxy As _  
      WebServicesClientProtocol) proxy.RequestSoapContext.
    Security.Tokens.Add(login.
    Token)
      
    Dim dk As New DerivedKeyToken(login.Token)   
       proxy.RequestSoapContext.
    Security.Tokens.Add(dk)  
       proxy.RequestSoapContext.
    Security.Elements.Add(
    _
          New MessageSignature(dk))
    End Sub 'ConfigureProxy
    ...


  6. Build and run SecureInvoiceClient. Verify that everything still works as before.

  7. Return to Windows Explorer and open the output trace file C:Microsoft Hands-on-LabDEV-HOL34VBExercisesAbeforeSecure
    InvoiceClientbinOutputTrace.
    webinfo
    and notice that the last SOAP request was signed using the provided UsernameToken.

  8. Close OutputTrace.webinfo.

  9. Since the message is signed with a UsernameToken, the signature itself can be used as proof of possession, thereby removing the need to send a password altogether. Return to login.vb and, in the button1_Click method, change the password option from PasswordOption.SendHashed to PasswordOption.SendNone.

  10. Build, run, test, and verify that everything still works.

Requiring a Signature

The only problem with this situation is that the WebMethod's don't current require a signature. And if the user doesn't provide a signature or a password, the infrastructure will let the request through. Try commenting out the line of code that adds the signature and rerun the client application. Provide bad password and verify that the request still works. You'll have to add code to each WebMethod to require a signature like you did before for UsernameTokens.

  1. Open WseSecurityHelpers.vb in the SecureInvoiceServiceA project.

  2. Add a new method to the WseSecurityHelpers class named CheckForSignature. It should take a SoapContext as input and return void as illustrated here:

    ...
    Public Shared Sub CheckForSignature(ByVal context
    As SoapContext)
    End Sub
    ...
     

  3. Within the method, write code to check the SoapContext for a MessageSignature object. It will be found in the SoapContext.Security.Elements collection if present. If a signature is not present, throw a SoapException indicating "missing signature".

    ...
    Public Shared Sub CheckForSignature(ByVal context
    As SoapContext)
       If context Is Nothing Then
          Throw New Exception("Only SOAP requests are permitted.")
       End If
       If context.Security.Elements.Count = 0 Then
    Throw New SoapException("Missing signature",
    _   
    soapException.ClientFaultCode) 
       End If
       Dim foundSignature As Boolean = False
       Dim se As ISecurityElement
       For Each se In context.Security.Elements
          If TypeOf se Is MessageSignature Then 
             foundSignature = True
          End If
       Next se
       If Not foundSignature Then
          Throw New SoapException("Missing signature",  
     SoapException.ClientFaultCode) 
       End If

    End Sub 'CheckForSignature
    ... 

  4. At the beginning of the GetUsernameToken method in the same class, add a call to this new method. Since all of the WebMethod's currently call GetUsernameToken, we know this method will also be called.

         Public Shared Function GetUsernameToken(ByVal context _
            As SoapContext) As UsernameToken 
      
    CheckForSignature(context)
     

  5. Build the project and test invoking the client without a signature. Verify that you get the "missing signature" exception. Test invoking the client with a signature, and without providing a password (PasswordOption.SendNone), and verify that it works.

Encrypting with a UsernameToken

In addition to authentication and signatures, you may also wish to encrypt the message in order to ensure confidentiality (and prevent eavesdropping). In this step, you'll add code to encrypt the SOAP messages with the UsernameToken you're already using.

Note: encrypting messages with a UsernameToken is not very secure. You would typically use a binary security token (e.g., X.509 certificate) to perform the encryption. We'll cover this approach later. 

  1. Open InvoiceManagerForm.vb in the SecureInvoiceClient project.
  2. Return to the ConfigureProxy method you wrote earlier and add an EncryptedData object, based on the UsernameToken, to the proxy. 
  3. Your ConfigureProxy method should look something like this now:

    ...
    Private Sub ConfigureProxy(ByVal proxy As _  
      WebServicesClientProtocol) proxy.RequestSoapContext.
    Security.Tokens.Add(login.
    Token)
       Dim dk As New DerivedKeyToken(login.Token)  
       proxy.RequestSoapContext.
    Security.Tokens.Add(dk)  
       proxy.RequestSoapContext.
    Security.Elements.Add(
    _
          New MessageSignature(dk)) 
      
    proxy.RequestSoap Context.
    Security.Elements.Add(
          New EncryptedData(dk)) End Sub 'ConfigureProxy
    ...
     
  4. Build and run SecureInvoiceClient. Verify that everything still works as before.
  5. Open the output trace file (OutputTrace.webinfo) in the application directory (SecureInvoiceClientbin) and notice that the last SOAP request was both signed and encrypted using the UsernameToken information.
  6. Close OutputTrace.webinfo.

    Note: the message should look much different at this point, and you shouldn't be able to read the contents of the body since it's now encrypted.

Requiring Encryption

At this point, the service does not require encryption. If you invoke an operation without providing an EncryptedData element, it still works. In this step, you'll write code to require encryption. 

  1. Open WseSecurityHelpers.vb in the SecureInvoiceServiceA project.
  2. Add a new method to the WseSecurityHelpers class named CheckForEncryption. It should take a SoapContext as input and return void as illustrated here:

    ...
    Public Shared Sub CheckForEncryption(ByVal context
    As SoapContext)
       ...
    End Sub
    ...

  3. Within the method, write code to check the SoapContext for an EncryptedData object. It will be found in the SoapContext.Security.Elements collection if present. If it's not present, throw a SoapException indicating "encryption required".

     ...
    Public Shared Sub CheckForEncryption(ByVal context
    As SoapContext)
       If context Is Nothing Then
          Throw New Exception("Only SOAP requests are permitted.")
       End If
       If context.Security.Elements. Count = 0 Then
    Throw New SoapException("Encryption
    required", _  
       SoapException.ClientFault Code)
       End If
       Dim foundEncryption As Boolean = False
       Dim se As ISecurityElement
       For Each se In context.Security.Elements
          If TypeOf se Is EncryptedData Then 
             foundEncryption = True
          End If
       Next se
       If Not foundEncryption Then
          Throw New SoapException("Encryption required",   
            SoapException.Client Fault Code)
       End If

    End Sub
    ...
     
  4. Modify the GetUsernameToken method in the same class to call this new method. Since all of the WebMethod's currently call GetUsernameToken, we know this method will also be called.

          Public Shared Function GetUsernameToken(ByVal context _
           As SoapContext) As UsernameToken 
       CheckForSignature(context)
       CheckForEncryption(context)
     
  5. Build the project and test invoking the client ensuring that it works as before.
  6. Close the Invoice Manager application. 
  7. Click File | Close Solution.

Please check back next week for the continuation of this article.

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 1 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials