示例#1
0
        /// <summary>
        /// Encrypts the specified XML element using Windows DPAPI:NG.
        /// </summary>
        /// <param name="plaintextElement">The plaintext XML element to encrypt. This element is unchanged by the method.</param>
        /// <returns>The encrypted form of the XML element.</returns>
        public XElement Encrypt([NotNull] XElement plaintextElement)
        {
            // First, convert the XML element to a byte[] so that it can be encrypted.
            ProtectedMemoryBlob secret;

            using (var memoryStream = new MemoryStream())
            {
                plaintextElement.Save(memoryStream);

#if !ASPNETCORE50
                // If we're on full desktop CLR, utilize the underlying buffer directly as an optimization.
                byte[] underlyingBuffer = memoryStream.GetBuffer();
                secret = new ProtectedMemoryBlob(new ArraySegment <byte>(underlyingBuffer, 0, checked ((int)memoryStream.Length)));
                Array.Clear(underlyingBuffer, 0, underlyingBuffer.Length);
#else
                // Otherwise, need to make a copy of the buffer.
                byte[] clonedBuffer = memoryStream.ToArray();
                secret = new ProtectedMemoryBlob(clonedBuffer);
                Array.Clear(clonedBuffer, 0, clonedBuffer.Length);
#endif
            }

            // <secret decryptor="{TYPE}">
            //   ... base64 data ...
            // </secret>
            byte[] encryptedBytes = DpapiSecretSerializerHelper.ProtectWithDpapiNG(secret, _protectionDescriptorHandle);
            return(new XElement(DpapiNGEncryptedSecretElementName,
                                new XAttribute("decryptor", typeof(DpapiNGXmlDecryptor).AssemblyQualifiedName),
                                new XAttribute("version", 1),
                                Convert.ToBase64String(encryptedBytes)));
        }
        public void Encrypt_KnownKey()
        {
            // Arrange
            ProtectedMemoryBlob           kdk       = new ProtectedMemoryBlob(Encoding.UTF8.GetBytes("master key"));
            ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
                                                                                        symmetricAlgorithmFactory: Aes.Create,
                                                                                        symmetricAlgorithmKeySizeInBytes: 256 / 8,
                                                                                        validationAlgorithmFactory: () => new HMACSHA256(),
                                                                                        genRandom: new SequentialGenRandom());
            ArraySegment <byte> plaintext = new ArraySegment <byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
            ArraySegment <byte> aad       = new ArraySegment <byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);

            // Act
            byte[] retVal = encryptor.Encrypt(
                plaintext: plaintext,
                additionalAuthenticatedData: aad);

            // Assert

            // retVal := 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (keyModifier)
            //         | 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F (IV)
            //         | B7 EA 3E 32 58 93 A3 06 03 89 C6 66 03 63 08 4B (encryptedData)
            //         | 9D 8A 85 C7 0F BD 98 D8 7F 72 E7 72 3E B5 A6 26 (HMAC)
            //         | 6C 38 77 F7 66 19 A2 C9 2C BB AD DA E7 62 00 00

            string retValAsString = Convert.ToBase64String(retVal);

            Assert.Equal("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh+36j4yWJOjBgOJxmYDYwhLnYqFxw+9mNh/cudyPrWmJmw4d/dmGaLJLLut2udiAAA=", retValAsString);
        }
        public void Encrypt_KnownKey()
        {
            // Arrange
            ProtectedMemoryBlob       kdk       = new ProtectedMemoryBlob(Encoding.UTF8.GetBytes("master key"));
            GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: new SequentialGenRandom());
            ArraySegment <byte>       plaintext = new ArraySegment <byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
            ArraySegment <byte>       aad       = new ArraySegment <byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);

            // Act
            byte[] retVal = encryptor.Encrypt(
                plaintext: plaintext,
                additionalAuthenticatedData: aad,
                preBufferSize: 3,
                postBufferSize: 4);

            // Assert

            // retVal := 00 00 00 (preBuffer)
            //         | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (keyModifier)
            //         | 10 11 12 13 14 15 16 17 18 19 1A 1B (nonce)
            //         | 43 B6 91 (encryptedData)
            //         | 8D 0D 66 D9 A1 D9 44 2D 5D 8E 41 DA 39 60 9C E8 (authTag)
            //         | 00 00 00 00 (postBuffer)

            string retValAsString = Convert.ToBase64String(retVal);

            Assert.Equal("AAAAAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaG0O2kY0NZtmh2UQtXY5B2jlgnOgAAAAA", retValAsString);
        }
