Signing and Encrypting Parts of the Message


So far when we’ve looked at signing and encrypting messages that we’re sending, we’ve accepted the defaults for what part of each message is signed and encrypted. In most cases, this is fine because you won’t need to specify other parts of the message for signing or encrypting.

However, in some cases you’ll want to sign or encrypt other parts of the message. A prime example is when you use SOAP headers. The header might contain information that you need to secure, and the default options won’t do this. You need to be able to modify the default signing and encrypting options for the message. Thankfully, WSE allows you to do just that.

We’ll build an example, PartsClient, that sends a message containing a SOAP header to the Web service at http://localhost/wscr/15/partsws.asmx. The example allows you to specify the header you’re sending and then choose to sign or encrypt the message. On receiving the message, the Web service displays the details of the header and then iterates through the signature and encryption details to tell you what was signed or encrypted.

Signing Parts of the Message

As you’ve seen, when you sign a SOAP message using the defaults, you sign the body of the message. The defaults for signing actually go further than this—you actually sign six other elements within the message.

If you’ve enabled the Routing and Timestamp output filters, you also sign the parts of the SOAP header that they add to the message. For the Routing filter, you sign the action, from, id, and to elements; for the Timestamp filter, you sign the created and expires elements. In most cases these will satisfy your requirements, but if you need to change what is signed by default, you can modify the SignatureOptions property of the Signature object that you’re signing with.

The SignatureOptions property is a bit field that allows you to specify which parts of the message you want to sign. There is also a SignatureOptions enumeration that allows you to easily specify what you want to sign by combining the different values of the enumeration. These values are described in Table 15-3.

Table 15-3: The SignatureOptions Enumeration

Member

Description

IncludeNone

Do not sign anything in the message.

IncludePath

Sign the entire WS-Routing header.

IncludePathAction

Sign the action element of the WS-Routing header.

IncludePathFrom

Sign the from element of the WS-Routing header.

IncludePathId

Sign the id element of the WS-Routing header.

IncludePathTo

Sign the to element of the WS-Routing header.

IncludeSoapBody

Sign the body of the message.

IncludeTimestamp

Sign the entire WS-Timestamp header.

IncludeTimestampCreated

Sign the created element of the WS-Timestamp header.

IncludeTimestampExpires

Sign the expires element of the WS-Timestamp header.

If you choose not to sign the entire body of the message or you need to sign any SOAP headers that you add to the message, you must specify individually what parts of the message you want to sign. You’ll see in the next example how to do this.

Encrypting Parts of the Message

When you encrypt a message, the only part of the message that is encrypted by default is the message body. Unlike signing parts of the message, when you encrypt a message you make the encrypted part unreadable. Encrypting certain parts of the message, such as the WS-Routing headers, would generate a message that might be incorrect. For example, only the ultimate recipient of the message can decrypt the message, so any routers that the message had to pass through would not be able to read the necessary SOAP header.

You might, however, need to encrypt SOAP headers that you add to the message to ensure that the contents of that header cannot be read. As you’ll soon see, WSE allows you to specify what you want to encrypt.

The Client

PartsClient, the client we’ll use for this example, is similar to X509Client, which we looked at earlier. It allows you to specify the message you want the Web service to echo and to specify whether to sign or encrypt the message using X509 certificates. As you can see in Figure 15-6, you can also specify a Number header.

click to expand
Figure 15-6: The PartsClient application

Most of the code for encrypting and signing the message is the same as what you saw for the X509Client. We just need to add code to tell WSE that we want to sign and encrypt the new header that we’ve added.

As you’ll see when we look at the code for the Web service, we have a header with two exposed properties—Id and Number. The Number property is the value that we want to pass as the header, and the Id property is a value that we use to tell WSE which element we want to sign or encrypt. Without the Id property, we’d have no way of telling WSE which element we’re referring to. As in the WS-Security specification, the ID of an element must be a unique valid GUID prefixed with Id:, and we can use Guid.NewGuid to retrieve one. We populate the header with the correct details and store the GUID we just created so we can use it to tell WSE that we want to sign and encrypt the header:

// create the GUID that we need for the header string headerId = "Id:" + Guid.NewGuid().ToString(); // create the header and add it to the call to the proxy NumberHeader numHeader = new NumberHeader(); numHeader.Id = headerId; numHeader.Number = txtNumber.Text; proxy.NumberHeaderValue = numHeader;

To sign this header, we create a reference to the ID of the element we want to sign and add it to the signature we’re using to sign the message. The reference to the element that we want to sign is an instance of the Reference class, and we use the GUID that we defined earlier and prefix it with the # symbol:

// create the signing details certSignature = new Signature(secSignToken); // we want to sign the header Reference refHeader = new Reference("#" + headerId); certSignature.AddReference(refHeader); // now add the signature to the message proxy.RequestSoapContext.Security.Elements.Add(certSignature);

Unlike signing, where we can use the existing signature and simply use this to sign an additional part of the message when we are encrypting, we have to create a completely new instance of the EncryptedData class, specifying which element we want to encrypt. To do this, we call a different version of the EncryptedData constructor, passing in the ID of the element we want to encrypt as well as the SecurityToken we want to use:

// encrypt the default message elements encData = new EncryptedData(secEncryptToken); proxy.RequestSoapContext.Security.Elements.Add(encData); // we also want to encrypt the header encData = new EncryptedData(secEncryptToken, "#" + headerId); proxy.RequestSoapContext.Security.Elements.Add(encData);

We’ve told WSE to sign and encrypt the body of the message and the header that we added to the message, and we can call the proxy in the same way we always have.

The Web Service

When the message gets to the server, the Security input filter goes through its normal process and checks the signature of the message and decrypts the parts of the message that need decrypting, populating the request’s SoapContext as it goes.

As with the X509Client application, we can loop through the Security.Elements collection of the request SoapContext to extract the security information that was added to the message.

If the item in the Security.Elements collection is a Signature object, we know that at least part of the message was signed. We loop through the SignedInfo.References collection of the signature to determine what parts of the message were signed, and we use the TargetElement.Name to return the namespace prefixed element that was signed. We simply concatenate the element name to a string that contains all of the elements in the message that are signed:

if(secElement is Signature) {     // cast to a real Signature object     Signature secSignature = (Signature)secElement;     // loop through each element in the message     foreach (Reference refElement in secSignature.SignedInfo.References)     {         // add the prefixed name to the output string         strSigned = strSigned + refElement.TargetElement.Name + ", ";     }     // at least some of the message was signed     boolSigned= true; }

When we check the encrypted details, we use roughly the same process. Each element that is encrypted has its own entry in the Security.Elements collection. We cast the element to an EncryptedData object and retrieve the data from the TargetElement.Name property of the EncryptedData object:

if(secElement is EncryptedData) {     // cast to a real EncryptedData object     EncryptedData secEncrypted = (EncryptedData)secElement;     // add the prefixed name to the output string     strEncrypted = strEncrypted + secEncrypted.TargetElement.Name + ", ";     // at least some of the message was encrypted     boolEncrypted = true; }

We then use the Boolean flags that were set to create the message that is returned to the caller.




Programming Microsoft. NET XML Web Services
Programming MicrosoftВ® .NET XML Web Services (Pro-Developer)
ISBN: 0735619123
EAN: 2147483647
Year: 2005
Pages: 172

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net