private string GetCacheLookupKey(string masterKeyPath, bool allowEnclaveComputations, byte[] signature, string keyStoreName) { StringBuilder cacheLookupKeyBuilder = new StringBuilder(keyStoreName, capacity: keyStoreName.Length + masterKeyPath.Length + SqlSecurityUtility.GetBase64LengthFromByteLength(signature.Length) + 3 /*separators*/ + 10 /*boolean value + somebuffer*/); cacheLookupKeyBuilder.Append(_cacheLookupKeySeparator); cacheLookupKeyBuilder.Append(masterKeyPath); cacheLookupKeyBuilder.Append(_cacheLookupKeySeparator); cacheLookupKeyBuilder.Append(allowEnclaveComputations); cacheLookupKeyBuilder.Append(_cacheLookupKeySeparator); cacheLookupKeyBuilder.Append(Convert.ToBase64String(signature)); cacheLookupKeyBuilder.Append(_cacheLookupKeySeparator); string cacheLookupKey = cacheLookupKeyBuilder.ToString(); return(cacheLookupKey); }
/// <summary> /// <para> Retrieves Symmetric Key (in plaintext) given the encryption material.</para> /// </summary> internal bool GetKey(SqlEncryptionKeyInfo keyInfo, string serverName, out SqlClientSymmetricKey encryptionKey) { Debug.Assert(serverName != null, @"serverName should not be null."); StringBuilder cacheLookupKeyBuilder = new StringBuilder(serverName, capacity: serverName.Length + SqlSecurityUtility.GetBase64LengthFromByteLength(keyInfo.encryptedKey.Length) + keyInfo.keyStoreName.Length + 2 /*separators*/); #if DEBUG int capacity = cacheLookupKeyBuilder.Capacity; #endif //DEBUG cacheLookupKeyBuilder.Append(":"); cacheLookupKeyBuilder.Append(Convert.ToBase64String(keyInfo.encryptedKey)); cacheLookupKeyBuilder.Append(":"); cacheLookupKeyBuilder.Append(keyInfo.keyStoreName); string cacheLookupKey = cacheLookupKeyBuilder.ToString(); #if DEBUG Debug.Assert(cacheLookupKey.Length <= capacity, "We needed to allocate a larger array"); #endif //DEBUG // Lookup the key in cache encryptionKey = _cache.Get(cacheLookupKey) as SqlClientSymmetricKey; if (encryptionKey == null) { Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths != null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); // Check against the trusted key paths // // Get the List corresponding to the connected server IList <string> trustedKeyPaths; if (SqlConnection.ColumnEncryptionTrustedMasterKeyPaths.TryGetValue(serverName, out trustedKeyPaths)) { // If the list is null or is empty or if the keyPath doesn't exist in the trusted key paths, then throw an exception. if ((trustedKeyPaths == null) || (trustedKeyPaths.Count() == 0) || // (trustedKeyPaths.Where(s => s.Equals(keyInfo.keyPath, StringComparison.InvariantCultureIgnoreCase)).Count() == 0)) { (trustedKeyPaths.Any(s => s.Equals(keyInfo.keyPath, StringComparison.InvariantCultureIgnoreCase)) == false)) { // throw an exception since the key path is not in the trusted key paths list for this server throw SQL.UntrustedKeyPath(keyInfo.keyPath, serverName); } } // Key Not found, attempt to look up the provider and decrypt CEK SqlColumnEncryptionKeyStoreProvider provider; if (!SqlConnection.TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out provider)) { throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName, SqlConnection.GetColumnEncryptionSystemKeyStoreProviders(), SqlConnection.GetColumnEncryptionCustomKeyStoreProviders()); } // Decrypt the CEK // We will simply bubble up the exception from the DecryptColumnEncryptionKey function. byte[] plaintextKey; try { plaintextKey = provider.DecryptColumnEncryptionKey(keyInfo.keyPath, keyInfo.algorithmName, keyInfo.encryptedKey); } catch (Exception e) { // Generate a new exception and throw. string keyHex = SqlSecurityUtility.GetBytesAsString(keyInfo.encryptedKey, fLast: true, countOfBytes: 10); throw SQL.KeyDecryptionFailed(keyInfo.keyStoreName, keyHex, e); } encryptionKey = new SqlClientSymmetricKey(plaintextKey); // If the cache TTL is zero, don't even bother inserting to the cache. if (SqlConnection.ColumnEncryptionKeyCacheTtl != TimeSpan.Zero) { // In case multiple threads reach here at the same time, the first one wins. // The allocated memory will be reclaimed by Garbage Collector. DateTimeOffset expirationTime = DateTimeOffset.UtcNow.Add(SqlConnection.ColumnEncryptionKeyCacheTtl); _cache.Add(cacheLookupKey, encryptionKey, expirationTime); } } return(true); }
/// <summary> /// Creates an instance of SqlAes256CbcAlgorithm class with a given key /// </summary> /// <param name="encryptionKey">Root key</param> /// <param name="encryptionType">Encryption Type. Expected values are either Determinitic or Randomized.</param> /// <param name="encryptionAlgorithm">Encryption Algorithm.</param> /// <returns></returns> internal override SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm) { // Callers should have validated the encryption algorithm and the encryption key Debug.Assert(encryptionKey != null); Debug.Assert(string.Equals(encryptionAlgorithm, SqlAes256CbcAlgorithm.AlgorithmName, StringComparison.OrdinalIgnoreCase) == true); // Validate encryption type if (!((encryptionType == SqlClientEncryptionType.Deterministic) || (encryptionType == SqlClientEncryptionType.Randomized))) { throw SQL.InvalidEncryptionType(SqlAes256CbcAlgorithm.AlgorithmName, encryptionType, SqlClientEncryptionType.Deterministic, SqlClientEncryptionType.Randomized); } // Get the cached encryption algorithm if one exists or create a new one, add it to cache and use it // // For now, we only have one version. In future, we may need to parse the algorithm names to derive the version byte. const byte algorithmVersion = 0x1; StringBuilder algorithmKeyBuilder = new StringBuilder(Convert.ToBase64String(encryptionKey.RootKey), SqlSecurityUtility.GetBase64LengthFromByteLength(encryptionKey.RootKey.Length) + 4 /*separators, type and version*/); #if DEBUG int capacity = algorithmKeyBuilder.Capacity; #endif //DEBUG algorithmKeyBuilder.Append(":"); algorithmKeyBuilder.Append((int)encryptionType); algorithmKeyBuilder.Append(":"); algorithmKeyBuilder.Append(algorithmVersion); string algorithmKey = algorithmKeyBuilder.ToString(); #if DEBUG Debug.Assert(algorithmKey.Length <= capacity, "We needed to allocate a larger array"); #endif //DEBUG SqlAes256CbcAlgorithm aesAlgorithm; if (!_encryptionAlgorithms.TryGetValue(algorithmKey, out aesAlgorithm)) { SqlAeadAes256CbcHmac256EncryptionKey encryptedKey = new SqlAeadAes256CbcHmac256EncryptionKey(encryptionKey.RootKey, SqlAes256CbcAlgorithm.AlgorithmName); aesAlgorithm = new SqlAes256CbcAlgorithm(encryptedKey, encryptionType, algorithmVersion); // In case multiple threads reach here at the same time, the first one adds the value // the second one will be a no-op, the allocated memory will be claimed by Garbage Collector. _encryptionAlgorithms.TryAdd(algorithmKey, aesAlgorithm); } return(aesAlgorithm); }