示例#4
0
        public CbcAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, BCryptAlgorithmHandle hmacAlgorithmHandle, IBCryptGenRandom genRandom = null)
        {
            CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");

            _genRandom = genRandom ?? BCryptGenRandomImpl.Instance;
            _sp800_108_ctr_hmac_provider           = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey);
            _symmetricAlgorithmHandle              = symmetricAlgorithmHandle;
            _symmetricAlgorithmBlockSizeInBytes    = symmetricAlgorithmHandle.GetCipherBlockLength();
            _symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes;
            _hmacAlgorithmHandle = hmacAlgorithmHandle;
            _hmacAlgorithmDigestLengthInBytes = hmacAlgorithmHandle.GetHashDigestLength();
            _hmacAlgorithmSubkeyLengthInBytes = _hmacAlgorithmDigestLengthInBytes; // for simplicity we'll generate HMAC subkeys with a length equal to the digest length

            CryptoUtil.Assert(SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES");

            CryptoUtil.Assert(HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _hmacAlgorithmDigestLengthInBytes,
                              "HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _hmacAlgorithmDigestLengthInBytes");

            CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= _hmacAlgorithmSubkeyLengthInBytes && _hmacAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "KEY_MODIFIER_SIZE_IN_BYTES <= _hmacAlgorithmSubkeyLengthInBytes && _hmacAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES");

            _contextHeader = CreateContextHeader();
        }
        public ManagedAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, Func <SymmetricAlgorithm> symmetricAlgorithmFactory, int symmetricAlgorithmKeySizeInBytes, Func <KeyedHashAlgorithm> validationAlgorithmFactory, IManagedGenRandom genRandom = null)
        {
            CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");

            _genRandom        = genRandom ?? ManagedGenRandomImpl.Instance;
            _keyDerivationKey = keyDerivationKey;

            // Validate that the symmetric algorithm has the properties we require
            using (var symmetricAlgorithm = symmetricAlgorithmFactory())
            {
                _symmetricAlgorithmFactory             = symmetricAlgorithmFactory;
                _symmetricAlgorithmBlockSizeInBytes    = symmetricAlgorithm.GetBlockSizeInBytes();
                _symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes;
            }

            // Validate that the MAC algorithm has the properties we require
            using (var validationAlgorithm = validationAlgorithmFactory())
            {
                _validationAlgorithmFactory             = validationAlgorithmFactory;
                _validationAlgorithmDigestLengthInBytes = validationAlgorithm.GetDigestSizeInBytes();
                _validationAlgorithmSubkeyLengthInBytes = _validationAlgorithmDigestLengthInBytes; // for simplicity we'll generate MAC subkeys with a length equal to the digest length
            }

            CryptoUtil.Assert(SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "SYMMETRIC_ALG_MIN_BLOCK_SIZE_IN_BYTES <= _symmetricAlgorithmBlockSizeInBytes && _symmetricAlgorithmBlockSizeInBytes <= Constants.MAX_STACKALLOC_BYTES");

            CryptoUtil.Assert(HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _validationAlgorithmDigestLengthInBytes,
                              "HASH_ALG_MIN_DIGEST_LENGTH_IN_BYTES <= _validationAlgorithmDigestLengthInBytes");

            CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= _validationAlgorithmSubkeyLengthInBytes && _validationAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "KEY_MODIFIER_SIZE_IN_BYTES <= _validationAlgorithmSubkeyLengthInBytes && _validationAlgorithmSubkeyLengthInBytes <= Constants.MAX_STACKALLOC_BYTES");

            _contextHeader = CreateContextHeader();
        }
        public IAuthenticatedEncryptorConfiguration CreateNewConfiguration()
        {
            // generate a 512-bit secret randomly
            const int KDK_SIZE_IN_BYTES = 512 / 8;
            var       secret            = ProtectedMemoryBlob.Random(KDK_SIZE_IN_BYTES);

            return(new CngCbcAuthenticatedEncryptorConfiguration(_options, secret));
        }
        public GcmAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom genRandom = null)
        {
            CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
                              "KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");

            _genRandom = genRandom ?? BCryptGenRandomImpl.Instance;
            _sp800_108_ctr_hmac_provider           = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey);
            _symmetricAlgorithmHandle              = symmetricAlgorithmHandle;
            _symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes;
            _contextHeader = CreateContextHeader();
        }
        public void Encrypt_Decrypt_RoundTrips()
        {
            // Arrange
            ProtectedMemoryBlob       kdk       = new ProtectedMemoryBlob(new byte[512 / 8]);
            GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8);
            ArraySegment <byte>       plaintext = new ArraySegment <byte>(Encoding.UTF8.GetBytes("plaintext"));
            ArraySegment <byte>       aad       = new ArraySegment <byte>(Encoding.UTF8.GetBytes("aad"));

            // Act
            byte[] ciphertext     = encryptor.Encrypt(plaintext, aad);
            byte[] decipheredtext = encryptor.Decrypt(new ArraySegment <byte>(ciphertext), aad);

            // Assert
            Assert.Equal(plaintext, decipheredtext);
        }
