Shared Key Encryption


The approach to encryption that we looked at in the previous section is asymmetric encryption—the keys to encrypt and decrypt the message are different. We used X509 certificates as the basis for the encryption and used the different parts of the certificate to encrypt and decrypt the message. It is also possible to encrypt and decrypt messages symmetrically—using the same key for decryption that you use for encryption. However, this relies on the transmission of the shared key having taken place between the client and the server. Whereas you can make the public key of the X509 certificate available for everyone to use and feel safe in the knowledge that only the holder of the private key can decrypt the messages sent, when you use shared key encryption you must ensure that the key is kept as secure as possible because all senders can decrypt data that is encrypted using the shared key. If you have a shared key that is known to everyone, you effectively have no encryption at all.

To use a shared key to encrypt the message, you create an EncryptionKey object and use it rather than an X509SecurityToken object to initialize the EncryptedData object. You can add this EncryptedData object to the Security.Elements collection, as we did earlier.

When you create the encryption key, you can use any of the algorithms that descend from the SymmetricAlgorithm class in the System.Security.Cryptography namespace. The current release of .NET includes implementations of the DES, RC2, Rijndael, and Triple-DES algorithms, but only two of these, Triple-DES and Rijndael, can be used as the basis for our shared encryption key.

Note

A full discussion of symmetric encryption and the algorithms provided by .NET in the System.Security.Cryptography namespace is beyond the scope of this book. A good reference tome for cryptography is Applied Cryptography: Protocols, Algorithms, and Source Code in C, Second Edition by Bruce Schneier (John Wiley & Sons, 1996). It’s in C rather than C# and is slightly out-of-date, but it contains everything you need to know to get started with cryptography. Also, several articles on encryption and what is provided by .NET are available on the MSDN site—a good starting point is at http://msdn.microsoft.com/library/en-us/cpguide/html/cpconcryptographyoverview.asp.

For this example, we’ll create a new client application, SharedKeyClient, and a new Web service at http://localhost/wscr/15/sharedkeyws.asmx. We’ll encrypt only the message to the server and return the results as plain text, but as you’ll see, the process by which we tell WSE that we want to encrypt the message is simple—and not a lot of code changes are required to encrypt the return.

The Shared Key

We’ll use the Rijndael algorithm as our shared key; all of the code for generating the necessary encryption and decryption keys is in the SharedKey class in the helper DLL. To use a shared key, we need to create instances of the SymmetricEncryptionKey and SymmetricDecryptionKey classes that we can use to encrypt and decrypt the messages that we’re sending. As with X509 certificates, most of the work to encrypt and decrypt the messages is handled automatically by WSE, and all we need to provide is the correct keys.

To create the keys, we simply pass the correct algorithm to the constructor of the SymmetricEncryptionKey or SymmetricDecryptionKey class. We’re using a shared key, and we’re using the same algorithm for both the encryption and decryption keys, so we place this code into its own method that can be called as required. Within this method we create the algorithm and set the key and initialization vector to whatever values we choose. (You should make a better choice than we have here!) The code for this method follows.

