Exemplo n.º 1
0
        // 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);
            }
        }
Exemplo n.º 2
0
        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));
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
 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;
     }
 }
Exemplo n.º 5
0
 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);
        }
Exemplo n.º 7
0
        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);
            }
        }
Exemplo n.º 8
0
        /// <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));
        }
Exemplo n.º 9
0
        /// <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));
        }
Exemplo n.º 11
0
        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));
                            }
            }
        }
Exemplo n.º 12
0
        /// <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));
        }
Exemplo n.º 13
0
        /// <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);
        }