示例#9
0
        public IAuthenticatedEncryptorConfiguration FromXml([NotNull] XElement element)
        {
            // <cbcEncryptor reader="{TYPE}">
            //   <encryption algorithm="{STRING}" provider="{STRING}" keyLength="{INT}" />
            //   <validation algorithm="{STRING}" provider="{STRING}" />
            //   <secret>...</secret>
            // </cbcEncryptor>

            CryptoUtil.Assert(element.Name == CngCbcAuthenticatedEncryptorConfiguration.CbcEncryptorElementName,
                              @"TODO: Bad element.");

            var options = new CngCbcAuthenticatedEncryptorConfigurationOptions();

            // read <encryption> element
            var encryptionElement = element.Element(CngCbcAuthenticatedEncryptorConfiguration.EncryptionElementName);

            options.EncryptionAlgorithm         = (string)encryptionElement.Attribute("algorithm");
            options.EncryptionAlgorithmProvider = (string)encryptionElement.Attribute("provider");
            options.EncryptionAlgorithmKeySize  = (int)encryptionElement.Attribute("keyLength");

            // read <validation> element
            var validationElement = element.Element(CngCbcAuthenticatedEncryptorConfiguration.ValidationElementName);

            options.HashAlgorithm         = (string)validationElement.Attribute("algorithm");
            options.HashAlgorithmProvider = (string)validationElement.Attribute("provider");

            // read the child of the <secret> element, then decrypt it
            var encryptedSecretElement         = element.Element(CngCbcAuthenticatedEncryptorConfiguration.SecretElementName).Elements().Single();
            var secretElementDecryptorTypeName = (string)encryptedSecretElement.Attribute("decryptor");
            var secretElementDecryptorType     = Type.GetType(secretElementDecryptorTypeName, throwOnError: true);
            var secretElementDecryptor         = (IXmlDecryptor)_typeActivator.CreateInstance(_serviceProvider, secretElementDecryptorType);
            var decryptedSecretElement         = secretElementDecryptor.Decrypt(encryptedSecretElement);

            CryptoUtil.Assert(decryptedSecretElement.Name == CngCbcAuthenticatedEncryptorConfiguration.SecretElementName,
                              @"TODO: Bad element.");

            byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
            try
            {
                var protectedMemoryBlob = new ProtectedMemoryBlob(decryptedSecretBytes);
                return(new CngCbcAuthenticatedEncryptorConfiguration(options, protectedMemoryBlob));
            }
            finally
            {
                Array.Clear(decryptedSecretBytes, 0, decryptedSecretBytes.Length);
            }
        }
        public void Encrypt_Decrypt_Tampering_Fails()
        {
            // Arrange
            ProtectedMemoryBlob           kdk       = new ProtectedMemoryBlob(new byte[512 / 8]);
            ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
                                                                                        symmetricAlgorithmFactory: Aes.Create,
                                                                                        symmetricAlgorithmKeySizeInBytes: 256 / 8,
                                                                                        validationAlgorithmFactory: () => new HMACSHA256());
            ArraySegment <byte> plaintext = new ArraySegment <byte>(Encoding.UTF8.GetBytes("plaintext"));
            ArraySegment <byte> aad       = new ArraySegment <byte>(Encoding.UTF8.GetBytes("aad"));

            byte[] validCiphertext = encryptor.Encrypt(plaintext, aad);

            // Act & assert - 1
            // Ciphertext is too short to be a valid payload
            byte[] invalidCiphertext_tooShort = new byte[10];
            Assert.Throws <CryptographicException>(() =>
            {
                encryptor.Decrypt(new ArraySegment <byte>(invalidCiphertext_tooShort), aad);
            });

            // Act & assert - 2
            // Ciphertext has been manipulated
            byte[] invalidCiphertext_manipulated = (byte[])validCiphertext.Clone();
            invalidCiphertext_manipulated[0] ^= 0x01;
            Assert.Throws <CryptographicException>(() =>
            {
                encryptor.Decrypt(new ArraySegment <byte>(invalidCiphertext_manipulated), aad);
            });

            // Act & assert - 3
            // Ciphertext is too long
            byte[] invalidCiphertext_tooLong = validCiphertext.Concat(new byte[] { 0 }).ToArray();
            Assert.Throws <CryptographicException>(() =>
            {
                encryptor.Decrypt(new ArraySegment <byte>(invalidCiphertext_tooLong), aad);
            });

            // Act & assert - 4
            // AAD is incorrect
            Assert.Throws <CryptographicException>(() =>
            {
                encryptor.Decrypt(new ArraySegment <byte>(validCiphertext), new ArraySegment <byte>(Encoding.UTF8.GetBytes("different aad")));
            });
        }
        public void Encrypt_Decrypt_RoundTrips()
        {
            // Arrange
            ProtectedMemoryBlob           kdk       = new ProtectedMemoryBlob(new byte[512 / 8]);
            ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
                                                                                        symmetricAlgorithmFactory: Aes.Create,
                                                                                        symmetricAlgorithmKeySizeInBytes: 256 / 8,
                                                                                        validationAlgorithmFactory: () => new HMACSHA256());
            ArraySegment <byte> plaintext = new ArraySegment <byte>(Encoding.UTF8.GetBytes("plaintext"));
            ArraySegment <byte> aad       = new ArraySegment <byte>(Encoding.UTF8.GetBytes("aad"));

            // Act
            byte[] ciphertext     = encryptor.Encrypt(plaintext, aad);
            byte[] decipheredtext = encryptor.Decrypt(new ArraySegment <byte>(ciphertext), aad);

            // Assert
            Assert.Equal(plaintext, decipheredtext);
        }
        public IAuthenticatedEncryptorConfiguration CreateNewConfiguration()
        {
            // generate a 512-bit secret randomly
            const int KDK_SIZE_IN_BYTES = 512 / 8;

            byte[] kdk = ManagedGenRandomImpl.Instance.GenRandom(KDK_SIZE_IN_BYTES);
            ProtectedMemoryBlob secret;

            try
            {
                secret = new ProtectedMemoryBlob(kdk);
            }
            finally
            {
                Array.Clear(kdk, 0, kdk.Length);
            }

            return(new ManagedAuthenticatedEncryptorConfiguration(_options, secret));
        }
        // Creates a provider from the given secret.
        public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(ProtectedMemoryBlob kdk)
        {
            uint secretLengthInBytes = checked ((uint)kdk.Length);

            if (secretLengthInBytes == 0)
            {
                return(CreateEmptyProvider());
            }
            else
            {
                fixed(byte *pbPlaintextSecret = new byte[secretLengthInBytes])
                {
                    try
                    {
                        kdk.WriteSecretIntoBuffer(pbPlaintextSecret, checked ((int)secretLengthInBytes));
                        return(CreateProvider(pbPlaintextSecret, secretLengthInBytes));
                    }
                    finally
                    {
                        UnsafeBufferUtil.SecureZeroMemory(pbPlaintextSecret, secretLengthInBytes);
                    }
                }
            }
        }