예제 #1
0
        /// <summary>
        /// Decrypt the keys that need to be sent to the enclave
        /// </summary>
        /// <param name="keysTobeSentToEnclave">Keys that need to sent to the enclave</param>
        /// <param name="serverName"></param>
        /// <returns></returns>
        private List <ColumnEncryptionKeyInfo> GetDecryptedKeysToBeSentToEnclave(Dictionary <int, SqlTceCipherInfoEntry> keysTobeSentToEnclave, string serverName)
        {
            List <ColumnEncryptionKeyInfo> decryptedKeysToBeSentToEnclave = new List <ColumnEncryptionKeyInfo>();

            foreach (SqlTceCipherInfoEntry cipherInfo in keysTobeSentToEnclave.Values)
            {
                SqlClientSymmetricKey sqlClientSymmetricKey   = null;
                SqlEncryptionKeyInfo? encryptionkeyInfoChosen = null;
                SqlSecurityUtility.DecryptSymmetricKey(cipherInfo, serverName, out sqlClientSymmetricKey,
                                                       out encryptionkeyInfoChosen);

                if (sqlClientSymmetricKey == null)
                {
                    throw SQL.NullArgumentInternal("sqlClientSymmetricKey", ClassName, GetDecryptedKeysToBeSentToEnclaveName);
                }
                if (cipherInfo.ColumnEncryptionKeyValues == null)
                {
                    throw SQL.NullArgumentInternal("ColumnEncryptionKeyValues", ClassName, GetDecryptedKeysToBeSentToEnclaveName);
                }
                if (!(cipherInfo.ColumnEncryptionKeyValues.Count > 0))
                {
                    throw SQL.ColumnEncryptionKeysNotFound();
                }

                //cipherInfo.CekId is always 0, hence used cipherInfo.ColumnEncryptionKeyValues[0].cekId. Even when cek has multiple ColumnEncryptionKeyValues
                //the cekid and the plaintext value will remain the same, what varies is the encrypted cek value, since the cek can be encrypted by
                //multiple CMKs
                decryptedKeysToBeSentToEnclave.Add(new ColumnEncryptionKeyInfo(sqlClientSymmetricKey.RootKey,
                                                                               cipherInfo.ColumnEncryptionKeyValues[0].databaseId,
                                                                               cipherInfo.ColumnEncryptionKeyValues[0].cekMdVersion, cipherInfo.ColumnEncryptionKeyValues[0].cekId));
            }
            return(decryptedKeysToBeSentToEnclave);
        }
예제 #2
0
        /// <summary>
        /// Encrypt the byte package containing keys with the session key
        /// </summary>
        /// <param name="bytePackage">byte package containing keys</param>
        /// <param name="sessionKey">session key used to encrypt the package</param>
        /// <param name="serverName">server hosting the enclave</param>
        /// <returns></returns>
        private byte[] EncryptBytePackage(byte[] bytePackage, byte[] sessionKey, string serverName)
        {
            if (sessionKey == null)
            {
                throw SQL.NullArgumentInternal("sessionKey", ClassName, "EncryptBytePackage");
            }
            if (sessionKey.Length == 0)
            {
                throw SQL.EmptyArgumentInternal("sessionKey", ClassName, "EncryptBytePackage");
            }
            //bytePackage is created internally in this class and is guaranteed to be non null and non empty

            try
            {
                SqlClientSymmetricKey        symmetricKey = new SqlClientSymmetricKey(sessionKey);
                SqlClientEncryptionAlgorithm sqlClientEncryptionAlgorithm =
                    SqlAeadAes256CbcHmac256Factory.Create(symmetricKey, SqlClientEncryptionType.Randomized,
                                                          SqlAeadAes256CbcHmac256Algorithm.AlgorithmName);
                return(sqlClientEncryptionAlgorithm.EncryptData(bytePackage));
            }
            catch (Exception e)
            {
                throw SQL.FailedToEncryptRegisterRulesBytePackage(e);
            }
        }
