Protect Your Info

Secure Web Service data with the WSE.

Xtreme Data

LANGUAGES: C#

TECHNOLOGIES: XML | WSE | SOAP | XML Encryption

 

Protect Your Info

Secure Web Service data with the WSE.

 

By Dan Wahlin

 

Security is an important part of any application, whether it is Web-based, client-server, a Windows Service, or something entirely different. Experienced developers usually build some type of security model into the initial architecture of their applications, but even more emphasis is being placed on security in today's connected world. To help developers increase their awareness of different security techniques, Microsoft has released a new document that outlines different steps developers should take to secure applications. You can read this more than 600- page(!) document on the MSDN site at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp?frame=true.

 

There are several different aspects to security. Beyond that, security may have a different meaning for everyone. A wide variety of application types must be secured, including intranet, extranet, and Internet sites and B2B data exchanges. Security can combine many different technologies, such as authentication and authorization, impersonation, data-access security, and data and communications security.

 

So, how does all this apply to Web Services? Because a given Web Service could be responsible for verifying who the consumer of the service is, authentication, and even authorization, might be necessary. There are several different ways to handle this, including the use of SOAP headers, HTTP authentication (basic or digest), and other custom solutions. In one of my previous asp.netPRO columns, Practice Safe Web Services, I discussed using SOAP headers and the Web Service Development Kit (WSDK) for Web Service authentication. Since I wrote the WSDK authentication article, the WSDK has been officially released and renamed Web Service Enhancements (WSE).

 

In this article, I'll introduce you to another aspect of security that can play a crucial role when a Web Service accepts or exposes sensitive data. You can use more than one technique to protect and secure data as it is sent from a client to a Web Service. Using Secure Sockets Layer (SSL) is arguably the most popular way to handle this. As a SOAP message is transmitted across the wire, it can be encrypted to protect against prying eyes that might intercept the network packets. Though this works well, it does add additional processing because of the need to encrypt and decrypt data. I've heard several people argue that hardware is cheap and that using SSL in high-load situations can be a viable solution assuming the network and servers are in place to handle the load. Even though more and more hardware can be thrown at high-load applications that use SSL, there is another inherit problem with using SSL as Web Services become more and more complex. SSL is a point-to-point solution for data and communications security. If a Web Service receives data it must pass to one or more Web Services behind the scenes, there is no guarantee those communications will be secure.

 

To help solve this problem, Microsoft and the other members of the Web Services Security group have outlined a technique you can use to secure SOAP messages between multiple points. The solution is defined as being "end-to-end," meaning the integrity and security of different messages can be maintained throughout the entire transfer process even if more than one Web Service is involved. The technology responsible for securing data while it is transferred is called XML Encryption (http://www.w3.org/Encryption/2001/). Another technology, named XML Signature (http://www.w3.org/Signature/), can be used to ensure that no entity tampers with the data as it is transferred from point to point. In this article, I'll detail how you can encrypt sensitive data that is sent to and from Web Services using WSE.

 

Configure Web.Config

To start securing data within SOAP messages, you need to download the WSE from http://msdn.microsoft.com/webservices/building/wse/default.aspx. After installing the SDK, you'll need to edit your Web Service application's web.config file and add the lines of code shown in Figure 1 within the element.

 

  

    type="Microsoft.Web.Services.Configuration.

    WebServicesConfiguration, Microsoft.Web.Services,

    Version=1.0.0.0, Culture=neutral,

    PublicKeyToken=31bf3856ad364e35" />

 

  

    

      input="inputTrace.config"

       output="outputTrace.config"/>

  

  

    

    

  

Figure 1. The code shown here must be added into the web.config file in order for WSE functionality to be available for use within client and Web Service classes. One of the most important sections in this code is the element, which defines the password-provider and decryption-key-provider classes. You'll learn more about creating a decryption-key-provider class later in this article.

 

This code enables logging of Request and Response SOAP messages to a file and allows more detailed error messages to appear. Within the element, you'll see that the type definitions for a password-provider class and a decryption-key-provider class are provided. You'll learn about the decryption-key-provider class in a moment. I covered the password-provider class in Practice Safe Web Services.

Within the tag, add this XML fragment:

 

  

    

      priority="1" group="0"/>

  

 

This allows the WSE processing pipeline to intercept and work with incoming and outgoing SOAP messages.

 

Write a Decryption-Key Class

There are two widely accepted techniques for working with keys that encrypt and decrypt data. These include private keys (symmetric encryption) and public-private keys (asymmetric encryption). This sample will demonstrate how to use symmetric keys. It's important to note that the WSE supports asymmetric encryption through the use of X.509 certificates and also supports the use of custom binary tokens. You can find more details about these alternatives in the WSE help documentation.

You can use a symmetric (private) key both to encrypt and decrypt data. As a result, both the client and the Web Service must have a copy of the same private key for the encryption of SOAP messages to work correctly between the two entities. Figure 2 demonstrates how this process works.

 


Figure 2. Encryption and decryption of SOAP messages can be accomplished by using a private key. Both the consumer and the Web Service must have a copy of the key for the exchange of data to be successful.

 

This raises a question: How do both sides get a copy of the key without someone else intercepting it? Although a complete discussion of this topic is beyond the scope of this article, the key exchange can be accomplished securely through several different means, one of which involves using asymmetric key encryption. You can read more about asymmetric encryption in the .NET SDK.

 

Assuming both entities involved in the exchange of SOAP messages have access to the same private key, a special class must be written that will be used by the Web Service to decrypt SOAP Request messages sent by a client. The class must implement an interface found in the WSE named IDecryptionKeyProvider, which defines a single method named GetDecryptionKey:

 

GetDecryptionKey(string algorithmUri, KeyInfo keyInfo) \\{\\}

 

The WSE framework calls this method automatically when a SOAP message containing XML Encryption information is received. The framework knows how to find the class because of the WSE security information defined in the web.config file (you saw this earlier in Figure 1).

 

The algorithmUri parameter in the GetDecryptionKey method will be passed a string containing information about the type of encryption. For the application shown in this article, algorithmUri will contain a value of http://www.w3.org/2001/04/xmlenc#tripledes-cbc, which represents Triple DES encryption. The keyInfo parameter will contain information about the key used to encrypt the data in the SOAP message, such as the key's name. Figure 3 shows the complete code for the decryption key class named DecryptionKeyProvider. If you look through the code, you'll see that it locates the name of the key and creates a new private key by reading a Base64 encoded string from the web.config file.

 

public class DecryptionKeyProvider : IDecryptionKeyProvider \\{

  public DecryptionKeyProvider() \\{\\}

 

  public DecryptionKey GetDecryptionKey(string algorithmUri,

    KeyInfo keyInfo) \\{

    foreach (KeyInfoClause clause in keyInfo ) \\{

      if (clause is KeyInfoName ) \\{

        if (((KeyInfoName)clause).Value

          "XMLforASP.NET Symmetric Key") \\{

          //Store this in a more secure store

          //(db for instance) in a real app!!!

          string keyData =

          ConfigurationSettings.AppSettings\\["symmetricKey"\\];

 

          if ( keyData

null )

            throw new Exception("Symmetric key " +

             "not found in configuration.");

 

          //Key is Base64 encoded in the

          //web.config file. Convert the key to a

          //byte array which can be used to create

          //a new private key object

          byte\\[\\] keyBytes =

            Convert.FromBase64String(keyData);

          return new

            SymmetricDecryptionKey(TripleDES.Create(),

            keyBytes);

        \\}

        else \\{

          throw new ApplicationException("Key name " +

          "not supported.");

        \\}

      \\}

    \\}  

    return null;      

  \\}

\\}

Figure 3. The DecryptionKeyProvider class implements the IDecryptionKeyProvider interface, which defines a single method named GetDecryptionKey. The WSE framework calls this class automatically when it needs a private key that will be used to decrypt a SOAP message. The type and assembly name of the DecryptionKeyProvider class must be defined in the web.config file.

 

It's important to note that the utmost security should be applied to the private key because anyone who has access to it could use it to encrypt and decrypt data. You'll want to investigate how your company recommends (or requires) that keys be stored. This example stores the key in the web.config file only to keep things simple.

 

Create the Web Method

Now that the DecryptionKeyProvider class is ready to be used by the Web Service, you can begin adding Web Methods that a client can consume. For this example, we'll create a Web Service that has a Web method named GetCustomers (see Figure 4). I have added several comments throughout the code to explain the more important parts.

 

\\[WebMethod\\]

public DataSet GetCustomers(string customerID) \\{

  if (EnsureUserName

     (HttpSoapContext.RequestContext.Security)) \\{

    //Encrypt Response SOAP message body so

    //dataset data can't be read by prying eyes.

    //Get private key from web.config file

    EncryptionKey key = GetEncryptionKey();          

    HttpSoapContext.ResponseContext.Security.Elements.Add(

      new EncryptedData(GetEncryptionKey()));

 

    //Add Time to Live to prevent tampering

    //This response will only be "useable" for

    //60 seconds

    HttpSoapContext.ResponseContext.Timestamp.Ttl = 60000;

 

    DataSet ds = new DataSet("NorthwindCustomers");

    string sql =

      "SELECT * FROM CUSTOMERS WHERE CustomerID='" +

      customerID + "'";

    string connStr =

      ConfigurationSettings.AppSettings\\["connStr"\\];

    SqlConnection dataConn = new SqlConnection(connStr);

    SqlDataAdapter da = new SqlDataAdapter(sql,dataConn);

     da.Fill(ds,"Customers");

    return ds;

  \\} else \\{

    throw new

      SoapFormatException("No user name specified!");

  \\}

\\}

Figure 4. The GetCustomers Web method accepts a CustomerID as a parameter and uses it to query a database and return a data set to the client. By using WSE classes, the message is encrypted automatically via a private key.

 

GetCustomers starts out by calling a method that checks to see that the client sent a username in the SOAP header. This technique is used along with a special password-provider class (see Practice Safe Web Services) to authenticate the client. If the client is authenticated successfully, the data sent from the method is encrypted by adding this code:

 

EncryptionKey key = GetEncryptionKey();          

HttpSoapContext.ResponseContext.Security.Elements.Add(

  new EncryptedData(GetEncryptionKey()));

 

//Add Time to Live to prevent tampering

//This response will only be "useable"

//for 60 seconds

HttpSoapContext.ResponseContext.Timestamp.Ttl =

  60000;

 

The first few lines create a new symmetric key that can be used to encrypt the SOAP Response message. This is done by calling a custom method named GetEncryptionKey. Figure 5 contains the complete GetEncryptionKey method.

 

private EncryptionKey GetEncryptionKey() \\{          

  //Store this in a more secure store in a "real" app!!!

  string keyData = ConfigurationSettings.AppSettings\\["symmetricKey"\\];

  if (keyData == null) \\{

     throw new

      Exception("Symmetric key not found in config.");

  \\}

 

  //Convert key string to a byte array

  byte\\[\\] keyBytes = Convert.FromBase64String(keyData);

  //Create a new symmetric key from the byte array

  SymmetricEncryptionKey key =

     new SymmetricEncryptionKey(TripleDES.Create(),

    keyBytes);

 

  //Give the key a name

  KeyInfoName keyName = new KeyInfoName();

  keyName.Value = "XMLforASP.NET Symmetric Key";

  key.KeyInfo.AddClause(keyName);

 

  return key; //Return the key to the caller of the method

\\}

Figure 5. This method reads a Base64 encoded key from the web.config file and uses the value to create a new EncryptionKey object.

 

After the symmetric key is created, it is added to the Response context security elements. With these few lines of code, the data within the Response SOAP message will be encrypted automatically. The rest of the GetCustomers method simply handles making a call to the database to return customer data based upon the customer ID the client has passed to the Web Method.

 

Create the Web Client

Sending encrypted data within a SOAP Request message to a Web Service requires a minor modification to the client's Web Service proxy class. After generating the proxy (using VS .NET or WSDL.exe), the proxy class must be changed to inherit from Microsoft.Web.Services.WebServicesClientProtocol in order for it to work correctly with the different WSE classes. This will allow access to objects such as RequestSoapContext and ResponseSoapContext. The proxy class should look like this after making the changes:

 

public class ProxyName :

Microsoft.Web.Services.WebServicesClientProtocol \\{

   //Proxy code goes here

\\}

 

After making this change to the proxy, the client can use it to send and receive encrypted data to and from the Web Service. Figure 6 shows what WSE-specific code needs to be added to the client application for data exchange to occur successfully.

 

private void btnSubmit_Click(object sender,

  System.EventArgs e) \\{

  try \\{

    //Create proxy        

    SymmetricEncryptionProxy proxy =

      new SymmetricEncryptionProxy();

    //Add auth token

    UsernameToken token =

      new UsernameToken(txtUserName.Text,txtPassword.Text,

      PasswordOption.SendHashed);

    proxy.RequestSoapContext.Security.Tokens.Add(token);

 

    //Encrypt SOAP message body so customerID

    //can't be read by prying eyes

    EncryptionKey key = GetEncryptionKey();

    proxy.RequestSoapContext.Security.Elements.Add(

      new EncryptedData(key));

 

    //Add Time to Live to prevent tampering

    proxy.RequestSoapContext.Timestamp.Ttl = 60000;

 

    //Call WebService and bind data to grid

    DataSet ds = proxy.GetCustomers("ALFKI");

    dg.DataSource = ds;

    dg.DataBind();

    this.lblOutput.Text = String.Empty;

    this.pnlDataGrid.Visible = true;

 

  \\} catch (Exception exp) \\{

    this.lblOutput.Text = exp.Message;

    this.pnlDataGrid.Visible = false;

  \\}

\\}

Figure 6. The client application calls the Web Service using a slightly modified proxy object as discussed earlier. Once the proxy is instantiated, the private key is loaded and used to encrypt the SOAP Request message.

 

Aside from the authentication code in Figure 6, you'll notice that the encryption piece looks almost identical to that shown earlier in the GetCustomers Web method (see Figure 4). The key to encrypting the message is assigning the EncryptionKey object to the RequestSoapContext through its Security property:

 

EncryptionKey key = GetEncryptionKey(); //See Figure 5

proxy.RequestSoapContext.Security.Elements.Add(

    new EncryptedData(key));

 

Figure 7 shows what the client sees after data is returned from the Web Service and is decrypted.

 


Figure 7. The client ASP.NET application is authenticated by the Web Service based upon a specific user name and password. After logging in, the CustomerID for a customer is encrypted and passed to the Web Service. The resulting data returned from the Web Service is decrypted by the client and then is bound to a DataGrid control.

 

You might be wondering what the SOAP Request and Response messages look like once the data is encrypted. Although I won't go into detail about the different elements and attributes here in the interest of space, this article's downloadable code contains a sample Request and Response message that you can examine.

 

Before the release of the WS-Security specification, SSL was one of the only options for securing SOAP messages as they were transferred on the wire (excluding custom solutions). With the release of the WSE, this type of functionality is possible with a relatively small amount of code. One of the many benefits of WS-Security is that SOAP messages contain embedded security information, which means an end-to-end solution can be developed allowing messages to be secured even when passed between multiple Web Services. To view a live example of the WSE functionality discussed in this article, visit http://www.xmlforasp.net/codeSection.aspx?csID=83.

 

References

 

This article's sample code is available for download.

 

Dan Wahlin received Microsoft's Most Valuable Professional award in the ASP.NET category. He is president of Wahlin Consulting and founded the XML for ASP.NET Developers Web site (http://www.XMLforASP.NET), which focuses on using XML and Web Services in Microsoft's .NET platform. He also is a corporate trainer and speaker, and he teaches XML and ASP.NET training courses around the United States. Dan co-authored the books Professional Windows DNA (Wrox) and ASP.NET: Tips, Tutorials & Code (Sams), and he wrote XML for ASP.NET Developers (Sams). E-mail Dan at [email protected].

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish