/// <summary> /// This function uses the asymmetric key specified by the key path /// and encrypts CEK with RSA encryption algorithm. /// </summary> /// <param name="keyPath">Complete path of an asymmetric key in AKV</param> /// <param name="encryptionAlgorithm">Asymmetric Key Encryption Algorithm</param> /// <param name="columnEncryptionKey">Plain text column encryption key</param> /// <returns>Encrypted column encryption key</returns> public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters ValidateNonEmptyKeyPath(masterKeyPath, isSystemOp: false); if (null == columnEncryptionKey) { throw SQL.NullColumnEncryptionKey(); } else if (0 == columnEncryptionKey.Length) { throw SQL.EmptyColumnEncryptionKey(); } // Validate encryptionAlgorithm ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: false); // CreateCNGProviderWithKey RSACng rsaCngProvider = CreateRSACngProvider(masterKeyPath, isSystemOp: false); // Validate whether the key is RSA one or not and then get the key size int keySizeInBytes = GetKeySize(rsaCngProvider); // Construct the encryptedColumnEncryptionKey // Format is // version + keyPathLength + ciphertextLength + ciphertext + keyPath + signature // // We currently only support one version byte[] version = new byte[] { _version[0] }; // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath byte[] masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant()); byte[] keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length); // Encrypt the plain text byte[] cipherText = RSAEncrypt(rsaCngProvider, columnEncryptionKey); byte[] cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length); Debug.Assert(cipherText.Length == keySizeInBytes, @"cipherText length does not match the RSA key size"); // Compute hash // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) byte[] hash; using (SHA256Cng sha256 = new SHA256Cng()) { sha256.TransformBlock(version, 0, version.Length, version, 0); sha256.TransformBlock(keyPathLength, 0, keyPathLength.Length, keyPathLength, 0); sha256.TransformBlock(cipherTextLength, 0, cipherTextLength.Length, cipherTextLength, 0); sha256.TransformBlock(masterKeyPathBytes, 0, masterKeyPathBytes.Length, masterKeyPathBytes, 0); sha256.TransformFinalBlock(cipherText, 0, cipherText.Length); hash = sha256.Hash; } // Sign the hash byte[] signedHash = RSASignHashedData(hash, rsaCngProvider); Debug.Assert(signedHash.Length == keySizeInBytes, @"signed hash length does not match the RSA key size"); Debug.Assert(RSAVerifySignature(hash, signedHash, rsaCngProvider), @"Invalid signature of the encrypted column encryption key computed."); // Construct the encrypted column encryption key // EncryptedColumnEncryptionKey = version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature int encryptedColumnEncryptionKeyLength = version.Length + cipherTextLength.Length + keyPathLength.Length + cipherText.Length + masterKeyPathBytes.Length + signedHash.Length; byte[] encryptedColumnEncryptionKey = new byte[encryptedColumnEncryptionKeyLength]; // Copy version byte int currentIndex = 0; Buffer.BlockCopy(version, 0, encryptedColumnEncryptionKey, currentIndex, version.Length); currentIndex += version.Length; // Copy key path length Buffer.BlockCopy(keyPathLength, 0, encryptedColumnEncryptionKey, currentIndex, keyPathLength.Length); currentIndex += keyPathLength.Length; // Copy ciphertext length Buffer.BlockCopy(cipherTextLength, 0, encryptedColumnEncryptionKey, currentIndex, cipherTextLength.Length); currentIndex += cipherTextLength.Length; // Copy key path Buffer.BlockCopy(masterKeyPathBytes, 0, encryptedColumnEncryptionKey, currentIndex, masterKeyPathBytes.Length); currentIndex += masterKeyPathBytes.Length; // Copy ciphertext Buffer.BlockCopy(cipherText, 0, encryptedColumnEncryptionKey, currentIndex, cipherText.Length); currentIndex += cipherText.Length; // copy the signature Buffer.BlockCopy(signedHash, 0, encryptedColumnEncryptionKey, currentIndex, signedHash.Length); return encryptedColumnEncryptionKey; }