17.3 Extending the .NET Framework

In the following sections, we complete our implementation of the ElGamal algorithm by creating the key exchange formatter and deformatter classes. We have only provided a C# implementation of the ElGamal key exchange formatters. Like almost all encryption algorithms, ElGamal relies on mathematical operations that are not possible in Visual Basic .NET without creating additional support functions to compensate for the limited numeric support the language provides.

17.3.1 ElGamal Key Exchange Explained

Our implementation of the ElGamal encryption functions exposes the "raw" algorithm; that is, unlike the Microsoft RSA implementation, our ElGamalManaged class does not format data prior to encryption.

To provide support for session key exchange, we must provide classes that extend the AsymmetricExchangeFormatter and AsymmetricExchangeDeformatter classes that we discussed in the previous section. We have selected the OAEP formatting scheme for our key exchange; we have listed the complete code for our formatting classes, which naturally includes the details of the OAEP scheme. We do not discuss the details of OAEP, and you should refer to the PKCS #1 standard (which defines both the OAEP and PKCS #1 v1.5 formatting schemes) for details.

17.3.2 Defining the Key Exchange Formatter

We begin by defining ElGamalOAEPKeyExchangeFormatter, which is our formatter class, extending the AsymmetricKeyExchangeFormatter class that we discussed in the previous section:

using System; using System.IO; using System.Security.Cryptography; public class ElGamalOAEPKeyExchangeFormatter : AsymmetricKeyExchangeFormatter {     private ElGamalManaged              o_algorithm;                  private Random                      o_random;                 private PKCS1MaskGenerationMethod   o_mask_generator;

The constructor creates an instance of the ElGamal implementation class, ElGamalManaged. As you see in the SetKey method, you can import the keys from any implementation class that extends the abstract ElGamal class using the common parameter import and export methods. The other instance variables are related to the OAEP formatting process:

        public ElGamalOAEPKeyExchangeFormatter(  ) {                 // create the instance of the algorithm         o_algorithm = new ElGamalManaged(  );         // init the rnd         o_random = new Random(  );         // init the mask generator         o_mask_generator = new PKCS1MaskGenerationMethod(  );         }

The Parameters property returns the parameters of the public key that will be used to encrypt the session key; we create the result by using the ToXmlString method defined in the AsymmetricAlgorithm class (see Chapter 15 for details):

    public override string Parameters {         get {             return o_algorithm.ToXmlString(false);         }     }

The SetKey method allows the user to specify an instance of the ElGamal algorithm that should be used for encryption; if the class is not an instance of our abstract ElGamal class, throw an exception; otherwise, import the public key parameters into the ElGamal instance created by the constructor:

    public override void SetKey(AsymmetricAlgorithm p_key) {         // encure that we are dealing with an ElGamal algorithm         if (p_key is ElGamal) {            // export the key and push it into the algorithm            o_algorithm.ImportParameters(((ElGamal)p_key).ExportParameters(false));         } else {            // we can't continue because the algorithm            // is the one for this class            throw new ArgumentException("Key Algorithm is not ElGamal", "p_key");         }     }

The CreateKeyExchange method is responsible for formatting the session key and encrypting the result using the ElGamal implementation class; there are two overloaded versions of this method, but they are functionality equivalent, because the differentiating argument is not currently used by the .NET Framework:

    public override byte[] CreateKeyExchange(byte[] p_byte, Type p_type) {         // the Type parameter is not curently supported by the          // .NET Framework, so we just need to work with the byte array         return CreateKeyExchange(p_byte);     }     public override byte[] CreateKeyExchange(byte[] p_byte) {         // create the OAEP padded data         byte[] x_padded_data = CreateOAEPPaddedData(p_byte);                 // create the ciphertext from the padded data         byte[] x_ciphertext = o_algorithm.EncryptData(x_padded_data);         // return the ciphertext         return x_ciphertext;     }

The remaining methods in this class are responsible for applying the OAEP formatting, which is outside the scope of this book:

    // the lHash value which is the preamble to an OAEP block     private byte[] o_lhash        = new BigInteger("da39a3ee5e6b4b0d3255bfef95601890afd80709", 16).getBytes(  );          private byte[] CreateOAEPPaddedData(byte[] p_data) {         // create a memory stream to hold the padded data         MemoryStream x_stream = new MemoryStream(  );         // define K         int x_K = o_algorithm.KeySize/8 -1;         // determine the block size         int x_max_bytes = x_K - (2 * o_lhash.Length) - 2;         // determine how many complete blocks there are         int x_complete_blocks = p_data.Length / x_max_bytes;         // run through and process the complete blocks         int i = 0;         byte[] x_block;         for (; i < x_complete_blocks; i++) {             x_block = CreateSingleOAEPBlock(p_data, i * x_max_bytes,                  x_max_bytes, x_K);             x_stream.Write(x_block, 0, x_block.Length);         }         // process any remaining data         x_block = CreateSingleOAEPBlock(p_data, i * x_max_bytes,              p_data.Length - (i * x_max_bytes), x_K);         x_stream.Write(x_block, 0, x_block.Length);         // return the padded data         return x_stream.ToArray(  );     }     private byte[] CreateSingleOAEPBlock(byte[] p_data, int p_offset,          int p_count, int p_K) {         // b.        Generate an octet string PS consisting of         // k - mLen - 2hLen - 2 zero octets.          // The length of PS may be zero.         byte[] x_PS = new byte[p_K - p_count - (2*o_lhash.Length) -2];         // c.        Concatenate lHash, PS, a single octet with          // hexadecimal value 0x01, and the message M to form a data          // block DB of length k - hLen - 1 octets as         // DB = lHash || PS || 0x01 || M .          byte[] x_DB = new byte[o_lhash.Length + x_PS.Length + 1 + p_count];         Array.Copy(o_lhash, 0, x_DB, 0, o_lhash.Length);         Array.Copy(x_PS, 0, x_DB, o_lhash.Length, x_PS.Length);         x_DB[o_lhash.Length + x_PS.Length] = 0x01;         Array.Copy(p_data, p_offset, x_DB,              o_lhash.Length + x_PS.Length + 1, p_count);         // d.        Generate a random octet string seed of length hLen         BigInteger x_temp = new BigInteger(  );         x_temp.genRandomBits(o_lhash.Length * 8, o_random);         byte[] x_seed = x_temp.getBytes(  );         // e.        Let dbMask = MGF (seed, k - hLen - 1)         byte[] x_dbMask = o_mask_generator.GenerateMask(x_seed,              p_K - o_lhash.Length -1);         // f.        Let maskedDB = DB XOR dbMask.         byte[] x_maskedDB = new byte[x_DB.Length];         byte[] x_temp_arr =              (new BigInteger(x_DB) ^ new BigInteger(x_dbMask)).getBytes(  );         Array.Copy(x_temp_arr, 0, x_maskedDB,              x_maskedDB.Length - x_temp_arr.Length, x_temp_arr.Length);         // g.        Let seedMask = MGF (maskedDB, hLen).         byte[] x_seedMask = o_mask_generator.GenerateMask(x_maskedDB,              o_lhash.Length);         // h.        Let maskedSeed = seed XOR seedMask.         byte[] x_maskedSeed              = (new BigInteger(x_seed) ^ new BigInteger(x_seedMask)).getBytes(  );         // i. Concatenate a single octet with hexadecimal value 0x00, maskedSeed,          // and maskedDB to form an encoded message EM of length k octets as         // EM = 0x00 || maskedSeed || maskedDB         byte[] x_EM = new byte[1 + o_lhash.Length + x_DB.Length];         Array.Copy(x_maskedSeed, 0, x_EM, x_EM.Length - x_DB.Length              - x_maskedSeed.Length, x_maskedSeed.Length);         Array.Copy(x_maskedDB, 0, x_EM,  x_EM.Length - x_maskedDB.Length,              x_maskedDB.Length);         // return the result         return x_EM;     } }

17.3.3 Defining the Key Exchange Deformatter

To complete the ElGamal implementation, define the ElGamalOAEPKeyExchangeDeformatter class, which decrypts the exchange data; this class extends the AsymmetricKeyExchangeDeformatter class:

using System.Security.Cryptography; using System.IO; using System; public class ElGamalOAEPKeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter {     private ElGamalManaged              o_algorithm;              private PKCS1MaskGenerationMethod   o_mask_generator;

The constructor creates an instance of the ElGamal implementation class, ElGamalManaged. As you see in the SetKey method, you can import the keys from any implementation class that extends the abstract ElGamal class using the common parameter import and export methods. The other instance variables are related to the OAEP formatting process:

        public ElGamalOAEPKeyExchangeDeformatter(  ) {         // create the instance of the algorithm         o_algorithm = new ElGamalManaged(  );         // init the mask generator         o_mask_generator = new PKCS1MaskGenerationMethod(  );         }     public override void SetKey(AsymmetricAlgorithm p_key) {         // encure that we are dealing with an ElGamal algorithm         if (p_key is ElGamal) {             // export the key and push it into the algorithm             o_algorithm.ImportParameters(((ElGamal)p_key).ExportParameters(true));         } else {             // we can't continue because the algorithm             // is the one for this class             throw new ArgumentException("Key Algorithm is not ElGamal", "p_key");         }     }

The Parameters property returns the parameters of the private key that will be used to decrypt the exchange data; create the result by using the ToXmlString method defined in the AsymmetricAlgorithm class (see Chapter 15 for details). Unlike the equivalent property in the formatter class, the deformatter implementation must also import the key to use through the Parameters property, which you do through the FromXmlString method:

    public override string Parameters {         get {             return o_algorithm.ToXmlString(true);         }         set {             o_algorithm.FromXmlString(value);         }     }     public override byte[] DecryptKeyExchange(byte[] p_byte) {         // decrypt the ciphertext         byte[] x_padded = o_algorithm.DecryptData(p_byte);         // remove the OAEP padding         byte[] x_plaintext = RestoreOAEPPaddedData(x_padded);         // return the ciphertext         return x_plaintext;     }

The remaining methods in this class are responsible for removing the OAEP formatting, which is outside the scope of this book:

    // the lHash value which is the preamble to an OAEP block     private byte[] o_lhash        = new BigInteger("da39a3ee5e6b4b0d3255bfef95601890afd80709", 16).getBytes(  );         private byte[] RestoreOAEPPaddedData(byte[] p_data) {         // create a memory stream to hold the padded data         MemoryStream x_stream = new MemoryStream(  );         // define K         int x_K = o_algorithm.KeySize/8 -1;         // determine how many complete blocks there are         int x_blocks = p_data.Length / x_K;         // run through and process the blocks blocks         byte[] x_block;         for (int i = 0; i < x_blocks; i++) {             x_block = RestoreSingleOAEPBlock(p_data, i * x_K, x_K, x_K);             x_stream.Write(x_block, 0, x_block.Length);         }                 // return the padded data         return x_stream.ToArray(  );     }     private byte[] RestoreSingleOAEPBlock(byte[] p_data, int p_offset,          int p_count, int p_K) {         // b. Separate the encoded message EM into a single octet Y,          // an octet string maskedSeed of length hLen, and an octet          // string maskedDB of length k - hLen - 1          // as EM = Y || maskedSeed || maskedDB         byte[] x_maskedSeed = new byte[o_lhash.Length];         Array.Copy(p_data, p_offset + 1, x_maskedSeed, 0, o_lhash.Length);         byte[] x_maskedDB = new byte[p_K - o_lhash.Length -1];         Array.Copy(p_data, p_offset + 1 + o_lhash.Length, x_maskedDB,              0, x_maskedDB.Length);         // c.        Let seedMask = MGF (maskedDB, hLen).         byte[] x_seedMask              = o_mask_generator.GenerateMask(x_maskedDB, o_lhash.Length);         // d.        Let seed = maskedSeed XOR seedMask         byte[] x_seed = (new BigInteger(x_maskedSeed)              ^ new BigInteger(x_seedMask)).getBytes(  );         // e.        Let dbMask = MGF (seed, k - hLen - 1).         byte[] x_dbMask              = o_mask_generator.GenerateMask(x_seed, p_K - o_lhash.Length -1);         // f.        Let DB = maskedDB XOR dbMask.         byte[] x_DB = (new BigInteger(x_maskedDB)              ^ new BigInteger(x_dbMask)).getBytes(  );         // g.        Separate DB into an octet string lHash' of length hLen, a          // (possibly empty) padding string PS consisting of octets with          // hexadecimal value 0x00, and a message M as          // DB = lHash' || PS || 0x01 || M .         // If there is no octet with hexadecimal value 0x01 to separate PS from M,          // if lHash does not equal lHash', or if Y is nonzero,          // output "decryption error" and stop.          for (int i = 0; i < o_lhash.Length; i++) {             if (x_DB[i] != o_lhash[i]) {                 throw new CryptographicException("Decryption Error");             }         }         if (p_data[0] != 0) {             throw new CryptographicException("Decryption Error");         }         // find the index representing the start of M         int x_index = -1;         for (int i = o_lhash.Length; i < x_DB.Length; i++) {             if (x_DB[i] == 0x01) {                 // the next index is the start of M                 x_index = i + 1;                 break;             } else if (x_DB[i] != (byte)0x00) {                 throw new CryptographicException("Decryption Error");             }         }         if (x_index == -1) {             throw new CryptographicException("Decryption Error");         }         // extract and return M         byte[] x_message = new byte[x_DB.Length - x_index];         Array.Copy(x_DB, x_index, x_message, 0, x_message.Length);         return x_message;     } }


Programming. NET Security
Programming .Net Security
ISBN: 0596004427
EAN: 2147483647
Year: 2005
Pages: 346

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