1121 lines · 52.4 KB · C#
RawDownload
//******************************************************************************************************//  Cipher.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  11/12/2003 - J. Ritchie Carroll//       Generated original version of source code.//  01/04/2006 - J. Ritchie Carroll//       Migrated 2.0 version of source code from 1.1 source (GSF.Shared.Crypto).//  02/28/2007 - J. Ritchie Carroll//       Changed string-based encrypt and decrypt functions to return null if//       input string to be encrypted or decrypted was null or empty.//  10/11/2007 - J. Ritchie Carroll//       Added Obfuscate and De-obfuscate functions that perform data obfuscation//       based upon simple bit-rotation algorithms.//  12/13/2007 - Darrell Zuercher//       Edited code comments.//  09/19/2008 - J. Ritchie Carroll//       Converted to C# - basic encryption/decryption extends string, byte[], and Stream.//  08/10/2009 - Josh L. Patterson//       Edited Comments.//  09/14/2009 - Stephen C. Wills//       Added new header and license agreement.//  10/05/2009 - J. Ritchie Carroll//       Switched to AES only encryption/decryption using machine specific local key cache.//  07/13/2010 - Stephen C. Wills//       Added a file watcher to reload the KeyIV table when an external//       process modifies the cache file.//  03/17/2011 - J. Ritchie Carroll//       Modified key and initialization vector cache to be able to operate in a non-elevated mode.//  04/06/2011 - J. Ritchie Carroll//       Added FlushCache() method to wait for pending key/IV cache serializations for applications//       with a short life-cycle.//  04/14/2011 - Pinal C. Patel//       Updated to use new serialization and deserialization methods in GSF.Serialization class.//  06/10/2011 - Pinal C. Patel//       Renamed RetryDelayInterval and MaximumRetryAttempts settings persisted to the config file //       to CacheRetryDelayInterval and CacheMaximumRetryAttempts for clarity.//  06/30/2011 - Stephen C. Wills - applying changes from Jian (Ryan) Zuo//       Added ManagedEncryption setting to the config file to allow the user to switch to//       wrappers over FIPS-compliant algorithms.//  07/05/2011 - Stephen C. Wills//       Removed config file setting for FIPS compliance. Checks the registry instead.//  07/13/2011 - Stephen C. Wills//       Modified check for FIPS compliance to work with Windows XP and Windows Server 2003.//  02/17/2012 - Stephen C. Wills//       Added public method to manually reload the key cache.//  12/14/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Diagnostics.CodeAnalysis;using System.IO;using System.Security.Cryptography;using System.Text;using System.Threading;using GSF.Collections;using GSF.Configuration;using GSF.IO;#if !MONOusing Microsoft.Win32;#endif namespace GSF.Security.Cryptography{    #region [ Enumerations ]     /// <summary>    /// Cryptographic strength enumeration.    /// </summary>    [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]    public enum CipherStrength    {        /// <summary>Uses no encryption.</summary>        None = 0,        /// <summary>Uses AES 128-bit encryption.</summary>        Aes128 = 128,        /// <summary>Uses AES 256-bit encryption.</summary>        Aes256 = 256    }     #endregion     /// <summary>    /// Provides general use cryptographic functions.    /// </summary>    /// <remarks>    /// This class exists to simplify usage of basic cryptography functionality.    /// </remarks>    public static partial class Cipher    {        // Constants        private const int KeyIndex = 0;        private const int IVIndex = 1;         // Default key and initialization vector cache file name        private const string DefaultCacheFileName = "KeyIVCache.bin";         // Default maximum retry attempts allowed for loading cryptographic key and initialization vector cache        private const int DefaultMaximumRetryAttempts = 5;         // Default wait interval, in milliseconds, before retrying load of cryptographic key and initialization vector cache        private const double DefaultRetryDelayInterval = 1000.0D;         // The standard settings category for cryptography information        private const string CryptoServicesSettingsCategory = "CryptographyServices";         /// <summary>        /// Represents an inter-process serializable cryptographic key and initialization vector cache.        /// </summary>        private class KeyIVCache : InterprocessCache        {            #region [ Members ]             // Internal key and initialization vector table            private Dictionary<string, byte[][]> m_keyIVTable = new Dictionary<string, byte[][]>();            private readonly object m_keyIVTableLock = new object();             // Flag to choose between AesManaged and AesCryptoServiceProvider for encryption            private bool m_managedEncryption;             #endregion             #region [ Properties ]             /// <summary>            /// Gets a copy of the internal key and initialization vector table.            /// </summary>            private Dictionary<string, byte[][]> KeyIVTable            {                get                {                    Dictionary<string, byte[][]> keyIVTable;                     // We wait until the key and IV cache is loaded before attempting to access it                    WaitForDataReady();                     // Wait for thread level lock on key table                    lock (m_keyIVTableLock)                    {                        // Make a copy of the keyIV table for external use                        keyIVTable = new Dictionary<string, byte[][]>(m_keyIVTable);                    }                     return keyIVTable;                }            }             /// <summary>            /// Gets or sets a boolean value that determines whether to use AesManaged            /// or the CAPI wrapper (AesCryptoServiceProvider) for encryption.            /// </summary>            public bool ManagedEncryption            {                get                {                    return m_managedEncryption;                }                set                {                    m_managedEncryption = value;                }            }             #endregion             #region [ Methods ]             /// <summary>            /// Gets the crypto key and initialization vector for the given user password.            /// </summary>            /// <param name="password">User password used for key lookup.</param>            /// <param name="keySize">Specifies the desired key size.</param>            /// <returns>Crypto key, index 0, and initialization vector, index 1, for the given user password.</returns>            public byte[][] GetCryptoKeyIV(string password, int keySize)            {                string hash = GetPasswordHash(password, keySize);                byte[][] keyIV;                byte[] key, iv;                bool addedKey = false;                 // We wait until the key and IV cache is loaded before attempting to access it                WaitForDataReady();                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    // Lookup crypto key based on password hash in persisted key table                    if (!m_keyIVTable.TryGetValue(hash, out keyIV))                    {                        Aes symmetricAlgorithm;                         // Key for password hash doesn't exist, create a new one                        if (m_managedEncryption)                        {                            symmetricAlgorithm = new AesManaged();                        }                        else                        {                            // Switch from AesManaged to AesCryptoServiceProvider complying with FIPS                            symmetricAlgorithm = new AesCryptoServiceProvider();                        }                         symmetricAlgorithm.KeySize = keySize;                        symmetricAlgorithm.GenerateKey();                        symmetricAlgorithm.GenerateIV();                         key = symmetricAlgorithm.Key;                        iv = symmetricAlgorithm.IV;                        keyIV = new[] { key, iv };                         // Add new crypto key to key table                        m_keyIVTable.Add(hash, keyIV);                        addedKey = true;                    }                }                 // Queue up a serialization for any newly added key                if (addedKey)                    Save();                 return keyIV;            }             /// <summary>            /// Determines if a key and initialization vector exists for the given <paramref name="password"/>.            /// </summary>            /// <param name="password">User password used for key lookups.</param>            /// <param name="keySize">Specifies the desired key size.</param>            /// <returns><c>true</c> if a key and initialization vector exists for the given <paramref name="password"/>; otherwise <c>false</c>.</returns>            // ReSharper disable once MemberHidesStaticFromOuterClass            public bool KeyIVExists(string password, int keySize)            {                string hash = GetPasswordHash(password, keySize);                 // We wait until the key and IV cache is loaded before attempting to access it                WaitForDataReady();                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    // Lookup crypto key based on password hash in persisted key table                    return m_keyIVTable.ContainsKey(hash);                }            }             /// <summary>            /// Imports a key and initialization vector into the local system key cache.            /// </summary>            /// <param name="password">User password used for key lookups.</param>            /// <param name="keySize">Specifies the desired key size.</param>            /// <param name="keyIVText">Text based key and initialization vector to import into local key cache.</param>            /// <remarks>            /// This method is used to manually import a key created on another computer.            /// </remarks>            // ReSharper disable once MemberHidesStaticFromOuterClass            public void ImportKeyIV(string password, int keySize, string keyIVText)            {                string hash = GetPasswordHash(password, keySize);                 // We wait until the key and IV cache is loaded before attempting to access it                WaitForDataReady();                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    string[] keyIV = keyIVText.Split('|');                    byte[] key = Convert.FromBase64String(keyIV[KeyIndex]);                    byte[] iv = Convert.FromBase64String(keyIV[IVIndex]);                     // Assign new crypto key to key table                    m_keyIVTable[hash] = new[] { key, iv };                }                 // Queue up a serialization for this new key                Save();            }             /// <summary>            /// Exports a key and initialization vector from the local system key cache.            /// </summary>            /// <param name="password">User password used for key lookup.</param>            /// <param name="keySize">Specifies the desired key size.</param>            /// <returns>Text based key and initialization vector exported from local key cache.</returns>            /// <remarks>            /// This method is used to manually export a key to be installed on another computer.             /// </remarks>            // ReSharper disable once MemberHidesStaticFromOuterClass            public string ExportKeyIV(string password, int keySize)            {                byte[][] keyIV = GetCryptoKeyIV(password, keySize);                return string.Concat(Convert.ToBase64String(keyIV[KeyIndex]), "|", Convert.ToBase64String(keyIV[IVIndex]));            }             /// <summary>            /// Merge keys and initialization vectors from another <see cref="KeyIVCache"/>, local cache taking precedence.            /// </summary>            /// <param name="other">Other <see cref="KeyIVCache"/> to merge with.</param>            public void MergeLeft(KeyIVCache other)            {                // Merge other keys into local ones                Dictionary<string, byte[][]> mergedKeyIVTable = KeyIVTable.Merge(other.KeyIVTable);                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    // Replace local key IV table with merged items                    m_keyIVTable = mergedKeyIVTable;                }                 // Queue up a serialization for any newly added keys                Save();            }             /// <summary>            /// Merge keys and initialization vectors from another <see cref="KeyIVCache"/>, other cache taking precedence.            /// </summary>            /// <param name="other">Other <see cref="KeyIVCache"/> to merge with.</param>            public void MergeRight(KeyIVCache other)            {                // Merge other keys into local ones                Dictionary<string, byte[][]> mergedKeyIVTable = other.KeyIVTable.Merge(KeyIVTable);                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    // Replace local key IV table with merged items                    m_keyIVTable = mergedKeyIVTable;                }                 // Queue up a serialization for any newly added keys                Save();            }             /// <summary>            /// Initiates inter-process synchronized save of key and initialization vector table.            /// </summary>            public override void Save()            {                byte[] serializedKeyIVTable;                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    serializedKeyIVTable = Serialization.Serialize(m_keyIVTable, SerializationFormat.Binary);                }                 // File data is the serialized Key/IV cache, assignment will initiate auto-save if needed                FileData = serializedKeyIVTable;            }             /// <summary>            /// Handles serialization of file to disk; virtual method allows customization (e.g., pre-save encryption and/or data merge).            /// </summary>            /// <param name="fileStream"><see cref="FileStream"/> used to serialize data.</param>            /// <param name="fileData">File data to be serialized.</param>            /// <remarks>            /// Consumers overriding this method should not directly call <see cref="InterprocessCache.FileData"/> property to avoid potential dead-locks.            /// </remarks>            protected override void SaveFileData(FileStream fileStream, byte[] fileData)            {                // Encrypt data local to this machine (this way user cannot copy key cache to another machine)                base.SaveFileData(fileStream, ProtectedData.Protect(fileData, null, DataProtectionScope.LocalMachine));            }             /// <summary>            /// Handles deserialization of file from disk; virtual method allows customization (e.g., pre-load decryption and/or data merge).            /// </summary>            /// <param name="fileStream"><see cref="FileStream"/> used to deserialize data.</param>            /// <returns>Deserialized file data.</returns>            /// <remarks>            /// Consumers overriding this method should not directly call <see cref="InterprocessCache.FileData"/> property to avoid potential dead-locks.            /// </remarks>            protected override byte[] LoadFileData(FileStream fileStream)            {                // Decrypt data that was encrypted local to this machine                byte[] serializedKeyIVTable = ProtectedData.Unprotect(fileStream.ReadStream(), null, DataProtectionScope.LocalMachine);                Dictionary<string, byte[][]> keyIVTable = Serialization.Deserialize<Dictionary<string, byte[][]>>(serializedKeyIVTable, SerializationFormat.Binary);                 // Wait for thread level lock on key table                lock (m_keyIVTableLock)                {                    // Merge new and existing key tables since new keys may have been queued for serialization, but not saved yet                    m_keyIVTable = keyIVTable.Merge(m_keyIVTable);                }                 return serializedKeyIVTable;            }             // Waits until the key and IV cache is loaded before attempting to access it            private void WaitForDataReady()            {                try                {                    // Just wrapping this method to provide a more detailed exception message if there is an issue loading cache                    WaitForLoad();                }                catch (Exception ex)                {                    throw new UnauthorizedAccessException("Cryptographic cipher failure: timeout while attempting to load cryptographic key cache.", ex);                }            }             #endregion        }         // Primary cryptographic key and initialization vector cache.        private static KeyIVCache GlobalKeyIVCache => s_keyIVCache.Value;        private static Lazy<KeyIVCache> s_keyIVCache;         // Set default encoding base Base64 strings        private static Encoding s_textEncoding;         /// <summary>        /// Static constructor continuation for the <see cref="Cipher"/> class.        /// </summary>        static partial void OnCreated()        {#if MONO            s_textEncoding = Encoding.Default;#else            s_textEncoding = Encoding.Unicode;#endif            KeyIVCache localKeyIVCache;            EnabledState useFIPSModules = EnabledState.Auto;            string localCacheFileName = DefaultCacheFileName;            double retryDelayInterval = DefaultRetryDelayInterval;            int maximumRetryAttempts = DefaultMaximumRetryAttempts;             // Load cryptographic settings            ConfigurationFile config = ConfigurationFile.Current;            CategorizedSettingsElementCollection settings = config.Settings[CryptoServicesSettingsCategory];             settings.Add("UseFIPSModules", useFIPSModules, "Determines if security functions should use FIPS cryptographic modules. Value is one of \"Auto\", \"On\" or \"Off\". Defaults to \"Auto\" which reads FIPS setting from registry.");            settings.Add("CryptoCache", localCacheFileName, "Path and file name of cryptographic key and initialization vector cache.");            settings.Add("CacheRetryDelayInterval", retryDelayInterval, "Wait interval, in milliseconds, before retrying load of cryptographic key and initialization vector cache.");            settings.Add("CacheMaximumRetryAttempts", maximumRetryAttempts, "Maximum retry attempts allowed for loading cryptographic key and initialization vector cache.");             useFIPSModules = settings["UseFIPSModules"].ValueAs(useFIPSModules);            localCacheFileName = FilePath.GetAbsolutePath(settings["CryptoCache"].ValueAs(localCacheFileName));            retryDelayInterval = settings["CacheRetryDelayInterval"].ValueAs(retryDelayInterval);            maximumRetryAttempts = settings["CacheMaximumRetryAttempts"].ValueAs(maximumRetryAttempts);             if (useFIPSModules != EnabledState.Auto)                s_managedEncryption = useFIPSModules == EnabledState.Off; // FIPS Off means use managed encryption             s_keyIVCache = new Lazy<KeyIVCache>(() =>            {                KeyIVCache globalKeyIVCache;                 // Initialize local cryptographic key and initialization vector cache (application may only have read-only access to this cache)                localKeyIVCache = new KeyIVCache                {                    FileName = localCacheFileName,                    RetryDelayInterval = retryDelayInterval,                    MaximumRetryAttempts = maximumRetryAttempts,                    ManagedEncryption = s_managedEncryption,#if DNF45 && !MONO                    ReloadOnChange = true,#else                    // Reload on change is disabled to eliminate GC handle leaks on .NET 4.0, this prevents                    // automatic runtime reloading of key/iv data cached by another application.                    ReloadOnChange = false,#endif                    AutoSave = false                };                 // Load initial keys                localKeyIVCache.Load();                 try                {                    // Validate that user has write access to the local cryptographic cache folder                    string tempFile = FilePath.GetDirectoryName(localCacheFileName) + Guid.NewGuid() + ".tmp";                     using (File.Create(tempFile))                    {                    }                     if (File.Exists(tempFile))                        File.Delete(tempFile);                     // No access issues exist, use local cache as the primary cryptographic key and initialization vector cache                    globalKeyIVCache = localKeyIVCache;

Lines 1–500 of 1121.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t