AesCryptoServiceProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / System / Security / Cryptography / AesCryptoServiceProvider.cs / 1305376 / AesCryptoServiceProvider.cs

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

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; 
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography {
    ///  
    ///     AES wrapper around the CAPI implementation. 
    /// 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public sealed class AesCryptoServiceProvider : Aes {
        private static KeySizes[] s_supportedKeySizes;
        private static int s_defaultKeySize;
 
        private SafeCspHandle m_cspHandle;
 
        // Note that keys are stored in CAPI rather than directly in the KeyValue property, which should not 
        // be used to retrieve the key value directly.
        private SafeCapiKeyHandle m_key; 

        // 
        // 
        //  
        // 
        //  
        //  
        // 
        [System.Security.SecurityCritical] 
        public AesCryptoServiceProvider () {
            Contract.Ensures(m_cspHandle != null && !m_cspHandle.IsInvalid && !m_cspHandle.IsClosed);

            // On Windows XP the AES CSP has the prototype name, but on newer operating systems it has the 
            // standard name
            string providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAes; 
            if (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1) { 
                providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAesPrototype;
            } 

            m_cspHandle = CapiNative.AcquireCsp(null,
                                                providerName,
                                                CapiNative.ProviderType.RsaAes, 
                                                CapiNative.CryptAcquireContextFlags.VerifyContext,
                                                true); 
 
            // CAPI will not allow feedback sizes greater than 64 bits
            FeedbackSizeValue = 8; 

            // Get the different AES key sizes supported by this platform, raising an error if there are no
            // supported key sizes.
            int defaultKeySize = 0; 
            KeySizes[] keySizes = FindSupportedKeySizes(m_cspHandle, out defaultKeySize);
            if (keySizes.Length != 0) { 
                Debug.Assert(defaultKeySize > 0, "defaultKeySize > 0"); 
                KeySizeValue = defaultKeySize;
            } 
            else {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
        } 

        ///  
        ///     Value of the symmetric key used for encryption / decryption 
        /// 
        public override byte[] Key { 
            // 
            // 
            // 
            //  
            // 
            //  
            [System.Security.SecurityCritical] 
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
            get { 
                Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed);
                Contract.Ensures(Contract.Result() != null &&
                                        Contract.Result().Length == KeySizeValue / 8);
 
                if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
                    GenerateKey(); 
                } 

                // We don't hold onto a key value directly, so we need to export it from CAPI when the user 
                // wants a byte array representation.
                byte[] keyValue =  CapiNative.ExportSymmetricKey(m_key);
                return keyValue;
            } 

            //  
            //  
            // 
            //  
            // 
            // 
            // 
            //  
            // 
            [System.Security.SecurityCritical] 
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] 
            set {
                Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed); 

                if (value == null) {
                    throw new ArgumentNullException("value");
                } 

                byte[] keyValue = (byte[])value.Clone(); 
 
                if (!ValidKeySize(keyValue.Length * 8)) {
                    throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidKeySize)); 
                }

                // Import the key, then close any current key and replace with the new one. We need to make
                // sure the import is successful before closing the current key to avoid having an algorithm 
                // with no valid keys.
                SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, 
                                                                              GetAlgorithmId(keyValue.Length * 8), 
                                                                              keyValue);
                if (m_key != null) { 
                    m_key.Dispose();
                }

                m_key = importedKey; 
                KeySizeValue = keyValue.Length * 8;
            } 
        } 

        ///  
        ///     Size, in bits, of the key
        /// 
        public override int KeySize {
            get { return base.KeySize; } 

            //  
            //  
            // 
            //  
            [System.Security.SecurityCritical]
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
            set {
                base.KeySize = value; 

                // Since the key size is being reset, we need to reset the key itself as well 
                if (m_key != null) { 
                    m_key.Dispose();
                } 
            }
        }

        ///  
        ///     Create an object to perform AES decryption with the current key and IV
        ///  
        ///  
        // 
        //  
        // 
        // 
        // 
        //  
        [System.Security.SecurityCritical]
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] 
        public override ICryptoTransform CreateDecryptor() { 
            Contract.Ensures(Contract.Result() != null);
 
            if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
                throw new CryptographicException(SR.GetString(SR.Cryptography_DecryptWithNoKey));
            }
 
            return CreateDecryptor(m_key, IVValue);
        } 
 
        /// 
        ///     Create an object to perform AES decryption with the given key and IV 
        /// 
        // 
        // 
        //  
        // 
        //  
        //  
        [System.Security.SecurityCritical]
        public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) { 
            Contract.Ensures(Contract.Result() != null);

            if (key == null) {
                throw new ArgumentNullException("key"); 
            }
            if (!ValidKeySize(key.Length * 8)) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key"); 
            }
            if (iv != null && iv.Length * 8 != BlockSizeValue) { 
                throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
            }

            byte[] keyCopy = (byte[])key.Clone(); 
            byte[] ivCopy = null;
            if (iv != null) { 
                ivCopy = (byte[])iv.Clone(); 
            }
 
            using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
                return CreateDecryptor(importedKey, ivCopy);
            }
        } 

        ///  
        ///     Create an object to perform AES decryption 
        /// 
        //  
        // 
        // 
        // 
        //  
        [System.Security.SecurityCritical]
        private ICryptoTransform CreateDecryptor(SafeCapiKeyHandle key, byte[] iv) { 
            Contract.Requires(key != null); 
            Contract.Ensures(Contract.Result() != null);
 
            return new CapiSymmetricAlgorithm(BlockSizeValue,
                                              FeedbackSizeValue,
                                              m_cspHandle,
                                              key, 
                                              iv,
                                              Mode, 
                                              PaddingValue, 
                                              EncryptionMode.Decrypt);
        } 

        /// 
        ///     Create an object to do AES encryption with the current key and IV
        ///  
        // 
        //  
        //  
        // 
        //  
        // 
        [System.Security.SecurityCritical]
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
        public override ICryptoTransform CreateEncryptor() { 
            Contract.Ensures(Contract.Result() != null);
 
            if (m_key == null || m_key.IsInvalid || m_key.IsClosed) { 
                GenerateKey();
            } 

            // ECB is the only mode which does not require an IV -- generate one here if we don't have one yet.
            if (Mode != CipherMode.ECB && IVValue == null) {
                GenerateIV(); 
            }
 
            return CreateEncryptor(m_key, IVValue); 
        }
 
        /// 
        ///     Create an object to do AES encryption with the given key and IV
        /// 
        //  
        // 
        //  
        //  
        // 
        //  
        [System.Security.SecurityCritical]
        public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) {
            Contract.Ensures(Contract.Result() != null);
 
            if (key == null) {
                throw new ArgumentNullException("key"); 
            } 
            if (!ValidKeySize(key.Length * 8)) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key"); 
            }
            if (iv != null && iv.Length * 8 != BlockSizeValue) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
            } 

            byte[] keyCopy = (byte[])key.Clone(); 
            byte[] ivCopy = null; 
            if (iv != null) {
                ivCopy = (byte[])iv.Clone(); 
            }

            using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
                return CreateEncryptor(importedKey, ivCopy); 
            }
        } 
 
        /// 
        ///     Create an object to perform AES encryption 
        /// 
        // 
        // 
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        private ICryptoTransform CreateEncryptor(SafeCapiKeyHandle key, byte[] iv) {
            Contract.Requires(key != null); 
            Contract.Ensures(Contract.Result() != null);

            return new CapiSymmetricAlgorithm(BlockSizeValue,
                                              FeedbackSizeValue, 
                                              m_cspHandle,
                                              key, 
                                              iv, 
                                              Mode,
                                              PaddingValue, 
                                              EncryptionMode.Encrypt);
        }

        ///  
        ///     Release any CAPI handles we're holding onto
        ///  
        //  
        // 
        //  
        // 
        // 
        // 
        [System.Security.SecurityCritical] 
        protected override void Dispose(bool disposing) {
            Contract.Ensures(!disposing || m_key == null || m_key.IsClosed); 
            Contract.Ensures(!disposing || m_cspHandle == null || m_cspHandle.IsClosed); 

            try { 
                if (disposing) {
                    if (m_key != null) {
                        m_key.Dispose();
                    } 

                    if (m_cspHandle != null) { 
                        m_cspHandle.Dispose(); 
                    }
                } 
            }
            finally {
                base.Dispose(disposing);
            } 
        }
 
        ///  
        ///     Get the size of AES keys supported by the given CSP, and which size should be used by default.
        /// 
        ///     We assume that the same CSP will always be used by all instances of the AesCryptoServiceProvider
        ///     in the current AppDomain.  If we add the ability for users to choose which CSP to use on a
        ///     per-instance basis, we need to update the code to account for the CSP when checking the cached
        ///     key size values. 
        /// 
        //  
        //  
        // 
        //  
        [System.Security.SecurityCritical]
        private static KeySizes[] FindSupportedKeySizes(SafeCspHandle csp, out int defaultKeySize) {
            Contract.Requires(csp != null);
            Contract.Ensures(Contract.Result() != null); 

            // If this platform has any supported algorithm sizes, then the default key size should be set to a 
            // reasonable value. 
            Contract.Ensures(Contract.Result().Length == 0 ||
                             (Contract.ValueAtReturn(out defaultKeySize) > 0 && Contract.ValueAtReturn(out defaultKeySize) % 8 == 0)); 

            if (s_supportedKeySizes == null) {
                List keySizes = new List();
                int maxKeySize = 0; 

                // 
                // Enumerate the CSP's supported algorithms to see what key sizes it supports for AES 
                //
 
                CapiNative.PROV_ENUMALGS algorithm =
                    CapiNative.GetProviderParameterStruct(csp,
                                                                                   CapiNative.ProviderParameter.EnumerateAlgorithms,
                                                                                   CapiNative.ProviderParameterFlags.RestartEnumeration); 

                // Translate between CAPI AES algorithm IDs and supported key sizes 
                while (algorithm.aiAlgId != CapiNative.AlgorithmId.None) { 
                    switch (algorithm.aiAlgId) {
                        case CapiNative.AlgorithmId.Aes128: 
                            keySizes.Add(new KeySizes(128, 128, 0));
                            if (128 > maxKeySize) {
                                maxKeySize = 128;
                            } 

                            break; 
 
                        case CapiNative.AlgorithmId.Aes192:
                            keySizes.Add(new KeySizes(192, 192, 0)); 
                            if (192 > maxKeySize) {
                                maxKeySize = 192;
                            }
                            break; 

                        case CapiNative.AlgorithmId.Aes256: 
                            keySizes.Add(new KeySizes(256, 256, 0)); 
                            if (256 > maxKeySize) {
                                maxKeySize = 256; 
                            }
                            break;

                        default: 
                            break;
                    } 
 
                    algorithm = CapiNative.GetProviderParameterStruct(csp,
                                                                                               CapiNative.ProviderParameter.EnumerateAlgorithms, 
                                                                                               CapiNative.ProviderParameterFlags.None);
                }

                s_supportedKeySizes = keySizes.ToArray(); 
                s_defaultKeySize = maxKeySize;
            } 
 
            defaultKeySize = s_defaultKeySize;
            return s_supportedKeySizes; 
        }

        /// 
        ///     Generate a new random key 
        /// 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
        public override void GenerateKey() { 
            Contract.Ensures(m_key != null && !m_key.IsInvalid & !m_key.IsClosed);
            Contract.Assert(m_cspHandle != null);

            SafeCapiKeyHandle key = null; 
            if (!CapiNative.UnsafeNativeMethods.CryptGenKey(m_cspHandle,
                                                            GetAlgorithmId(KeySizeValue), 
                                                            CapiNative.KeyFlags.Exportable, 
                                                            out key)) {
                throw new CryptographicException(Marshal.GetLastWin32Error()); 
            }

            if (m_key != null) {
                m_key.Dispose(); 
            }
 
            m_key = key; 
        }
 
        /// 
        ///     Generate a random initialization vector
        /// 
        //  
        // 
        //  
        //  
        // 
        [System.Security.SecurityCritical] 
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
        public override void GenerateIV() {
            Contract.Ensures(IVValue != null && IVValue.Length == BlockSizeValue / 8);
            Contract.Assert(m_cspHandle != null); 
            Contract.Assert(BlockSizeValue % 8 == 0);
 
            byte[] iv = new byte[BlockSizeValue / 8]; 
            if (!CapiNative.UnsafeNativeMethods.CryptGenRandom(m_cspHandle, iv.Length, iv)) {
                throw new CryptographicException(Marshal.GetLastWin32Error()); 
            }

            IVValue = iv;
        } 

        ///  
        ///     Map an AES key size to the corresponding CAPI algorithm ID 
        /// 
        private static CapiNative.AlgorithmId GetAlgorithmId(int keySize) { 
            // We should always return either a data encryption algorithm ID or None if we don't recognize the key size
            Contract.Ensures(
                ((((int)Contract.Result()) & (int)CapiNative.AlgorithmClass.DataEncryption) == (int)CapiNative.AlgorithmClass.DataEncryption) ||
                Contract.Result() == CapiNative.AlgorithmId.None); 

            switch (keySize) { 
                case 128: 
                    return CapiNative.AlgorithmId.Aes128;
 
                case 192:
                    return CapiNative.AlgorithmId.Aes192;

                case 256: 
                    return CapiNative.AlgorithmId.Aes256;
 
                default: 
                    return CapiNative.AlgorithmId.None;
            } 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

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