diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SymmetricKeyWrap.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SymmetricKeyWrap.cs index 36a6ec8db24d2..02e54ea8ae004 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SymmetricKeyWrap.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SymmetricKeyWrap.cs @@ -31,27 +31,43 @@ internal static byte[] TripleDESKeyWrapEncrypt(byte[] rgbKey, byte[] rgbWrappedK } // generate a random IV - RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); byte[] rgbIV = new byte[8]; - rng.GetBytes(rgbIV); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(rgbIV); + } // rgbWKCS = rgbWrappedKeyData | (first 8 bytes of the hash) byte[] rgbWKCKS = new byte[rgbWrappedKeyData.Length + 8]; - TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider(); - // Don't add padding, use CBC mode: for example, a 192 bits key will yield 40 bytes of encrypted data - tripleDES.Padding = PaddingMode.None; - ICryptoTransform enc1 = tripleDES.CreateEncryptor(rgbKey, rgbIV); - Buffer.BlockCopy(rgbWrappedKeyData, 0, rgbWKCKS, 0, rgbWrappedKeyData.Length); - Buffer.BlockCopy(rgbCKS, 0, rgbWKCKS, rgbWrappedKeyData.Length, 8); - byte[] temp1 = enc1.TransformFinalBlock(rgbWKCKS, 0, rgbWKCKS.Length); - byte[] temp2 = new byte[rgbIV.Length + temp1.Length]; - Buffer.BlockCopy(rgbIV, 0, temp2, 0, rgbIV.Length); - Buffer.BlockCopy(temp1, 0, temp2, rgbIV.Length, temp1.Length); - // temp2 = REV (rgbIV | E_k(rgbWrappedKeyData | rgbCKS)) - Array.Reverse(temp2); - - ICryptoTransform enc2 = tripleDES.CreateEncryptor(rgbKey, s_rgbTripleDES_KW_IV); - return enc2.TransformFinalBlock(temp2, 0, temp2.Length); + TripleDES tripleDES = null; + ICryptoTransform enc1 = null; + ICryptoTransform enc2 = null; + + try + { + tripleDES = TripleDES.Create(); + // Don't add padding, use CBC mode: for example, a 192 bits key will yield 40 bytes of encrypted data + tripleDES.Padding = PaddingMode.None; + enc1 = tripleDES.CreateEncryptor(rgbKey, rgbIV); + enc2 = tripleDES.CreateEncryptor(rgbKey, s_rgbTripleDES_KW_IV); + + Buffer.BlockCopy(rgbWrappedKeyData, 0, rgbWKCKS, 0, rgbWrappedKeyData.Length); + Buffer.BlockCopy(rgbCKS, 0, rgbWKCKS, rgbWrappedKeyData.Length, 8); + byte[] temp1 = enc1.TransformFinalBlock(rgbWKCKS, 0, rgbWKCKS.Length); + byte[] temp2 = new byte[rgbIV.Length + temp1.Length]; + Buffer.BlockCopy(rgbIV, 0, temp2, 0, rgbIV.Length); + Buffer.BlockCopy(temp1, 0, temp2, rgbIV.Length, temp1.Length); + // temp2 = REV (rgbIV | E_k(rgbWrappedKeyData | rgbCKS)) + Array.Reverse(temp2); + + return enc2.TransformFinalBlock(temp2, 0, temp2.Length); + } + finally + { + enc2?.Dispose(); + enc1?.Dispose(); + tripleDES?.Dispose(); + } } [SuppressMessage("Microsoft.Cryptography", "CA5350", Justification = "Explicitly requested by the message contents")] @@ -62,31 +78,45 @@ internal static byte[] TripleDESKeyWrapDecrypt(byte[] rgbKey, byte[] rgbEncrypte && rgbEncryptedWrappedKeyData.Length != 48) throw new CryptographicException(SR.Cryptography_Xml_KW_BadKeySize); - TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider(); - // Assume no padding, use CBC mode - tripleDES.Padding = PaddingMode.None; - ICryptoTransform dec1 = tripleDES.CreateDecryptor(rgbKey, s_rgbTripleDES_KW_IV); - byte[] temp2 = dec1.TransformFinalBlock(rgbEncryptedWrappedKeyData, 0, rgbEncryptedWrappedKeyData.Length); - Array.Reverse(temp2); - // Get the IV and temp1 - byte[] rgbIV = new byte[8]; - Buffer.BlockCopy(temp2, 0, rgbIV, 0, 8); - byte[] temp1 = new byte[temp2.Length - rgbIV.Length]; - Buffer.BlockCopy(temp2, 8, temp1, 0, temp1.Length); + TripleDES tripleDES = null; + ICryptoTransform dec1 = null; + ICryptoTransform dec2 = null; - ICryptoTransform dec2 = tripleDES.CreateDecryptor(rgbKey, rgbIV); - byte[] rgbWKCKS = dec2.TransformFinalBlock(temp1, 0, temp1.Length); + try + { + tripleDES = TripleDES.Create(); + // Assume no padding, use CBC mode + tripleDES.Padding = PaddingMode.None; + dec1 = tripleDES.CreateDecryptor(rgbKey, s_rgbTripleDES_KW_IV); + + byte[] temp2 = dec1.TransformFinalBlock(rgbEncryptedWrappedKeyData, 0, rgbEncryptedWrappedKeyData.Length); + Array.Reverse(temp2); + // Get the IV and temp1 + byte[] rgbIV = new byte[8]; + Buffer.BlockCopy(temp2, 0, rgbIV, 0, 8); + byte[] temp1 = new byte[temp2.Length - rgbIV.Length]; + Buffer.BlockCopy(temp2, 8, temp1, 0, temp1.Length); + + dec2 = tripleDES.CreateDecryptor(rgbKey, rgbIV); + byte[] rgbWKCKS = dec2.TransformFinalBlock(temp1, 0, temp1.Length); - // checksum the key - byte[] rgbWrappedKeyData = new byte[rgbWKCKS.Length - 8]; - Buffer.BlockCopy(rgbWKCKS, 0, rgbWrappedKeyData, 0, rgbWrappedKeyData.Length); - using (var sha = SHA1.Create()) + // checksum the key + byte[] rgbWrappedKeyData = new byte[rgbWKCKS.Length - 8]; + Buffer.BlockCopy(rgbWKCKS, 0, rgbWrappedKeyData, 0, rgbWrappedKeyData.Length); + using (var sha = SHA1.Create()) + { + byte[] rgbCKS = sha.ComputeHash(rgbWrappedKeyData); + for (int index = rgbWrappedKeyData.Length, index1 = 0; index < rgbWKCKS.Length; index++, index1++) + if (rgbWKCKS[index] != rgbCKS[index1]) + throw new CryptographicException(SR.Cryptography_Xml_BadWrappedKeySize); + return rgbWrappedKeyData; + } + } + finally { - byte[] rgbCKS = sha.ComputeHash(rgbWrappedKeyData); - for (int index = rgbWrappedKeyData.Length, index1 = 0; index < rgbWKCKS.Length; index++, index1++) - if (rgbWKCKS[index] != rgbCKS[index1]) - throw new CryptographicException(SR.Cryptography_Xml_BadWrappedKeySize); - return rgbWrappedKeyData; + dec2?.Dispose(); + dec1?.Dispose(); + tripleDES?.Dispose(); } } @@ -98,48 +128,59 @@ internal static byte[] AESKeyWrapEncrypt(byte[] rgbKey, byte[] rgbWrappedKeyData if ((rgbWrappedKeyData.Length % 8 != 0) || N <= 0) throw new CryptographicException(SR.Cryptography_Xml_KW_BadKeySize); - RijndaelManaged rijn = new RijndaelManaged(); - rijn.Key = rgbKey; - // Use ECB mode, no padding - rijn.Mode = CipherMode.ECB; - rijn.Padding = PaddingMode.None; - ICryptoTransform enc = rijn.CreateEncryptor(); - // special case: only 1 block -- 8 bytes - if (N == 1) - { - // temp = 0xa6a6a6a6a6a6a6a6 | P(1) - byte[] temp = new byte[s_rgbAES_KW_IV.Length + rgbWrappedKeyData.Length]; - Buffer.BlockCopy(s_rgbAES_KW_IV, 0, temp, 0, s_rgbAES_KW_IV.Length); - Buffer.BlockCopy(rgbWrappedKeyData, 0, temp, s_rgbAES_KW_IV.Length, rgbWrappedKeyData.Length); - return enc.TransformFinalBlock(temp, 0, temp.Length); - } - // second case: more than 1 block - long t = 0; - byte[] rgbOutput = new byte[(N + 1) << 3]; - // initialize the R_i's - Buffer.BlockCopy(rgbWrappedKeyData, 0, rgbOutput, 8, rgbWrappedKeyData.Length); - byte[] rgbA = new byte[8]; - byte[] rgbBlock = new byte[16]; - Buffer.BlockCopy(s_rgbAES_KW_IV, 0, rgbA, 0, 8); - for (int j = 0; j <= 5; j++) + Aes aes = null; + ICryptoTransform enc = null; + + try { - for (int i = 1; i <= N; i++) + aes = Aes.Create(); + aes.Key = rgbKey; + // Use ECB mode, no padding + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.None; + enc = aes.CreateEncryptor(); + // special case: only 1 block -- 8 bytes + if (N == 1) + { + // temp = 0xa6a6a6a6a6a6a6a6 | P(1) + byte[] temp = new byte[s_rgbAES_KW_IV.Length + rgbWrappedKeyData.Length]; + Buffer.BlockCopy(s_rgbAES_KW_IV, 0, temp, 0, s_rgbAES_KW_IV.Length); + Buffer.BlockCopy(rgbWrappedKeyData, 0, temp, s_rgbAES_KW_IV.Length, rgbWrappedKeyData.Length); + return enc.TransformFinalBlock(temp, 0, temp.Length); + } + // second case: more than 1 block + long t = 0; + byte[] rgbOutput = new byte[(N + 1) << 3]; + // initialize the R_i's + Buffer.BlockCopy(rgbWrappedKeyData, 0, rgbOutput, 8, rgbWrappedKeyData.Length); + byte[] rgbA = new byte[8]; + byte[] rgbBlock = new byte[16]; + Buffer.BlockCopy(s_rgbAES_KW_IV, 0, rgbA, 0, 8); + for (int j = 0; j <= 5; j++) { - t = i + j * N; - Buffer.BlockCopy(rgbA, 0, rgbBlock, 0, 8); - Buffer.BlockCopy(rgbOutput, 8 * i, rgbBlock, 8, 8); - byte[] rgbB = enc.TransformFinalBlock(rgbBlock, 0, 16); - for (int k = 0; k < 8; k++) + for (int i = 1; i <= N; i++) { - byte tmp = (byte)((t >> (8 * (7 - k))) & 0xFF); - rgbA[k] = (byte)(tmp ^ rgbB[k]); + t = i + j * N; + Buffer.BlockCopy(rgbA, 0, rgbBlock, 0, 8); + Buffer.BlockCopy(rgbOutput, 8 * i, rgbBlock, 8, 8); + byte[] rgbB = enc.TransformFinalBlock(rgbBlock, 0, 16); + for (int k = 0; k < 8; k++) + { + byte tmp = (byte)((t >> (8 * (7 - k))) & 0xFF); + rgbA[k] = (byte)(tmp ^ rgbB[k]); + } + Buffer.BlockCopy(rgbB, 8, rgbOutput, 8 * i, 8); } - Buffer.BlockCopy(rgbB, 8, rgbOutput, 8 * i, 8); } + // Set the first block of rgbOutput to rgbA + Buffer.BlockCopy(rgbA, 0, rgbOutput, 0, 8); + return rgbOutput; + } + finally + { + enc?.Dispose(); + aes?.Dispose(); } - // Set the first block of rgbOutput to rgbA - Buffer.BlockCopy(rgbA, 0, rgbOutput, 0, 8); - return rgbOutput; } internal static byte[] AESKeyWrapDecrypt(byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData) @@ -150,53 +191,65 @@ internal static byte[] AESKeyWrapDecrypt(byte[] rgbKey, byte[] rgbEncryptedWrapp throw new CryptographicException(SR.Cryptography_Xml_KW_BadKeySize); byte[] rgbOutput = new byte[N << 3]; - RijndaelManaged rijn = new RijndaelManaged(); - rijn.Key = rgbKey; - // Use ECB mode, no padding - rijn.Mode = CipherMode.ECB; - rijn.Padding = PaddingMode.None; - ICryptoTransform dec = rijn.CreateDecryptor(); - // special case: only 1 block -- 8 bytes - if (N == 1) + Aes aes = null; + ICryptoTransform dec = null; + + try { - byte[] temp = dec.TransformFinalBlock(rgbEncryptedWrappedKeyData, 0, rgbEncryptedWrappedKeyData.Length); + aes = Aes.Create(); + aes.Key = rgbKey; + // Use ECB mode, no padding + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.None; + dec = aes.CreateDecryptor(); + + // special case: only 1 block -- 8 bytes + if (N == 1) + { + byte[] temp = dec.TransformFinalBlock(rgbEncryptedWrappedKeyData, 0, rgbEncryptedWrappedKeyData.Length); + // checksum the key + for (int index = 0; index < 8; index++) + if (temp[index] != s_rgbAES_KW_IV[index]) + throw new CryptographicException(SR.Cryptography_Xml_BadWrappedKeySize); + // rgbOutput is LSB(temp) + Buffer.BlockCopy(temp, 8, rgbOutput, 0, 8); + return rgbOutput; + } + // second case: more than 1 block + long t = 0; + // initialize the C_i's + Buffer.BlockCopy(rgbEncryptedWrappedKeyData, 8, rgbOutput, 0, rgbOutput.Length); + byte[] rgbA = new byte[8]; + byte[] rgbBlock = new byte[16]; + Buffer.BlockCopy(rgbEncryptedWrappedKeyData, 0, rgbA, 0, 8); + for (int j = 5; j >= 0; j--) + { + for (int i = N; i >= 1; i--) + { + t = i + j * N; + for (int k = 0; k < 8; k++) + { + byte tmp = (byte)((t >> (8 * (7 - k))) & 0xFF); + rgbA[k] ^= tmp; + } + Buffer.BlockCopy(rgbA, 0, rgbBlock, 0, 8); + Buffer.BlockCopy(rgbOutput, 8 * (i - 1), rgbBlock, 8, 8); + byte[] rgbB = dec.TransformFinalBlock(rgbBlock, 0, 16); + Buffer.BlockCopy(rgbB, 8, rgbOutput, 8 * (i - 1), 8); + Buffer.BlockCopy(rgbB, 0, rgbA, 0, 8); + } + } // checksum the key for (int index = 0; index < 8; index++) - if (temp[index] != s_rgbAES_KW_IV[index]) + if (rgbA[index] != s_rgbAES_KW_IV[index]) throw new CryptographicException(SR.Cryptography_Xml_BadWrappedKeySize); - // rgbOutput is LSB(temp) - Buffer.BlockCopy(temp, 8, rgbOutput, 0, 8); return rgbOutput; } - // second case: more than 1 block - long t = 0; - // initialize the C_i's - Buffer.BlockCopy(rgbEncryptedWrappedKeyData, 8, rgbOutput, 0, rgbOutput.Length); - byte[] rgbA = new byte[8]; - byte[] rgbBlock = new byte[16]; - Buffer.BlockCopy(rgbEncryptedWrappedKeyData, 0, rgbA, 0, 8); - for (int j = 5; j >= 0; j--) + finally { - for (int i = N; i >= 1; i--) - { - t = i + j * N; - for (int k = 0; k < 8; k++) - { - byte tmp = (byte)((t >> (8 * (7 - k))) & 0xFF); - rgbA[k] ^= tmp; - } - Buffer.BlockCopy(rgbA, 0, rgbBlock, 0, 8); - Buffer.BlockCopy(rgbOutput, 8 * (i - 1), rgbBlock, 8, 8); - byte[] rgbB = dec.TransformFinalBlock(rgbBlock, 0, 16); - Buffer.BlockCopy(rgbB, 8, rgbOutput, 8 * (i - 1), 8); - Buffer.BlockCopy(rgbB, 0, rgbA, 0, 8); - } + dec?.Dispose(); + aes?.Dispose(); } - // checksum the key - for (int index = 0; index < 8; index++) - if (rgbA[index] != s_rgbAES_KW_IV[index]) - throw new CryptographicException(SR.Cryptography_Xml_BadWrappedKeySize); - return rgbOutput; } } } diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs index 6e901bef91015..a0010c58f8aa6 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs @@ -330,6 +330,123 @@ public void DecryptEncryptedKey_Null() Assert.Throws(() => ex.DecryptEncryptedKey(null)); } + [Fact] + public void EncryptKey_TripleDES() + { + using (TripleDES tripleDES = TripleDES.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123456781234567812345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, tripleDES); + + Assert.NotNull(encryptedKey); + Assert.Equal(key, EncryptedXml.DecryptKey(encryptedKey, tripleDES)); + } + } + + [Fact] + public void EncryptKey_AES() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123456781234567812345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, aes); + + Assert.NotNull(encryptedKey); + Assert.Equal(key, EncryptedXml.DecryptKey(encryptedKey, aes)); + } + } + + [Fact] + public void EncryptKey_AES8Bytes() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("12345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, aes); + + Assert.NotNull(encryptedKey); + Assert.Equal(key, EncryptedXml.DecryptKey(encryptedKey, aes)); + } + } + + [Fact] + public void EncryptKey_AESNotDivisibleBy8() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("1234567"); + + Assert.Throws(() => EncryptedXml.EncryptKey(key, aes)); + } + } + + [Fact] + public void DecryptKey_TripleDESWrongKeySize() + { + using (TripleDES tripleDES = TripleDES.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123"); + + Assert.Throws(() => EncryptedXml.DecryptKey(key, tripleDES)); + } + } + + [Fact] + public void DecryptKey_TripleDESCorruptedKey() + { + using (TripleDES tripleDES = TripleDES.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123456781234567812345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, tripleDES); + encryptedKey[0] ^= 0xFF; + + Assert.Throws(() => EncryptedXml.DecryptKey(encryptedKey, tripleDES)); + } + } + + [Fact] + public void DecryptKey_AESWrongKeySize() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123"); + + Assert.Throws(() => EncryptedXml.DecryptKey(key, aes)); + } + } + + [Fact] + public void DecryptKey_AESCorruptedKey() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("123456781234567812345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, aes); + encryptedKey[0] ^= 0xFF; + + Assert.Throws(() => EncryptedXml.DecryptKey(encryptedKey, aes)); + } + } + + [Fact] + public void DecryptKey_AESCorruptedKey8Bytes() + { + using (Aes aes = Aes.Create()) + { + byte[] key = Encoding.ASCII.GetBytes("12345678"); + + byte[] encryptedKey = EncryptedXml.EncryptKey(key, aes); + encryptedKey[0] ^= 0xFF; + + Assert.Throws(() => EncryptedXml.DecryptKey(encryptedKey, aes)); + } + } + private Stream LoadResourceStream(string resourceName) { return Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);