Beispiel #1
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);
            }
        }
Beispiel #2
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);
        }
Beispiel #3
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));
                            }
            }
        }
Beispiel #4
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);
        }