예제 #3
0
        /// <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);
        }
        /// <summary>
        /// Gets the algorithm handle instance for a given algorithm and instantiates it using the provided key and the encryption type.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="type"></param>
        /// <param name="algorithmName"></param>
        /// <param name="encryptionAlgorithm"></param>
        internal void GetAlgorithm(SqlClientSymmetricKey key, byte type, string algorithmName, out SqlClientEncryptionAlgorithm encryptionAlgorithm)
        {
            encryptionAlgorithm = null;

            SqlClientEncryptionAlgorithmFactory factory = null;

            if (!_encryptionAlgoFactoryList.TryGetValue(algorithmName, out factory))
            {
                throw SQL.UnknownColumnEncryptionAlgorithm(algorithmName,
                                                           SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetRegisteredCipherAlgorithmNames());
            }

            Debug.Assert(null != factory, "Null Algorithm Factory class detected");

            // If the factory exists, following method will Create an algorithm object. If this fails,
            // it will raise an exception.
            encryptionAlgorithm = factory.Create(key, (SqlClientEncryptionType)type, algorithmName);
        }
예제 #5
0
        /// <summary>
        /// <para> Decrypts the symmetric key and saves it in metadata. In addition, initializes
        /// the SqlClientEncryptionAlgorithm for rapid decryption.</para>
        /// </summary>
        internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection connection, SqlCommand command)
        {
            Debug.Assert(md is not null, "md should not be null in DecryptSymmetricKey.");

            SqlClientSymmetricKey symKey = null;
            SqlEncryptionKeyInfo  encryptionkeyInfoChosen = null;

            DecryptSymmetricKey(md.EncryptionInfo, out symKey, out encryptionkeyInfoChosen, connection, command);

            // Given the symmetric key instantiate a SqlClientEncryptionAlgorithm object and cache it in metadata
            md.CipherAlgorithm = null;
            SqlClientEncryptionAlgorithm cipherAlgorithm = null;
            string algorithmName = ValidateAndGetEncryptionAlgorithmName(md.CipherAlgorithmId, md.CipherAlgorithmName);                        // may throw

            SqlClientEncryptionAlgorithmFactoryList.GetInstance().GetAlgorithm(symKey, md.EncryptionType, algorithmName, out cipherAlgorithm); // will validate algorithm name and type
            Debug.Assert(cipherAlgorithm is not null);
            md.CipherAlgorithm   = cipherAlgorithm;
            md.EncryptionKeyInfo = encryptionkeyInfoChosen;
            return;
        }
예제 #6
0
        /// <summary>
        /// Derives all the required keys from the given root key
        /// </summary>
        /// <param name="rootKey">Root key used to derive all the required derived keys</param>
        internal SqlAeadAes256CbcHmac256EncryptionKey(byte[] rootKey, string algorithmName) : base(rootKey)
        {
            _algorithmName = algorithmName;

            int keySizeInBytes = KeySize / 8;

            // Key validation
            if (rootKey.Length != keySizeInBytes)
            {
                throw SQL.InvalidKeySize(_algorithmName,
                                         rootKey.Length,
                                         keySizeInBytes);
            }

            // Derive keys from the root key
            //
            // Derive encryption key
            string encryptionKeySalt = string.Format(_encryptionKeySaltFormat,
                                                     _algorithmName,
                                                     KeySize);

            byte[] buff1 = new byte[keySizeInBytes];
            SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(encryptionKeySalt), RootKey, buff1);
            _encryptionKey = new SqlClientSymmetricKey(buff1);

            // Derive mac key
            string macKeySalt = string.Format(_macKeySaltFormat, _algorithmName, KeySize);

            byte[] buff2 = new byte[keySizeInBytes];
            SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(macKeySalt), RootKey, buff2);
            _macKey = new SqlClientSymmetricKey(buff2);

            // Derive iv key
            string ivKeySalt = string.Format(_ivKeySaltFormat, _algorithmName, KeySize);

            byte[] buff3 = new byte[keySizeInBytes];
            SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(ivKeySalt), RootKey, buff3);
            _ivKey = new SqlClientSymmetricKey(buff3);
        }
예제 #7
0
        /// <summary>
        /// Decrypts the symmetric key and saves it in metadata.
        /// </summary>
        internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoEntry, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, SqlConnection connection, SqlCommand command)
        {
            Debug.Assert(sqlTceCipherInfoEntry is not null, "sqlTceCipherInfoEntry should not be null in DecryptSymmetricKey.");
            Debug.Assert(sqlTceCipherInfoEntry.ColumnEncryptionKeyValues is not null,
                         "sqlTceCipherInfoEntry.ColumnEncryptionKeyValues should not be null in DecryptSymmetricKey.");

            sqlClientSymmetricKey   = null;
            encryptionkeyInfoChosen = null;
            Exception            lastException  = null;
            SqlSymmetricKeyCache globalCekCache = SqlSymmetricKeyCache.GetInstance();

            foreach (SqlEncryptionKeyInfo keyInfo in sqlTceCipherInfoEntry.ColumnEncryptionKeyValues)
            {
                try
                {
                    sqlClientSymmetricKey = ShouldUseInstanceLevelProviderFlow(keyInfo.keyStoreName, connection, command) ?
                                            GetKeyFromLocalProviders(keyInfo, connection, command) :
                                            globalCekCache.GetKey(keyInfo, connection, command);
                    encryptionkeyInfoChosen = keyInfo;
                    break;
                }
                catch (Exception e)
                {
                    lastException = e;
                }
            }

            if (sqlClientSymmetricKey is null)
            {
                Debug.Assert(lastException is not null, "CEK decryption failed without raising exceptions");
                throw lastException;
            }

            Debug.Assert(encryptionkeyInfoChosen is not null, "encryptionkeyInfoChosen must have a value.");
        }
예제 #8
0
        /// <summary>
        /// Decrypts the symmetric key and saves it in metadata.
        /// </summary>
        internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry?sqlTceCipherInfoEntry, string serverName, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo?encryptionkeyInfoChosen)
        {
            Debug.Assert(serverName != null, @"serverName should not be null in DecryptSymmetricKey.");
            Debug.Assert(sqlTceCipherInfoEntry.HasValue, "sqlTceCipherInfoEntry should not be null in DecryptSymmetricKey.");
            Debug.Assert(sqlTceCipherInfoEntry.Value.ColumnEncryptionKeyValues != null,
                         "sqlTceCipherInfoEntry.ColumnEncryptionKeyValues should not be null in DecryptSymmetricKey.");

            sqlClientSymmetricKey   = null;
            encryptionkeyInfoChosen = null;
            Exception            lastException = null;
            SqlSymmetricKeyCache cache         = SqlSymmetricKeyCache.GetInstance();

            foreach (SqlEncryptionKeyInfo keyInfo in sqlTceCipherInfoEntry.Value.ColumnEncryptionKeyValues)
            {
                try
                {
                    if (cache.GetKey(keyInfo, serverName, out sqlClientSymmetricKey))
                    {
                        encryptionkeyInfoChosen = keyInfo;
                        break;
                    }
                }
                catch (Exception e)
                {
                    lastException = e;
                }
            }

            if (null == sqlClientSymmetricKey)
            {
                Debug.Assert(null != lastException, "CEK decryption failed without raising exceptions");
                throw lastException;
            }

            Debug.Assert(encryptionkeyInfoChosen.HasValue, "encryptionkeyInfoChosen must have a value.");
        }
        /// <summary>
        /// <para> Retrieves Symmetric Key (in plaintext) given the encryption material.</para>
        /// </summary>
        internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnection connection, SqlCommand command)
        {
            string serverName = connection.DataSource;

            Debug.Assert(serverName is not 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
            if (!(_cache.Get(cacheLookupKey) is SqlClientSymmetricKey encryptionKey))
            {
                Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null");

                SqlSecurityUtility.ThrowIfKeyPathIsNotTrustedForServer(serverName, keyInfo.keyPath);

                // Key Not found, attempt to look up the provider and decrypt CEK
                if (!SqlSecurityUtility.TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command))
                {
                    throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName,
                                                               SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(),
                                                               SqlSecurityUtility.GetListOfProviderNamesThatWereSearched(connection, command));
                }

                // Decrypt the CEK
                // We will simply bubble up the exception from the DecryptColumnEncryptionKey function.
                byte[] plaintextKey;
                try
                {
                    // to prevent conflicts between CEK caches, global providers should not use their own CEK caches
                    provider.ColumnEncryptionKeyCacheTtl = new TimeSpan(0);
                    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(encryptionKey);
        }
예제 #10
0
        /// <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);
        }