//******************************************************************************************************// 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. Load more
//****************************************************************************************************** // 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 !MONO using 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;