RoamingStoreFileUtility.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 / RoamingStoreFileUtility.cs / 1 / RoamingStoreFileUtility.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace Microsoft.InfoCards 
{
    using System; 
    using System.IO; 
    using System.Security.Cryptography;
    using System.Text; 
    using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace;

    sealed class RoamingStoreFileUtility
    { 
        //
        // Define the constants used to derive the signing and encryption keys. 
        // 
        static readonly byte[] DerivedKeySignatureEntropy = { 0xc4, 0x01, 0x7b, 0xf1, 0x6b, 0xad, 0x2f, 0x42, 0xaf, 0xf4, 0x97, 0x7d, 0x4, 0x68, 0x3, 0xdb };
        static readonly byte[] DerivedKeyEncryptionEntropy = { 0xd9, 0x59, 0x7b, 0x26, 0x1e, 0xd8, 0xb3, 0x44, 0x93, 0x23, 0xb3, 0x96, 0x85, 0xde, 0x95, 0xfc }; 

        const Int32 ENCRYPTIONKEYBUFFERSIZE = 32;
        const Int32 ENCRYPTIONKEYBITLENGTH = ENCRYPTIONKEYBUFFERSIZE * 8;
 
        const Int32 ENCRYPTIONIVBUFFERSIZE = 16;
        const Int32 ENCRYPTIONIVBITLENGTH = ENCRYPTIONIVBUFFERSIZE * 8; 
 
        const Int32 ITERATIONCOUNT = 1000;
        const Int32 SHA256_BUFFERSIZE = 256 / 8; 

        public static int SaltLength
        {
            get 
            {
                return ENCRYPTIONIVBUFFERSIZE; 
            } 
        }
 
        private RoamingStoreFileUtility( )
        {
        }
 

        // 
        // Summary: 
        //  Calculates the size of the buffer needed to hold the encrypted data blob
        // 
        // Arguments:
        //  decryptedLength:  The length of the clear text data.
        //
        public static int CalculateEncryptedSize( int decryptedLength ) 
        {
            int encLength = decryptedLength; 
 
            //
            // Round to the nearest next block. 
            //
            encLength += ENCRYPTIONIVBUFFERSIZE - ( encLength % ENCRYPTIONIVBUFFERSIZE );

            // 
            // add the prefix info size
            // 
            encLength += ENCRYPTIONIVBUFFERSIZE + SHA256_BUFFERSIZE; 

            return encLength; 
        }

        //
        // Summary: 
        //  Calculates the size of the buffer needed to hold the decrypted data
        // 
        // Arguments: 
        //  encryptedLength:  The length of the cipher data blob.
        // 
        public static int CalculateDecryptedSize( int encryptedLength )
        {
            return encryptedLength - ( ENCRYPTIONIVBUFFERSIZE + SHA256_BUFFERSIZE );
        } 

 
        // 
        // Summary:
        //  Decrypts the source buffer using the specified password and salt. 
        //
        // Arguments:
        //  source:         The source data to decrypt.  Stream must be at position
        //                  0, and entire stream will be decrypted. 
        //                  Data written in the following pattern:
        //                  IV + Signature + CipherValue 
        //  destination:    The desination stream that will recieve the raw text data. 
        //  passwordString: The string password to use to encrypt the file with.
        //  salt:           The generated random value used to derive the keys. 
        //
        public static void Decrypt(
                                Stream source,
                                Stream destination, 
                                string passwordString,
                                byte[] salt ) 
        { 
            //
            // Genarate the key pair from the password. 
            //
            byte[] password = Encoding.Unicode.GetBytes( passwordString );
            byte[] encKey;
            byte[] sigKey; 
            try
            { 
                CreateKeyPair( password, salt, out encKey, out sigKey ); 
            }
            finally 
            {
                Array.Clear( password, 0, password.Length );
            }
 

            try 
            { 
                //
                // Read the IV. 
                //
                byte[] iv = new byte[ ENCRYPTIONIVBUFFERSIZE ];
                if( iv.Length != source.Read( iv, 0, iv.Length ) )
                { 
                    throw IDT.ThrowHelperError(
                                new ImportException( SR.GetString( SR.InvalidImportFile ) ) ); 
                } 

                using( RijndaelManaged algorithm = new RijndaelManaged( ) ) 
                {
                    //
                    // Setup our encryption alg for CBC
                    // 
                    algorithm.Padding = PaddingMode.PKCS7;
                    algorithm.Mode = CipherMode.CBC; 
                    algorithm.BlockSize = iv.Length * 8; 
                    algorithm.KeySize = encKey.Length * 8;
                    using( RijndaelManagedTransform decrypTransform = (RijndaelManagedTransform)algorithm.CreateDecryptor( encKey, iv ) ) 
                    {
                        using( SHA256Managed sha256 = new SHA256Managed( ) )
                        {
                            byte[] block = new byte[ decrypTransform.InputBlockSize ]; 
                            byte[] finalBlock = null;
                            byte[] decryptBlock = new byte[ decrypTransform.OutputBlockSize ]; 
                            byte[] signature = null; 
                            byte[] expectedSignature = new byte[ sha256.HashSize / 8 ];
 
                            using( MemoryStream digest = new MemoryStream( new byte[ iv.Length + encKey.Length + decrypTransform.OutputBlockSize ] ) )
                            {
                                //
                                // Read the expected signature from the file. 
                                //
                                if( expectedSignature.Length != source.Read( expectedSignature, 0, expectedSignature.Length ) ) 
                                { 
                                    throw IDT.ThrowHelperError(
                                                new ImportException( SR.GetString( SR.InvalidImportFile ) ) ); 
                                }


                                // 
                                // Capture the iv and derived sig key into the
                                //  digest. 
                                // 
                                digest.Write( iv, 0, iv.Length );
                                digest.Write( sigKey, 0, sigKey.Length ); 

                                int bytesRead = 0;
                                int bytesTansformed = 0;
 
                                while( source.Position < source.Length - block.Length )
                                { 
 
                                    try
                                    { 
                                        bytesRead = source.Read( block, 0, block.Length );

                                        bytesTansformed = decrypTransform.TransformBlock( block, 0, bytesRead, decryptBlock, 0 );
 
                                        //
                                        // Bytes transformed will be 0 the first time throught, 
                                        //  and the second time through will return the first times data. 
                                        //  and so on, until we get to the final block.
                                        // 
                                        if( bytesTansformed > 0 )
                                        {
                                            //
                                            // Write the clear text to the destination. 
                                            //
                                            destination.Write( decryptBlock, 0, bytesTansformed ); 
                                        } 

                                    } 
                                    finally
                                    {
                                        Array.Clear( decryptBlock, 0, decryptBlock.Length );
                                        Array.Clear( block, 0, block.Length ); 
                                    }
 
                                } 

                                bytesRead = source.Read( block, 0, block.Length ); 

                                finalBlock = decrypTransform.TransformFinalBlock( block, 0, bytesRead );

                                destination.Write( finalBlock, 0, finalBlock.Length ); 

                                // 
                                // Append the final block of raw text to the digest. 
                                //
                                digest.Write( 
                                    finalBlock,
                                    // In case finalBlock is > decrypTransform.OutputBlockSize
                                    // the following offset will make sure only decrypTransform.OutputBlockSize
                                    // get written to the digest. 
                                    finalBlock.Length - decrypTransform.OutputBlockSize,
                                    decrypTransform.OutputBlockSize ); 
 

                                // 
                                // Generate the signature from the computed digest.
                                //
                                digest.Flush( );
                                digest.Seek( 0, SeekOrigin.Begin ); 
                                signature = sha256.ComputeHash( digest );
 
                                // 
                                // Compare the signatures.
                                // 
                                if( !CompareSignature( signature, expectedSignature ) )
                                {
                                    throw IDT.ThrowHelperError(
                                                new ImportException( SR.GetString( SR.InvalidImportFile ) ) ); 
                                }
                            } 
                        } 
                    }
                } 
            }
            finally
            {
                Array.Clear( encKey, 0, encKey.Length ); 
                Array.Clear( sigKey, 0, sigKey.Length );
            } 
        } 

 
        //
        // Summary:
        //  Encrypts the source buffer using the specified password.
        // 
        // Arguments:
        //  source:         The source data to encrypt.  Stream must be at position 
        //                  0, and entire stream will be encrypted. 
        //  destination:    The desination stream that will recieve the encrypted data.
        //                  Data written in the following pattern: 
        //                  IV + Signature + CipherValue
        //  passwordString: The string password to use to encrypt the file with.
        //  salt:           The generated random value that must be persisted for decryption.
        // 
        public static void Encrypt(
                                Stream source, 
                                Stream destination, 
                                string passwordString,
                                out byte[] salt ) 
        {

            RandomNumberGenerator generator = new RNGCryptoServiceProvider( );
 
            salt = new byte[ ENCRYPTIONIVBUFFERSIZE ];
 
            // 
            // Rand up the salt we need for pksc5
            // 
            generator.GetBytes( salt );


            byte[] password = Encoding.Unicode.GetBytes( passwordString ); 
            //
            // using pkcs5 and sha256, generate a key pair for encryption and signing. 
            // 
            byte[] encKey;
            byte[] sigKey; 
            try
            {
                CreateKeyPair( password, salt, out encKey, out sigKey );
 
            }
            finally 
            { 
                Array.Clear( password, 0, password.Length );
            } 

            try
            {
 
                byte[] iv = new byte[ ENCRYPTIONIVBUFFERSIZE ];
                generator.GetBytes( iv ); 
                using( RijndaelManaged algorithm = new RijndaelManaged( ) ) 
                {
                    algorithm.Padding = PaddingMode.PKCS7; 
                    algorithm.Mode = CipherMode.CBC;
                    algorithm.BlockSize = iv.Length * 8;
                    algorithm.KeySize = encKey.Length * 8;
 
                    using( RijndaelManagedTransform encrTransform = (RijndaelManagedTransform)algorithm.CreateEncryptor( encKey, iv ) )
                    { 
                        using( SHA256Managed sha256 = new SHA256Managed( ) ) 
                        {
                            byte[] block = new byte[ encrTransform.InputBlockSize ]; 
                            byte[] finalBlock = null;
                            byte[] encBlock = new byte[ encrTransform.OutputBlockSize ];
                            byte[] signature = null;
                            byte[] prefixBuffer = new byte[ iv.Length + ( sha256.HashSize / 8 ) ]; 

                            using( MemoryStream digest = new MemoryStream( new byte[ iv.Length + sigKey.Length + encrTransform.InputBlockSize ] ) ) 
                            { 
                                //
                                // Prepend IV and sigKey 
                                //
                                digest.Write( iv, 0, iv.Length );
                                digest.Write( sigKey, 0, sigKey.Length );
                                try 
                                {
 
                                    // 
                                    // Write the empty prefixBuffer buffer to seek the stream to the point where
                                    //  we write the encrypted data.  We will seek back once we have captured 
                                    //  the final block of data.
                                    //
                                    destination.Write( prefixBuffer, 0, prefixBuffer.Length );
 

                                    // 
                                    // Encrypt the entire input. 
                                    //
                                    int bytesRead = 0; 
                                    int targetBytes = 0;

                                    //
                                    // Until there is only the final block left... 
                                    //
                                    while( source.Position < source.Length - block.Length ) 
                                    { 
                                        //
                                        // read the block of source to process. 
                                        //
                                        bytesRead = source.Read( block, 0, block.Length );
                                        try
                                        { 
                                            //
                                            // decide if this is the final block 
                                            // 
                                            targetBytes = encrTransform.TransformBlock( block, 0, block.Length, encBlock, 0 );
                                            destination.Write( encBlock, 0, targetBytes ); 
                                        }
                                        finally
                                        {
                                            Array.Clear( block, 0, block.Length ); 
                                            Array.Clear( encBlock, 0, encBlock.Length );
                                        } 
                                    } 

                                    // 
                                    // Read the last block, could be an incomplete block.
                                    //
                                    bytesRead = source.Read( block, 0, block.Length );
 
                                    if( bytesRead != block.Length )
                                    { 
                                        // 
                                        // Does not line up, lets read last 0x20 bytes from the source for digest.
                                        // 
                                        byte[ ] lastClearBlock = new byte[ block.Length ];
                                        source.Seek( source.Length - block.Length, SeekOrigin.Begin );
                                        int lastClearByteCount = source.Read( lastClearBlock, 0, lastClearBlock.Length );
                                        IDT.Assert( lastClearByteCount == lastClearBlock.Length && lastClearBlock.Length == block.Length, 
                                            "Should have read exactly 0x20 bytes" );
                                        digest.Write( lastClearBlock, 0, lastClearBlock.Length ); 
                                    } 
                                    else
                                    { 
                                        //
                                        // The last block is the right size we want,
                                        // simply write it to the digest.
                                        // 
                                        digest.Write( block, 0, block.Length );
                                    } 
 

                                    finalBlock = encrTransform.TransformFinalBlock( block, 0, bytesRead ); 
                                    destination.Write( finalBlock, 0, finalBlock.Length );

                                    destination.Flush( );
                                    digest.Flush( ); 

                                    // 
                                    // Reset the digest stream now that its full and hash it 
                                    //
                                    digest.Seek( 0, SeekOrigin.Begin ); 
                                    signature = sha256.ComputeHash( digest );

                                    //
                                    // Populate the prefix buffer with the iv and hashed digest 
                                    //
                                    Array.Copy( iv, 0, prefixBuffer, 0, iv.Length ); 
                                    Array.Copy( signature, 0, prefixBuffer, iv.Length, signature.Length ); 

 
                                    //
                                    // Seek back to the start, and write the digest value.
                                    //
                                    destination.Seek( 0, SeekOrigin.Begin ); 
                                    destination.Write( prefixBuffer, 0, prefixBuffer.Length );
 
                                    // 
                                    // Return to the end of the stream.
                                    // 
                                    destination.Flush( );
                                    destination.Seek( 0, SeekOrigin.End );

                                } 
                                finally
                                { 
                                    // 
                                    // ensure that the last of the clear text data or keys we have is clear.
                                    // 
                                    Array.Clear( block, 0, block.Length );
                                    Array.Clear( encBlock, 0, encBlock.Length );
                                    Array.Clear( prefixBuffer, 0, prefixBuffer.Length );
 
                                    if( null != finalBlock )
                                    { 
                                        Array.Clear( finalBlock, 0, finalBlock.Length ); 
                                    }
                                } 
                            }
                        }
                    }
                } 

            } 
            finally 
            {
                Array.Clear( encKey, 0, encKey.Length ); 
                Array.Clear( sigKey, 0, sigKey.Length );
            }
        }
 

 
        // 
        // Summary:
        //  Creates a KeyPair for use in encryption/decryption and signatures 
        //
        // Summary:
        //  password:   The password to use as the basis of the key
        //  salt:       The salt used in the PKCS5 impl. 
        //  encryptionKey:  The key to be used as the symmetric encryption key
        //  signatureKey:   The key to be used as part of the digest info. 
        // 
        static void CreateKeyPair(
                        byte[] password, 
                        byte[] salt,
                        out byte[] encryptionKey,
                        out byte[] signatureKey )
        { 
            byte[] pkcs5Raw = DoPkcs5( password, salt );
            using( SHA256Managed sha256 = new SHA256Managed( ) ) 
            { 
                byte[] encKeyBase = new byte[ ENCRYPTIONKEYBUFFERSIZE + DerivedKeyEncryptionEntropy.Length ];
                byte[] sigKeyBase = new byte[ ENCRYPTIONKEYBUFFERSIZE + DerivedKeySignatureEntropy.Length ]; 

                Array.Copy( DerivedKeyEncryptionEntropy, 0, encKeyBase, 0, DerivedKeyEncryptionEntropy.Length );
                Array.Copy( DerivedKeySignatureEntropy, 0, sigKeyBase, 0, DerivedKeySignatureEntropy.Length );
 
                Array.Copy( pkcs5Raw, 0, encKeyBase, DerivedKeyEncryptionEntropy.Length, ENCRYPTIONKEYBUFFERSIZE );
                Array.Copy( pkcs5Raw, 0, sigKeyBase, DerivedKeySignatureEntropy.Length, ENCRYPTIONKEYBUFFERSIZE ); 
 
                encryptionKey = sha256.ComputeHash( encKeyBase );
                signatureKey = sha256.ComputeHash( sigKeyBase ); 
            }
        }

        // 
        // Summary:
        //  Derives a PKCS5 key from a password. 
        // 
        static byte[] DoPkcs5( byte[] password, byte[] salt )
        { 
            PasswordDeriveBytes pkcs5 = new PasswordDeriveBytes( password, salt, "SHA256", ITERATIONCOUNT );
            return pkcs5.GetBytes( ENCRYPTIONKEYBUFFERSIZE );
        }
 
        //
        // Summary: 
        //  Compares two signatures and return true if they are the same. 
        //
        internal static bool CompareSignature( byte[] input, byte[] expected ) 
        {
            if( input.Length == 0 )
            {
                return false; 
            }
            if( input.Length != expected.Length ) 
            { 
                return false;
            } 

            for( int i = 0; i < input.Length; i++ )
            {
                if( expected[ i ] != input[ i ] ) 
                {
                    return false; 
                } 
            }
            return true; 
        }
    }
}

// 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