Securing SOA with WSE 2.0

 

In the previous article I discussed how WSE 2.0 implements the WS-Security specification. This article will demonstrate how to use this implementation to add security to the service-oriented Mortgage Loan Service application.

 

Now that you understand how WSE 2.0 implements the WS-Security specification, the next step is to add security to the Mortgage Loan Service.

 

Mortgage Loan Service Security Architecture

 

Trust is the degree to which one entity believes the claims of another, or allows another to undertake actions on its behalf. Think of how you decide whom you trust. You might trust a messenger with a small amount of money to go to the store on your behalf. For large sums sent to the bank, you might do it yourself, or demand a bonded courier. How any individual or organization makes such decisions is called their trust model. In the service oriented world, each entity has to have its own trust model. These entities might be companies, divisions of a company, or individuals. Each such entity is said to be a separate trust domain.

 

In the mortgage example, the Credit Agency, the Bank, and the Loan officer are different trust domains. Hence, there are two major trust boundaries in the mortgage example. The first is between the Loan Officer and the Bank. The second is between the Bank and the Credit Agency. The level of trust within the Bank is much higher, than the level of trust between the Credit Agency and the Bank, or the Bank and the Loan officer.

 

Determining which applications and users a service trusts is a major design decision for any service oriented application.  One problem that has to be solved is to what degree security tokens created by one trust domain, and sent to another trust domain, can be relied upon. In the Mortgage Loan example, the loan officers and the bank are identified by X509 certificates. Who issues them? How does the Bank get the list of current loan officers? How does the bank find out about a new loan officer, or one that gets fired? How do the loan officers know the X509 certificate for the bank was really issued by the bank? The WS-Trust specification builds on WS-Security to allow security tokens to be issued and exchanged between two services.  Often a third party is required. To simplify the example I will hard code the list of acceptable X509 certificates in the application.

 

The previous version of the Mortgage example consisted of a Loan Officer client application, and a very primitive Mortgage service. To develop the example further to illustrate security, I will break up the Mortgage service into two parts. The actual decision about whether to grant the mortgage will be made by the Mortgage Decision service. The security decisions about authenticating and authorizing the loan officers will be made by the Bank Portal service. If the Loan Officer is authorized to make the mortgage application, the Portal service will forward the application to the Mortgage Decision service.

 

This division of labor is a common technique in more sophisticated SOA based applications. The Bank typically offers multiple services, not just one as in this example. Each service does not want to duplicate the common tasks of authentication and authorization. The portal provides a single point of entry. The portal authenticates the security credentials associated with the request (in the example X509 certificates). If the credentials can be authenticated, the portal then decides if the user can be authorized to access the service it is requesting. The portal uses the WS-Addressing action header to determine the service being requested. If the identity sending the message is authorized, the portal will forward the request to the appropriate decision service. The Bank Portal is an example of a SOAP intermediary.

 

Using a portal also protects the decision objects from denial of service attacks, and allows the URL of the decision service to be easily moved. In fact, the portal could do load balancing among several identical decisions services if necessary.  The portal could route a message to a decision object based on the level of service appropriate for the user. For example, premium customers might be routed to a different service than regular customers. Other users and services within the Bank’s trust domain might be able use the decision service directly without going through the portal.

 

The Mortgage Decision Service and the Bank Portal will be in the same trust domain, and will exchange messages through a trusted connection. As a result, in this example these two services will not validate each other.

 

Mortgage Loan Application Architecture

 

The Loan Officer client application will interact with the Portal in two phases. In the first phase the client and the Portal will mutually authenticate each other. The client will send up an X509 certificate associated with the Loan Officer that made the application. If the Portal recognizes this certificate as being associated with an authorized user of the Portal, the Portal will send its certificate back to the client application. The client will then know that it is using the correct URL for the Portal. Normally these certificates would be encrypted, or this interaction would happen over an encrypted channel (such as https). For simplicity of exposition this is not done here. To illustrate the use of ASP.NET with WSE 2.0, this first step is implemented using ASP.NET technology.

 

In the second phase, the client application will send up an application that is encrypted with the X509 certificate associated with the Mortgage Decision service, not the Bank Portal. Only the ultimate recipient should be able to read the Mortgage Application. The client application will also sign the data so that Portal knows who is sending the application. If the Portal authorizes the sender, the encrypted application will be sent on to the Mortgage Decision service. This service will never see the client X509 certificates as the Portal will be remove them from the SOAP headers in the message. The portal never looks at the encrypted application data.

 

This version of the Mortgage Loan Application is developed in four steps; each step adds a particular piece of the solution. Except for one minor modification, the Mortgage Decision Service will be unchanged from the previous article. The readme.txt files have the setup instructions for each step. Make sure that you follow the instructions for each step properly.

 

First Step: Setting up the Bank Portal

 

The first step adds the Bank Portal to the application. The portal has two code files. The first is a standard .asmx file that implements a WebMethod Verify.  For this first step it just returns a string “Verified.”. The second part, implemented in BankPortal.cs is the portal itself. The portal class, BankPortal is derived from the Microsoft.Web.Services2.Message.SoapHttpRouter class. Its location is defined in the web.config file to be BankPortal.ashx. The derived class overrides the ProcessRequestMessage method which allows the portal to do any work that it must do to process the message. In the first step it just returns the URL to where the message will be forwarded. In this case, it will be the URL of the Mortgage Decision Service. In another case it could be the URL of another intermediary.

 

As with the Mortgage Decision Service, the BankPortal endpoint address (BankPortal.ashx) is added to the web.config file handlers so that requests coming in on the endpoint can be mapped to the correct assembly and class. Figure 1 shows how to do this within Visual Studio WSE 2.0 Settings property dialog.

 

Figure 1 Adding an HTTP Handler to web.config.

 

The resulting entry in web.config looks like this:

 

  <httpHandlers>

      <add type="BankPortal, BankPortal"

                  path="BankPortal.ashx" verb="*" />

   </httpHandlers>

 

Since the BankPortal also uses ASP.NET, the “Enable Microsoft Web Service Enhancement SOAP Extensions” dialog box has been on the first tab of the WSE 2.0 Settings property dialog.

 

The Mortgage Client has two changes to it. To access the verification method, the Add Web Reference menu item in Visual Studio.NET is used. However, since this is a WSE 2 project, two proxies are created. One is the usual ASP.NET proxy derived from SoapHttpClientProtocol. The second is a WSE 2 proxy derived from Microsoft.Web.Services2.WebServicesClientProtocol. This proxy will be used in subsequent steps to access the SoapContext object to access and modify the appropriate SOAP headers. This first step uses the SoapHttpClientProtocol derived proxy.

 

If the client verification of the portal succeeds, the client sends the application to the proxy.

 

string verify = new VerifyBankPortal.Verify();

 

string verify = VerifyBankPortal.Verify();

txtResponse.Text = verify;

if (verify == "Verified")

{

application.RequestedLoan =

                  Convert.ToDecimal(txtAmount.Text);

  string result = MortgageRequest.SendMortgageRequest();

  txtResponse.Text = result;

}

 

The key change from the previous version is how the SendMortgageRequest method sends the message to the portal. Since the URL of the Mortgage Decision service is unknown, a URN, urn:MortgageRequest is used as an address in the SOAP message. On the other hand, the message has to be sent to the portal’s URL.  The EndpointReference class has two properties that can be used to instruct the WSE infrastructure. The to property is the URN for the Mortgage Decision service. The via property is the URL for the portal.

 

Uri via = new Uri(

          "http://localhost/Portal1/BankPortal.ashx");

Uri to = new Uri("urn:MortgageRequest");

EndpointReference endpointReference = new

                           EndpointReference(to, via);

sender = new SoapSender(endpointReference);

 

The Mortgage Decision service’s URN will be the To address in the WS-Addressing header. The message, however, will be sent to the Bank Portal’s URL. The remainder of the client is unchanged from the previous version.

 

Second Step: Using X509 Certificates

 

Follow the instructions in the readme.txt file for this step to install the certificates. For more information about certificate security, Microsoft Windows 2003 and PKI Certificate Security by Brian Komar, et. al from Microsoft Press, is a good reference. Both the Mortgage Client and the Bank Portal have a code file certificate.cs which contains the code to obtain the X509 certificates from within the certificate stores that they were installed. The code for authenticating and authorizing a certificate in the method IsCertificateValid is very primitive. The code basically checks the certificate name against a hard-coded list.

 

Figure 2 shows the security settings for the certificates. I have selected “Verify trust” to make sure the certificates are validated against the trust authority that issued them. Since I have created my own test certificates using the Test trust authority, I have also selected “Allow test roots.” This allows “Verify trust” to work against test certificates. Obviously, do not set this in a production environment.

 


Figure 2 Security Settings for the Bank Portal certificates.

 

The portal code checks for the existence of the certificates by iterating through the collection of security tokens looking for an appropriate certificate. Here is the code from the verify code in verify.asmx.

 

  X509Certificate cert = null;

  bool valid = false;

 

  SecurityTokenCollection requestTokens =

           RequestSoapContext.Current.Security.Tokens;

              

  // check to see if known client

  foreach (SecurityToken token in requestTokens)

  {

    X509SecurityToken tok = token as X509SecurityToken;

    if (tok != null)

    {

      cert = tok.Certificate;

      string name = Certificate.

                       IsClientCertificateValid(cert);

      if (name != null)

      {

         valid = true;

          break;

      }

    }

  }

 

Note how the SoapContext instance is obtained from the property of the RequestSoapContext.Current. If you are working with straight WSE code, as in the ProcessClientCredentials code, you can get the context from the SOAP message. The verification code just adds the Bank Portal’s certificate by adding to the token collection in the ResponseSoapContext.Current. That is the SoapContext instance for the outgoing message. This step uses the WSE version of the ASP.NET proxy.

 

The Mortgage Client uses similar code to check for the Portal’s certificate and to add its own certificate to its outgoing message. However since this certificate is being examined by an intermediary, and not the ultimate destination, I use the ExtendedSecurity property of the SoapContext. The Security property is designed to create headers for the ultimate recipient as will be demonstrated in the next step. The actor specified in the Security constructor is the actor defined in the SOAP specification for an intermediary.

 

// create security headers for intermediary

Security security = new Security("http://schemas.xmlsoap.org/soap/actor/next");

 

// Add client certificate

X509Certificate clientCert = Certificate.GetCertificate(Certificate.ClientCertName);

X509SecurityToken clientToken = new X509SecurityToken(clientCert);

security.Tokens.Add(clientToken);    

 

request.Context.ExtendedSecurity.Add(security);

 

The actor indicates that the header should be processed by the intermediary, not the ultimate recipient. Note that WSE 2.0 uses the SOAP 1.1 protocol. In SOAP 1.2, the term actor has been changed to role.

 

Third Step: Encrypting the Data For the Ultimate Recipient

 

The Mortgage Client encrypts the body with the public key of the ultimate recipient, the Mortgage Decision Service. Here the Security object, not the ExtendedSecurity object is used.

 

//encrypt data for final destination

X509Certificate bankCert = Certificate.GetCertificate(Certificate.BankCertName);
X509SecurityToken bankToken = new X509SecurityToken(bankCert);
EncryptedData encryptedData = new EncryptedData(bankToken);
request.Context.Security.Elements.Add(encryptedData);

 

If you look at the SOAP headers in the diagnostic output, you will see two security headers. One has the actor “next” which is the header examined by the intermediary. The other, that has no actor, is the security header for the ultimate destination.

 

No other changes have to be made to the application to get this to work!

 

Nonetheless, I did add some code to the Mortgage Decision service to require that the mortgage application be encrypted.

 

bool foundEncryptedData = false;

foreach(ISecurityElement se in

                 message.Context.Security.Elements)

{

  if (se is EncryptedData)

  {

        foundEncryptedData = true;

        break;

  }

}

 

if (foundEncryptedData == false)

  throw new SoapException("Message should be

          encrypted.", SoapException.ClientFaultCode); 

 

Since the portal does not act on the header for the ultimate destination, it does not need to figure out how to decrypt the body. At the Mortgage Decision Service, the WSE infrastructure decrypts the data before it is passed to the service’s code. In other words, if the portal was on one computer, and the decision service was on another, only the X509 certificate for the appropriate service would have to be installed on each computer.

 

Fourth Step: Signing the Data for Portal Authentication and Authorization

 

The final step is the sign the data so that the Bank Portal can verify it was added by a recognized Loan Officer. 

 

// sign the data

MessageSignature signedData = new MessageSignature(clientToken);

security.Elements.Add(signedData);

 

The Mortgage Client code adds this to the ExtendedSecurity object. The Bank Portal can then examine the signature to make sure it is valid.

 

// require signed data

bool foundSignedData = false;

foreach (ISecurityElement se in envelope.Context.

                                    Security.Elements)

{

  if (se is MessageSignature)

  {

    MessageSignature ms = se as MessageSignature;                         

    X509SecurityToken st = ms.SigningToken as

                                    X509SecurityToken;

    X509Certificate cert = st.Certificate;

    string name = Certificate.

                       IsClientCertificateValid(cert);

    if (name != null)

    {

      foundSignedData = true;

      break;

    }

  }

}

 

if (foundSignedData == false)

  throw new SoapException("Message should be signed",

                       SoapException.ClientFaultCode);            

Conclusion

 

This discussion illustrates the basic principles of using WSE 2.0 to add security to a service oriented application. Obviously, this article does not exhaust the issues that must be considered, but it does provide a basic understanding of what must be done. The next article will talk about adding policy to this application when I add the Credit Agency service.

 

All Content (c) 2000 - 2014 Reliable Software, Inc. All rights reserved.