private static SymmetricAlgorithm getAlgorithm() {     // create instance of the correct algorithm     SymmetricAlgorithm algo = new RijndaelManaged();     // set the key and IV for the algorithm     byte[] keyBytes = {1,2,4,8,16,32,64,128,128,64,32,16,8,4,2,1};     byte[] ivBytes =  {2,4,6,8,10,12,14};     algo.Key = keyBytes;     algo.IV = ivBytes;     // return the algorithm     return (algo); }

We can then use the algorithm that is returned to create the correct decryption and encryption keys that we need.

To create the encryption key, we create a new instance of the SymmetricEncryptionKey class and pass the algorithm that we’re using to the constructor. We must also add a KeyInfo clause to the key so that when we come to decrypt the message we know which encryption algorithm we used. The KeyInfoName object that we add as the clause can be any arbitrary value, but we must ensure that both the client and the server are using the same name.

public static EncryptionKey GetEncryptionKey() {     // create new EncryptionKey     SymmetricEncryptionKey keyEncrypt = new         SymmetricEncryptionKey(getAlgorithm());     // add the ID details to the key     KeyInfoName keyName = new KeyInfoName();     keyName.Value  = "www.notashop.com/wscr encrypted";     keyEncrypt.KeyInfo.AddClause(keyName);                  // return the EncryptionKey in question     return keyEncrypt; } 

Creating the decryption key is a lot simpler because the determination of which key we need to use has already been made before the call to create the key. We simply create a new instance of the SymmetricDecryptionKey class, passing the correct algorithm to the constructor:

public static DecryptionKey GetDecryptionKey() {     // create new DecryptionKey and return     return (new SymmetricDecryptionKey(getAlgorithm())); }

The Client

The client application we’ll use for this example follows the same process as the clients we used for the earlier examples in this chapter. We simply enter the message we want the server to echo back and click the Echo button. Unlike in the other examples, however, we don’t have to specify how we want the message encrypted—all the messages we send will be encrypted to the encryption key that is hardcoded at both the client and the server.

The code for encrypting the message is the same as the code you saw for encrypting the message using an X509 certificate, except instead of using the X509 certificate you use the key that you retrieve from the helper class to generate the EncryptedData object:

// create the proxy class SharedKeysWSWse proxy = new SharedKeysWSWse(); // get the encryption key EncryptionKey keyEncrypt = SharedKey.GetEncryptionKey(); //  create the encryption details and add to the message EncryptedData encData = new EncryptedData(keyEncrypt); proxy.RequestSoapContext.Security.Elements.Add(encData); // make the call MessageBox.Show(proxy.Echo(txtMessage.Text), "Returned message");

As we pointed out earlier, all of the work required to encrypt the message is handed internally by WSE.

The Web Service

When we looked at decrypting messages that were encrypted using X509 certificates, you saw that you don’t have to write any code on the server for the decryption to take place. The decryption is performed automatically by the Security input filter, and control is passed to the calling method only if the decryption is successful.

When you use shared keys for encryption, the decryption also takes place automatically, but you have to let the Security input filter know what the encryption key is so it knows how to decrypt the message. You do this by creating a class that implements the IDecryptionKeyProvider interface.

This interface exposes one method, GetDecryptionKey, which returns the key that will decrypt the message that has been received. The key is determined from the KeyInfo element that is passed to the method.

Within the KeyInfo object is a series of KeyInfoClause elements that correspond to the details that have been added to the encryption key. We iterate through each of these to determine the key that we need to provide the key for:

public DecryptionKey GetDecryptionKey(string algorithmUri, KeyInfo keyInfo) {     foreach ( KeyInfoClause clause in keyInfo )     {

In the earlier code we added a KeyInfoName element to the encryption key, and now we’ll look for that key. If we find this key, we’ll return the decryption key provided by the helper class; if we find a key that we can’t decode, we’ll throw an exception.

        // are we using a shared encryption key         if ( clause is KeyInfoName )         {             switch (((KeyInfoName)clause).Value)             {                 case "www.notashop.com/wscr encrypted":                 {                     return(SharedKey.GetDecryptionKey());                 }                 default:                 {                     throw new ApplicationException                         ("Unsupported encryption key");                 }             }         }

If we’re worried only about being able to decrypt messages that are encrypted using shared keys, simply adding new case statements to the above code will handle all possible situations. But if we need to be able to decrypt X509 certificates within the same application, as we have for the four Web services we’ve used in this chapter, we must add another piece of code to the loop through the KeyInfo collection.

Adding a handler for the IDecryptionKeyProvider interface to an application removes the application’s ability to automatically decrypt messages. We need to be able to handle the situation in which we receive a message encrypted using an X509 certificate. We simply look for a SecurityTokenReference and determine whether it is an X509 certificate. If it is, we create an instance of the default implementation of the IDecryptionKeyProvider interface, the DecryptionKeyProvider class, and call the GetDecryptionKey method, passing in the parameters we received:

        // manually handle X509 Security as          // we've broken the default handler         if (clause is SecurityTokenReference)         {             switch(((SecurityTokenReference)clause)                      .KeyIdentifier.ValueType.Name)             {                 case "X509v3":                 {                     DecryptionKeyProvider Decryptor =                          new DecryptionKeyProvider();                     return(Decryptor.GetDecryptionKey                         (algorithmUri,keyInfo));                 }                 default:                 {                     throw new ApplicationException                         ("Unsupported encryption key");                 }             }         }     }

If we go through the loop without finding any keys in the message, something has gone wrong and we won’t be able to decrypt the message. We throw an exception that will be passed to the caller:

    // if we get here we can't decode the message     throw new ApplicationException("Unknown encryption key"); }

Although we now have a method to provide the decryption key, we still haven’t hooked it to the Security input filter. To do this, we must add an entry to the <security> element in the configuration file. We add a <decryptionKeyProvider> element that points at the correct type to instantiate:

<security>   <decryptionKeyProvider type="notashop.wscr.Helper.DecryptKey, Helper" /> </security> 




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