// Private #region Methods private byte[] calculateHash(byte[] bytes, int offset) { using (var hasher = new SHA256Cng()) { hasher.TransformFinalBlock(bytes, offset + HashCalculationStartPosition, _clusterSizeBytes - HashCalculationStartPosition); return(hasher.Hash); } }
public void ByteBlockHashTest() { byte[] bytes = new byte[bufferSize]; Random r = new Random(); r.NextBytes(bytes); int offset = bufferSize / 2; int length = bufferSize - offset; DataBlock b1 = new DataBlock(bytes); DataBlock b2 = new DataBlock(b1, offset); byte[] h0; using (var h = new SHA256Cng()) { h.TransformFinalBlock(bytes, offset, length); h0 = h.Hash; } byte[] h1; using (var h = new SHA256Cng()) { h.TransformFinalBlock(b1, offset, length); h1 = h.Hash; } byte[] h2; using (var h = new SHA256Cng()) { h.TransformFinalBlock(b2, 0, b2.Length); h2 = h.Hash; } Assert.IsTrue(h0.SequenceEqual(h1)); Assert.IsTrue(h0.SequenceEqual(h2)); }
private byte[] ComputeQueryStringHash(string queryString) { // Validate the input parameters if (string.IsNullOrWhiteSpace(queryString)) { string argumentName = "queryString"; if (null == queryString) { throw SQL.NullArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); } else { throw SQL.EmptyArgumentInternal(argumentName, ClassName, ComputeQueryStringHashName); } } byte[] queryStringBytes = Encoding.Unicode.GetBytes(queryString); // Compute hash byte[] hash; using (SHA256Cng sha256 = new SHA256Cng()) { sha256.TransformFinalBlock(queryStringBytes, 0, queryStringBytes.Length); hash = sha256.Hash; } return(hash); }
public byte[] HashString(string s) { using (var hasher = new SHA256Cng()) { byte[] bytes = Encoding.Unicode.GetBytes(s); hasher.TransformFinalBlock(bytes, 0, bytes.Length); return hasher.Hash; } }
public KeyThumbprint(CngKey key) : this() { using (SHA256Cng hasher = new SHA256Cng()) { byte[] keyBytes = key.Export(CngKeyBlobFormat.EccPublicBlob); hasher.TransformFinalBlock(keyBytes, 0, keyBytes.Length); Buffer.BlockCopy(hasher.Hash, 0, Bytes, 0, Length); } }
private byte[] ComputeMasterKeyMetadataHash(string masterKeyPath, bool allowEnclaveComputations, bool isSystemOp) { // Validate the input parameters ValidateNonEmptyCertificatePath(masterKeyPath, isSystemOp); // Validate masterKeyPath Length ValidateCertificatePathLength(masterKeyPath, isSystemOp); string masterkeyMetadata = ProviderName + masterKeyPath + allowEnclaveComputations; masterkeyMetadata = masterkeyMetadata.ToLowerInvariant(); byte[] masterkeyMetadataBytes = Encoding.Unicode.GetBytes(masterkeyMetadata.ToLowerInvariant()); // Compute hash byte[] hash; using (SHA256Cng sha256 = new SHA256Cng()) { sha256.TransformFinalBlock(masterkeyMetadataBytes, 0, masterkeyMetadataBytes.Length); hash = sha256.Hash; } return(hash); }
private static byte[] HashString(SecureString s) { // Until we have some better way to securely handle generating hashes in unmanaged memory... // Of course the resulting byte[] needs to be handled securely as well, but who knows what SHA256Cng is doing? IntPtr sPointer = Marshal.SecureStringToGlobalAllocUnicode(s); try { IntPtr p = sPointer; byte[] bytes = new byte[2]; using (var hasher = new SHA256Cng()) { for (int i = 0; i < s.Length - 1; i++) { Marshal.Copy(p, bytes, 0, 2); hasher.TransformBlock(bytes, 0, 2, null, 0); p = IntPtr.Add(p, 2); } Marshal.Copy(p, bytes, 0, 2); hasher.TransformFinalBlock(bytes, 0, 2); return(hasher.Hash); } } finally { Marshal.ZeroFreeGlobalAllocUnicode(sPointer); } }
/// <include file='..\..\..\..\..\..\..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml' path='docs/members[@name="SqlColumnEncryptionCngProvider"]/DecryptColumnEncryptionKey/*' /> public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters ValidateNonEmptyKeyPath(masterKeyPath, isSystemOp: true); if (null == encryptedColumnEncryptionKey) { throw SQL.NullEncryptedColumnEncryptionKey(); } if (0 == encryptedColumnEncryptionKey.Length) { throw SQL.EmptyEncryptedColumnEncryptionKey(); } // Validate encryptionAlgorithm ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); // Create RSA Provider with the given CNG name and key name RSACng rsaCngProvider = CreateRSACngProvider(masterKeyPath, isSystemOp: true); // Validate whether the key is RSA one or not and then get the key size int keySizeInBytes = GetKeySize(rsaCngProvider); // Validate and decrypt the EncryptedColumnEncryptionKey // Format is // version + keyPathLength + ciphertextLength + keyPath + ciphervtext + signature // // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and // we will not validate it against the data contained in the CMK metadata (masterKeyPath). // Validate the version byte if (encryptedColumnEncryptionKey[0] != _version[0]) { throw SQL.InvalidAlgorithmVersionInEncryptedCEK(encryptedColumnEncryptionKey[0], _version[0]); } // Get key path length int currentIndex = _version.Length; UInt16 keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(UInt16); // Get ciphertext length UInt16 cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(UInt16); // Skip KeyPath // KeyPath exists only for troubleshooting purposes and doesnt need validation. currentIndex += keyPathLength; // validate the ciphertext length if (cipherTextLength != keySizeInBytes) { throw SQL.InvalidCiphertextLengthInEncryptedCEKCng(cipherTextLength, keySizeInBytes, masterKeyPath); } // Validate the signature length // Signature length should be same as the key side for RSA PKCSv1.5 int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; if (signatureLength != keySizeInBytes) { throw SQL.InvalidSignatureInEncryptedCEKCng(signatureLength, keySizeInBytes, masterKeyPath); } // Get ciphertext byte[] cipherText = new byte[cipherTextLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherText.Length); currentIndex += cipherTextLength; // Get signature byte[] signature = new byte[signatureLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); // Compute the hash to validate the signature byte[] hash; using (SHA256Cng sha256 = new SHA256Cng()) { sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); hash = sha256.Hash; } Debug.Assert(hash != null, @"hash should not be null while decrypting encrypted column encryption key."); // Validate the signature if (!RSAVerifySignature(hash, signature, rsaCngProvider)) { throw SQL.InvalidSignature(masterKeyPath); } // Decrypt the CEK return(RSADecrypt(rsaCngProvider, cipherText)); }
/// <include file='..\..\..\..\..\..\..\doc\snippets\Microsoft.Data.SqlClient\SqlColumnEncryptionCngProvider.xml' path='docs/members[@name="SqlColumnEncryptionCngProvider"]/EncryptColumnEncryptionKey/*' /> 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); }
/// <summary> /// This function uses a certificate specified by the key path /// and decrypts an encrypted CEK with RSA encryption algorithm. /// </summary> /// <param name="masterKeyPath">Complete path of a certificate</param> /// <param name="encryptionAlgorithm">Asymmetric Key Encryption Algorithm</param> /// <param name="encryptedColumnEncryptionKey">Encrypted Column Encryption Key</param> /// <returns>Plain text column encryption key</returns> public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters ValidateNonEmptyCertificatePath(masterKeyPath, isSystemOp: true); if (null == encryptedColumnEncryptionKey) { throw SQL.NullEncryptedColumnEncryptionKey(); } else if (0 == encryptedColumnEncryptionKey.Length) { throw SQL.EmptyEncryptedColumnEncryptionKey(); } // Validate encryptionAlgorithm ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); // Validate key path length ValidateCertificatePathLength(masterKeyPath, isSystemOp: true); // Parse the path and get the X509 cert X509Certificate2 certificate = GetCertificateByPath(masterKeyPath, isSystemOp: true); int keySizeInBytes = certificate.PublicKey.Key.KeySize / 8; // Validate and decrypt the EncryptedColumnEncryptionKey // Format is // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature // // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and // we will not validate it against the data contained in the CMK metadata (masterKeyPath). // Validate the version byte if (encryptedColumnEncryptionKey[0] != _version[0]) { throw SQL.InvalidAlgorithmVersionInEncryptedCEK(encryptedColumnEncryptionKey[0], _version[0]); } // Get key path length int currentIndex = _version.Length; Int16 keyPathLength = BitConverter.ToInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(Int16); // Get ciphertext length int cipherTextLength = BitConverter.ToInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(Int16); // Skip KeyPath // KeyPath exists only for troubleshooting purposes and doesnt need validation. currentIndex += keyPathLength; // validate the ciphertext length if (cipherTextLength != keySizeInBytes) { throw SQL.InvalidCiphertextLengthInEncryptedCEK(cipherTextLength, keySizeInBytes, masterKeyPath); } // Validate the signature length int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; if (signatureLength != keySizeInBytes) { throw SQL.InvalidSignatureInEncryptedCEK(signatureLength, keySizeInBytes, masterKeyPath); } // Get ciphertext byte[] cipherText = new byte[cipherTextLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherTextLength); currentIndex += cipherTextLength; // Get signature byte[] signature = new byte[signatureLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); // Compute the hash to validate the signature byte[] hash; using (SHA256Cng sha256 = new SHA256Cng()) { sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); hash = sha256.Hash; } Debug.Assert(hash != null, @"hash should not be null while decrypting encrypted column encryption key."); // Validate the signature if (!RSAVerifySignature(hash, signature, certificate)) { throw SQL.InvalidCertificateSignature(masterKeyPath); } // Decrypt the CEK return(RSADecrypt(cipherText, certificate)); }
private void CompressFunct() { var CompressionIO = new byte[104857600]; var blocksPerChunk = CompressionIO.Length / bs + (CompressionIO.Length % bs > 0 ? 1 : 0); var sourceFs = new LocalFileSystem(inFolderPath); var destFs = new LocalFileSystem(outFolderPath); foreach (var file in sourceFs.EnumerateEntries().Where(item => item.Type == DirectoryEntryType.File)) { Out.Log($"{file.FullPath}\r\n"); var outFileName = $"{file.Name}.nsz"; using (var outputFileBase = FolderTools.CreateAndOpen(file, destFs, outFileName)) using (var outputFile = new FilePositionStorage(outputFileBase)) using (var inputFileBase = sourceFs.OpenFile(file.FullPath, OpenMode.Read)) using (var inputFile = new FilePositionStorage(inputFileBase)) { amountOfBlocks = (int)Math.Ceiling((decimal)inputFile.GetSize() / bs); sizeOfSize = (int)Math.Ceiling(Math.Log(bs, 2) / 8); var perBlockHeaderSize = sizeOfSize + 1; var headerSize = 0x15 + perBlockHeaderSize * amountOfBlocks; outputFile.Seek(headerSize); var nsZipMagic = new byte[] { 0x6e, 0x73, 0x5a, 0x69, 0x70 }; var nsZipMagicRandomKey = new byte[5]; secureRNG.GetBytes(nsZipMagicRandomKey); Util.XorArrays(nsZipMagic, nsZipMagicRandomKey); var chunkIndex = 0; nsZipHeader = new byte[headerSize]; Array.Copy(nsZipMagic, 0x00, nsZipHeader, 0x00, 0x05); Array.Copy(nsZipMagicRandomKey, 0x00, nsZipHeader, 0x05, 0x05); nsZipHeader[0x0A] = 0x00; //Version nsZipHeader[0x0B] = 0x01; //Type nsZipHeader[0x0C] = (byte)(bs >> 32); nsZipHeader[0x0D] = (byte)(bs >> 24); nsZipHeader[0x0E] = (byte)(bs >> 16); nsZipHeader[0x0F] = (byte)(bs >> 8); nsZipHeader[0x10] = (byte)bs; nsZipHeader[0x11] = (byte)(amountOfBlocks >> 24); nsZipHeader[0x12] = (byte)(amountOfBlocks >> 16); nsZipHeader[0x13] = (byte)(amountOfBlocks >> 8); nsZipHeader[0x14] = (byte)amountOfBlocks; sha256Compressed = new SHA256Cng(); long maxPos = inputFile.GetSize(); int blocksLeft; int blocksInThisChunk; do { var outputLen = new int[blocksPerChunk]; //Filled with 0 inputFile.Read(CompressionIO); blocksLeft = amountOfBlocks - chunkIndex * blocksPerChunk; blocksInThisChunk = Math.Min(blocksPerChunk, blocksLeft); var opt = new ParallelOptions() { MaxDegreeOfParallelism = this.MaxDegreeOfParallelism }; //for(int index = 0; index < blocksInThisChunk; ++index) Parallel.For(0, blocksInThisChunk, opt, index => { var currentBlockID = chunkIndex * blocksPerChunk + index; var startPosRelative = index * bs; //Don't directly cast bytesLeft to int or sectors over 2 GB will overflow into negative size long startPos = (long)currentBlockID * (long)bs; long bytesLeft = maxPos - startPos; var blockSize = bs < bytesLeft ? bs : (int)bytesLeft; Out.Print($"Block: {currentBlockID + 1}/{amountOfBlocks} ({opt.MaxDegreeOfParallelism})\r\n"); CompressionAlgorithm compressionAlgorithm; outputLen[index] = CompressBlock(ref CompressionIO, startPosRelative, blockSize, out compressionAlgorithm); //Out.Log($"inputLen[{currentBlockID}]: {blockSize}\r\n"); //Out.Log($"outputLen[{currentBlockID}]: {outputLen[index]} bytesLeft={bytesLeft}\r\n"); var offset = currentBlockID * (sizeOfSize + 1); switch (compressionAlgorithm) { case CompressionAlgorithm.None: nsZipHeader[0x15 + offset] = 0x00; break; case CompressionAlgorithm.Zstandard: nsZipHeader[0x15 + offset] = 0x01; break; case CompressionAlgorithm.LZMA: nsZipHeader[0x15 + offset] = 0x02; break; default: throw new ArgumentOutOfRangeException(); } for (var j = 0; j < sizeOfSize; ++j) { nsZipHeader[0x16 + offset + j] = (byte)(outputLen[index] >> ((sizeOfSize - j - 1) * 8)); } }); for (int index = 0; index < blocksInThisChunk; ++index) { var startPos = index * bs; sha256Compressed.TransformBlock(CompressionIO, startPos, outputLen[index], null, 0); var dataToWrite = CompressionIO.AsSpan().Slice(startPos, outputLen[index]); outputFile.Write(dataToWrite); } ++chunkIndex; } while (blocksLeft - blocksInThisChunk > 0); outputFile.Write(nsZipHeader, 0); sha256Header = new SHA256Cng(); sha256Header.ComputeHash(nsZipHeader); var sha256Hash = new byte[0x20]; Array.Copy(sha256Header.Hash, sha256Hash, 0x20); sha256Compressed.TransformFinalBlock(new byte[0], 0, 0); Util.XorArrays(sha256Hash, sha256Compressed.Hash); //Console.WriteLine(sha256Header.Hash.ToHexString()); //Console.WriteLine(sha256Compressed.Hash.ToHexString()); outputFile.Seek(0, SeekOrigin.End); outputFile.Write(sha256Hash.AsSpan().Slice(0, 0x10)); } } }
/// <summary> /// This function uses the asymmetric key specified by the key path /// and decrypts an encrypted CEK with RSA encryption algorithm. /// </summary> /// <param name="masterKeyPath">Complete path of an asymmetric key in AKV</param> /// <param name="encryptionAlgorithm">Asymmetric Key Encryption Algorithm</param> /// <param name="encryptedColumnEncryptionKey">Encrypted Column Encryption Key</param> /// <returns>Plain text column encryption key</returns> public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters ValidateNonEmptyAKVPath(masterKeyPath, isSystemOp: true); if (null == encryptedColumnEncryptionKey) { throw new ArgumentNullException(Constants.AeParamEncryptedCek, "Internal error. Encrypted column encryption key cannot be null."); } if (0 == encryptedColumnEncryptionKey.Length) { throw new ArgumentException(@"Internal error. Empty encrypted column encryption key specified.", Constants.AeParamEncryptedCek); } // Validate encryptionAlgorithm ValidateEncryptionAlgorithm(ref encryptionAlgorithm, isSystemOp: true); // Validate whether the key is RSA one or not and then get the key size int keySizeInBytes = GetAkvKeySize(masterKeyPath); // Validate and decrypt the EncryptedColumnEncryptionKey // Format is // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature // // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and // we will not validate it against the data contained in the CMK metadata (masterKeyPath). // Validate the version byte if (encryptedColumnEncryptionKey[0] != firstVersion[0]) { throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, @"Specified encrypted column encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.", encryptedColumnEncryptionKey[0].ToString(@"X2"), firstVersion[0].ToString("X2")), Constants.AeParamEncryptedCek); } // Get key path length int currentIndex = firstVersion.Length; var keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(ushort); // Get ciphertext length var cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); currentIndex += sizeof(ushort); // Skip KeyPath // KeyPath exists only for troubleshooting purposes and doesnt need validation. currentIndex += keyPathLength; // validate the ciphertext length if (cipherTextLength != keySizeInBytes) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, @"The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (Azure Key Vault key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.", cipherTextLength, keySizeInBytes, masterKeyPath), Constants.AeParamEncryptedCek); } // Validate the signature length int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; if (signatureLength != keySizeInBytes) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, @"The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (Azure Key Vault key) in '{2}'. The encrypted column encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.", signatureLength, keySizeInBytes, masterKeyPath), Constants.AeParamEncryptedCek); } // Get ciphertext var cipherText = new byte[cipherTextLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherTextLength); currentIndex += cipherTextLength; // Get signature var signature = new byte[signatureLength]; Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); // Compute the hash to validate the signature byte[] hash; using (var sha256 = new SHA256Cng()) { sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); hash = sha256.Hash; } if (null == hash) { throw new CryptographicException("Hash should not be null while decrypting encrypted column encryption key."); } // Validate the signature if (!AzureKeyVaultVerifySignature(hash, signature, masterKeyPath)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, @"The specified encrypted column encryption key signature does not match the signature computed with the column master key (Asymmetric key in Azure Key Vault) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.", masterKeyPath), Constants.AeParamEncryptedCek); } // Decrypt the CEK return(AzureKeyVaultUnWrap(masterKeyPath, encryptionAlgorithm, cipherText)); }
/// <summary> /// This function uses the asymmetric key specified by the key path /// and encrypts CEK with RSA encryption algorithm. /// </summary> /// <param name="masterKeyPath"></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 ValidateNonEmptyAKVPath(masterKeyPath, false); if (null == columnEncryptionKey) { throw new ArgumentNullException(Constants.AeParamColumnEncryptionKey, @"Column encryption key cannot be null."); } if (0 == columnEncryptionKey.Length) { throw new ArgumentException(@"Empty column encryption key specified.", Constants.AeParamColumnEncryptionKey); } // Validate encryptionAlgorithm ValidateEncryptionAlgorithm(ref encryptionAlgorithm, false); // Validate whether the key is RSA one or not and then get the key size int keySizeInBytes = GetAkvKeySize(masterKeyPath); // We currently only support one version var version = new[] { firstVersion[0] }; // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath var masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant()); var keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length); // Encrypt the plain text var cipherText = AzureKeyVaultWrap(masterKeyPath, encryptionAlgorithm, columnEncryptionKey); var cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length); if (cipherText.Length != keySizeInBytes) { throw new CryptographicException(@"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 var signedHash = AzureKeyVaultSignHashedData(hash, masterKeyPath); if (signedHash.Length != keySizeInBytes) { throw new CryptographicException(@"Signed hash length does not match the RSA key size"); } if (!AzureKeyVaultVerifySignature(hash, signedHash, masterKeyPath)) { throw new CryptographicException(@"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; var 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); }