/// <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); }
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); }
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); } } } }