RsaKeyGen.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / infocard / Service / managed / Microsoft / InfoCards / RsaKeyGen.cs / 1 / RsaKeyGen.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace Microsoft.InfoCards
{ 
    using System;
    using System.Collections; 
    using System.Collections.Generic; 
    using System.Reflection;
    using System.Runtime.InteropServices; 
    using System.Security.Cryptography;
    using System.IO;
    using System.Text;
    using System.Diagnostics; 

    using Microsoft.InfoCards.Diagnostics; 
    using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace; 

 
    //
    // Summary:
    // Utility class for RSA Key Pair Generation
    // 
    sealed class RsaKeyGen
    { 
 
//////////////////////////////////////////////////////////////////
// Key pair size (size of the modulus n) is fixed at 2048 bits 
//////////////////////////////////////////////////////////////////
        const int Xpq12Length        = 14;
        const int XpqLength          = 128;
        const int NumberOfSha1Hashes = 16; 
        const int InfoCardKeyLength  = 2048;
 
        // 
        // Overall approach
        // Since the msbignum code wants little endian byte order for large integers Xp and Xq 
        // (which are laid out as a sequence of bytes), we want to generate Xp and Xq in little endian.
        //
        // The spec says to generate
        // H4 (big endian) + H5 (big endian) + H6 (big endian) + H0[4 MSB] (big endian) 
        // + H10 (big endian) + H11 (big endian) + H12 (big endian) + H2[4 MSB] (big endian)
        // 
        // In little endian this equals: 
        //  Array.Reverse( H4 (big endian) + H5 (big endian) + H6 (big endian) + H0[4 MSB] (big endian)
        //              + H10 (big endian) + H11 (big endian) + H12 (big endian) + H2[4 MSB] (big endian) ) 
        //
        // Which is the same as (note the order of H has been reversed):
        //   H2[4 MSB] (little endian) +  H12 (little endian) +  H11 (little endian) + H10 (little endian)
        //   + H0[4 MSB] (little endian) +  H6  (little endian) +  H5  (little endian) + H4 (little endian) 
        //
        // Therefore the approach is to generate each of the H's in little endian, and then 
        // append them in reverse order [H2 H12 H11 H10 H0 H6 H5 H4] compared to the big endian order [H4 H5 H6 H0 H10 H11 H12 H2] 
        // that is detailed in the spec.
        // 
        static int[ ] hashesToWriteP = new int[ 8 ] { 2, 12, 11, 10, 0, 6, 5, 4 };
        static int[ ] numBytesToWriteP = new int[ 8 ] { 4, HnLength, HnLength, HnLength,
                                               4, HnLength, HnLength, HnLength };
        static int[] srcIndexBytesToWriteP = new int[ 8 ] { 16, 0, 0, 0, 16, 0, 0, 0 }; 

        static int[ ] hashesToWriteQ = new int[ 8 ] { 3, 15, 14, 13, 1, 9, 8, 7 }; 
        static int[ ] numBytesToWriteQ = numBytesToWriteP; 
        static int[ ] srcIndexBytesToWriteQ = srcIndexBytesToWriteP;
//////////////////////////////////////////////////////////////// 
// Uncomment below if we want asymm key pairs to be 1024 bits.
////////////////////////////////////////////////////////////////
//
//        const int Xpq12Length        = 14; 
//        const int XpqLength          = 64;
//        const int NumberOfSha1Hashes = 16; 
//        const int InfoCardKeyLength  = 1024; 
//
//        static int[ ] hashesToWriteP = new int[ 4 ] { 0, 6, 5, 4 }; 
//        static int[ ] numBytesToWriteP = new int[ 4 ] { 4, HnLength, HnLength, HnLength };
//        static int[ ] srcIndexBytesToWriteP = new int[ 4 ] { 16, 0, 0, 0 };
//
//        static int[ ] hashesToWriteQ = new int[ 4 ] { 1, 9, 8, 7 }; 
//        static int[ ] numBytesToWriteQ = numBytesToWriteP;
//        static int[ ] srcIndexBytesToWriteQ = srcIndexBytesToWriteP; 
// 
////////////////////////////////////////////////////////////////
 

        // Size of a SHA1 hash in bytes
        const int HnLength = 20;
 
        // A recommended public exponent value
        const int PublicExponentValue = 65537; 
 
        // The encryption key used when regenerating input params
        const string Aes128EncryptionKey = "f561e58b-3ebc-4e0c-940d-0a6ddc219dfd"; 

        //
        // Represents the byte in which 2^100 is located.
        // 
        const int ByteForHundredthBit = 100/8;
 		 
	 

        // 
        // Set up AES 128 using Electronic Code Book mode
        //
        static RijndaelManaged s_rjAes128 = new RijndaelManaged();
 

        // 
        // Summary: 
        // Set up AES 128 using Electronic Code Book mode
        // 
        static RsaKeyGen()
        {
            s_rjAes128.Mode = CipherMode.ECB;
            s_rjAes128.Padding = PaddingMode.Zeros; 
            s_rjAes128.Key = ( new Guid( Aes128EncryptionKey ) ).ToByteArray();
        } 
 
        //
        // Summary: 
        // Implementation of the InfoCard RSA Key Gen Algorithm complying to
        // X9.31 to create a privatekey blob.
        //
        // Params: 
        // masterKey - M (infocard masterkey)
        // TargetId - T (target recipient entropy) 
        // 
        // Return:
        // The CryptoAPI PRIVATEKEYBLOB 
        //
        public static byte[ ] CreateRsaKeyPairX931( byte[ ] masterKey, byte[ ] targetId )
        {
 
            //
            // Sanity Check: Only 1024 or 2048 bit keys supported! 
            // 
            IDT.Assert( InfoCardKeyLength == InfoCard.KeySize, "Only 2048 bit key sizes supported" );
 

            //
            // Calculate all the Hn = n + T + M (LITTLE ENDIAN)
            // i.e. Hn = M + T + n (BIG ENDIAN) 
            //
            byte[ ][ ] hnArray = CalculateHn( masterKey, targetId ); 
 
            // Will be populate with the return blob
            byte[ ] cryptoBlob = null; 


            //
            // Allocate bytes that will contain the 6 integers. We'll copy these to 
            // native memory and then call the native RSA Key Gen function.
            // 
            // 
            // Question: Why create the extra copy?
            // Why not just copy over appropriate number of bytes from Hn to an IntPtr 
            // and pass that to native memory?
            //
            // Answer: We create Xp*ByteArray as local variables which comes in useful
            // when rsakeygen fails and we regenerate input params. So if Xp* 
            // need to be regenerated, they are copied into the original buffer locations
            // 
            byte[ ] Xp1ByteArray = new byte[ Xpq12Length ]; 
            byte[ ] Xp2ByteArray = new byte[ Xpq12Length ];
            byte[ ] Xq1ByteArray = new byte[ Xpq12Length ]; 
            byte[ ] Xq2ByteArray = new byte[ Xpq12Length ];
            byte[ ] XpByteArray = new byte[ XpqLength ];
            byte[ ] XqByteArray = new byte[ XpqLength ];
 
            //
            // Init Xp1ByteArray, Xp2ByteArray, Xq1ByteArray, Xq2ByteArray 
            // hnArray = 20 bytes, Xp1ByteArray = 14 bytes 
            //
            Array.Copy( hnArray[ 0 ], 0, Xp1ByteArray, 0, Xp1ByteArray.Length ); 
            Array.Copy( hnArray[ 1 ], 0, Xp2ByteArray, 0, Xp2ByteArray.Length );
            Array.Copy( hnArray[ 2 ], 0, Xq1ByteArray, 0, Xq1ByteArray.Length );
            Array.Copy( hnArray[ 3 ], 0, Xq2ByteArray, 0, Xq2ByteArray.Length );
 

            // 
            // Init XpByteArray - H2 H12 H11 H10 H0 H6 H5 H4 (little endian) 
            //
            // Sanity check: We want H4 as part of MSB. 
            // I.e. stored last in buffer in little endian.
            //
            WriteToArray( hashesToWriteP, numBytesToWriteP, srcIndexBytesToWriteP,
                hnArray, XpByteArray ); 

 
            // 
            // Init XqByteArray - H3 H15 H14 H13 H1 H9 H8 H7 (little endian)
            // 
            WriteToArray( hashesToWriteQ, numBytesToWriteQ, srcIndexBytesToWriteQ,
                hnArray, XqByteArray );

 
            try
            { 
 
                KeyGenRestartCodes restartCode = KeyGenRestartCodes.X931RestartNone;
 
                //
                // The chance of failure in each iteration is very rare.
                // So the likelyhood of 2 iterations failing in a row is
                // even smaller. So even a while( true ), should suffice. 
                // Plan to run this with lots of input combinations overnight
                // or determine actual probabilities 
                // to provide a confidence level in termination of algorithm 
                //
                do 
                {
                    //
                    // The X931KeyGen checks that the seeds will produce a
                    // modulus of the specified length, this means the upper 
                    // bits of the most significant byte needs to be set to 0xF0
                    // so that we get a 2048 bit modulus.  Confirmed with 
                    // Josh Benaloh on this is Ok. 
                    //
                    // Question: Why | with 0xC0 as opposed to 0x1000 ? 
                    // Answer: The problem is that 0x80 x 0x80 = 0x4000,
                    // so the high bit of the modulus is not set.  Since 0xC0 x 0xC0 = 0x9000,
                    // we guarantee the high bit is set.
                    // 
                    XpByteArray[ XpqLength - 1 ] |= ( byte ) 0xC0;
                    XqByteArray[ XpqLength - 1 ] |= ( byte ) 0xC0; 
 
                    //
                    // Make sure Xpq12 are greater than 2^100 as required by X9.31 
                    // The maximum i.e. Xpq12 < 2^120 is automatically met becuase
                    // Xpq12 are only 112 bits long.
                    //
                    Xp1ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10; 
                    Xp2ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10;
                    Xq1ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10; 
                    Xq2ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10; 

 
                    cryptoBlob = NativeMcppMethods.X931KeyGenWrapper(
                        Xp1ByteArray,
                        Xp2ByteArray,
                        Xq1ByteArray, 
                        Xq2ByteArray,
                        XpByteArray, 
                        XqByteArray, 
                        PublicExponentValue,
                        ref restartCode ); 

                    IDT.TraceDebug( "InfoCardKeyGen: Restart code is : {0}", restartCode );

                    switch ( restartCode ) 
                    {
                        case KeyGenRestartCodes.X931RestartNone: 
                            // Success 
                            IDT.Assert( null != cryptoBlob, "Cryptoblob should not be null since API succeeded"  );
                            break; 

                        // decryexp < 2^bitlen_pq
                        case KeyGenRestartCodes.X931RestartDTooSmall:
                            // As per Josh Benaloh, with e fixed at 65537, 
                            // d will be guaranteed to be much larger than 1/2 the length of n
                            IDT.Assert( null == cryptoBlob, "Cryptoblob should be null"  ); 
                            IDT.Assert( false, "Not expected for d < 2^1024 with e = 65537" ); 
                            InfoCardRegenerateParam( Xq1ByteArray );
                            InfoCardRegenerateParam( Xq2ByteArray ); 
                            InfoCardRegenerateParam( XqByteArray );
                            break;

                        // GCD(p1, p2) > 1 or GCD(q1, q2) > 1 
                        case KeyGenRestartCodes.X931RestartNonTrivialGcd:
                            IDT.Assert( null == cryptoBlob, "Cryptoblob should be null"  ); 
                            InfoCardRegenerateParam( Xp1ByteArray ); 
                            InfoCardRegenerateParam( Xp2ByteArray );
                            InfoCardRegenerateParam( Xq1ByteArray ); 
                            InfoCardRegenerateParam( Xq2ByteArray );
                            break;

                        // p or q > 2^bitlen_pq 
                        case KeyGenRestartCodes.X931RestartPQOverflow:
                            IDT.Assert( null == cryptoBlob, "Cryptoblob should be null"  ); 
                            InfoCardRegenerateParam( XpByteArray ); 
                            InfoCardRegenerateParam( XqByteArray );
                            break; 

                        // p1 or p2 or q1 or q2 > 2^X931_MAXBITS_PQ12
                        case KeyGenRestartCodes.X931RestartPQ12OverFlow:
                            IDT.Assert( null == cryptoBlob, "Cryptoblob should be null"  ); 
                            InfoCardRegenerateParam( Xp1ByteArray );
                            InfoCardRegenerateParam( Xp2ByteArray ); 
                            InfoCardRegenerateParam( Xq1ByteArray ); 
                            InfoCardRegenerateParam( Xq2ByteArray );
                            break; 

                        // abs(Xp - Xq) too small
                        case KeyGenRestartCodes.X931RestartXpXqClose:
                            IDT.Assert( null == cryptoBlob, "Cryptoblob should be null"  ); 
                            InfoCardRegenerateParam( XqByteArray );
                            break; 
 
                    }
 

                }while( restartCode != KeyGenRestartCodes.X931RestartNone );

                IDT.Assert( null != cryptoBlob, "Cryptoblob should NOT be null outside while loop" ); 

            } 
            finally 
            {
                // 
                // Clear out all arrays allocated
                //
                Array.Clear( Xp1ByteArray, 0, Xp1ByteArray.Length );
                Array.Clear( Xp2ByteArray, 0, Xp2ByteArray.Length ); 
                Array.Clear( Xq1ByteArray, 0, Xq1ByteArray.Length );
                Array.Clear( Xq2ByteArray, 0, Xq2ByteArray.Length ); 
                Array.Clear( XpByteArray, 0, XpByteArray.Length ); 
                Array.Clear( XqByteArray, 0, XqByteArray.Length );
 

            }

            return cryptoBlob; 

 
        } 

        // 
        // Summary:
        // Utility function to copy appropriate number of bytes to appropriate index
        // in destArray
        // 
        // hashesToWrite - represents index in sourceArrayOfHashes that we want to copy
        // numBytesToWrite - num of bytes to copy 
        // srcIndexBytesToWrite - index of bytes to begin copying from 
        // sourceArrayOfHashes - a 2d array of the SHA1 hashes we created
        // destArray - the buffer where we'll copy this to 
        //
        static void WriteToArray(
            int[ ] hashesToWrite,
            int[ ] numBytesToWrite, 
            int[ ] srcIndexBytesToWrite,
            byte[ ][ ] sourceArrayOfHashes, 
            byte[ ] destArray ) 
        {
            IDT.Assert( hashesToWrite.Length == numBytesToWrite.Length, "Size must be equal" ); 
            IDT.Assert( numBytesToWrite.Length == srcIndexBytesToWrite.Length, "Size must be equal" );

            int destIndex = 0;
            for ( int index = 0; index < hashesToWrite.Length; index++ ) 
            {
                int indexInHashArray = hashesToWrite[ index ]; 
                Array.Copy( 
                    sourceArrayOfHashes[ indexInHashArray ],
                    srcIndexBytesToWrite[ index ], 
                    destArray,
                    destIndex,
                    numBytesToWrite[ index ] );
 
                destIndex += numBytesToWrite[ index ];
            } 
        } 

        // 
        // Summary:
        // Create byte array of H(n) for n = 0, 1, 2, ...15
        // where H(n) = M + T + C(n) = M + T + n  in Big Endian.
        // 
        // Params:
        // masterKey - M (infocard masterkey) 
        // subjectEntropy - T (Recipient entropy) 
        //
        // Remarks: 
        // n - constant represented by C(n), must be in big endian.
        // M + T - does not really make sense if this means big endian or not becuase we're simply concatenating bytes
        //
        // Return: 
        // 16 byte arrays (representing H0, H1, ..., H15) each of length 20 (i.e. 160 bits)
        // 
        static byte[ ][ ] CalculateHn( byte[ ] masterKey, byte[ ] subjectEntropy ) 
        {
            byte[ ][ ] hnArray = new byte[ NumberOfSha1Hashes ][ ]; 

            //
            // Setup array common to all H(n)
            // 
            int byteArraySize = masterKey.Length + subjectEntropy.Length + sizeof( int );
            byte[ ] inputBytes = new byte[ byteArraySize ]; 
 
            //
            // Put in M 
            //
            Array.Copy( masterKey, 0, inputBytes, 0, masterKey.Length );

            // 
            // Put in T after M
            // 
            Array.Copy( subjectEntropy, 0, inputBytes, masterKey.Length, subjectEntropy.Length ); 

            // 
            // Now fill in n for all the Hn's
            //
            for ( uint n = 0; n < hnArray.Length; n++ )
            { 

                // 
                // Convert n to a byte and fill it in the last element of the array (since we want big-endian n) 
                // (We assert that n does not exceed a byte.)
                // 
                // POTENTIAL TEST ?
                // Passing in constant integer 2 (instead of n) means we'd generate identical Xp Xq etc.
                // which will cause the code the to shutle thru X931RestartXpXqClose, X931RestartNonTrivialGcd,
                // followed by success 
                //
                IDT.Assert( n <= 0xFF, "n must fit in a byte." ); 
                IDT.Assert( inputBytes.Length - 1 == masterKey.Length + subjectEntropy.Length + 3, "Index location of n must be last element of array." ); 
                inputBytes[ inputBytes.Length - 1 ] = ( byte ) n;
 
                //
                // SHA digest (an integer) is returned in big endian
                //
                hnArray[ n ] = SHA1.Create().ComputeHash( inputBytes ); 

                // 
                // Our code deals with little endian byte order (as detailed at the start of this file), so flip the bytes 
                //
                Array.Reverse( hnArray[ n ] ); 
            }

            Array.Clear( inputBytes, 0, inputBytes.Length );
 
            return hnArray;
        } 
 

 

        //
        // Summary:
        // Regenerates input params as per infocard key gen algorithm 
        // Copies new data on top of input array
        // 
        // Params: 
        // prevBytes - the byte array to encrypt
        // 
        // Return:
        // Nil, the input array contains the newly generated bytes
        //
        static void InfoCardRegenerateParam( 
            byte[ ] prevBytes
            ) 
        { 
            //
            // AESEncrypt previous bytes 
            //
            byte[ ] newBytes = AESEncryptECB( prevBytes );

            // 
            // Copy it into old memory
            // 
            Array.Copy( newBytes, 0, prevBytes, 0, prevBytes.Length ); 

            // 
            // Zero out previous buffer
            //
            Array.Clear( newBytes, 0, newBytes.Length );
 
        }
 
        // 
        // Summary:
        // Does AES 128 using Electronic Code Book mode 
        // Encrytion Key = C = f561e58b-3ebc-4e0c-940d-0a6ddc219dfd
        // Padding = zero-bits such that bit-length is a multiple of 128
        //
        // Params: 
        // plaintextbyte - the byte array to encrypt
        // 
        static byte[ ] AESEncryptECB( byte[ ] plaintextbyte ) 
        {
            // 
            // Create a CryptoStream from a memory stream
            //
            MemoryStream ciphertextmem = new MemoryStream();
            CryptoStream crystm = new CryptoStream( 
                ciphertextmem,
                s_rjAes128.CreateEncryptor(), 
                CryptoStreamMode.Write ); 

            // 
            // Get the cipher text
            //
            crystm.Write( plaintextbyte, 0, plaintextbyte.Length );
            crystm.Close(); 
            Byte[ ] ciphertextbyte = ciphertextmem.ToArray();
            ciphertextmem.Close(); 
 
            return ciphertextbyte;
        } 

        //
        // Summary:
        // This class is purely for utility purposes 
        //
        private RsaKeyGen() 
        { 
        }
 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK