Wrap ArgumentException with CryptographicException for incorrect key in EnvelopedCms
authorKevin Jones <kevin@vcsjones.com>
Fri, 26 Mar 2021 21:15:58 +0000 (17:15 -0400)
committerGitHub <noreply@github.com>
Fri, 26 Mar 2021 21:15:58 +0000 (14:15 -0700)
If an incorrect asymmetric key is used to decrypt a symmetric key
in enveloped data, and the decryption is successful but produces
an invalid symmetric key, wrap that as a CryptographicException.

src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decrypt.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx
src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTestsUsingCertWithPrivateKey.cs

index bfb2ef3..3c8bc80 100644 (file)
@@ -170,16 +170,31 @@ namespace Internal.Cryptography.Pal.AnyOS
                     encryptedContent.CopyTo(encryptedContentArray);
 
                     using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
-                    using (ICryptoTransform decryptor = alg.CreateDecryptor(cek, alg.IV))
                     {
-                        // If we extend this library to accept additional algorithm providers
-                        // then a different array pool needs to be used.
-                        Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly);
-
-                        return decryptor.OneShot(
-                            encryptedContentArray,
-                            0,
-                            encryptedContentLength);
+                        ICryptoTransform decryptor;
+
+                        try
+                        {
+                            decryptor = alg.CreateDecryptor(cek, alg.IV);
+                        }
+                        catch (ArgumentException ae)
+                        {
+                            // Decrypting or deriving the symmetric key with the wrong key may still succeed
+                            // but produce a symmetric key that is not the correct length.
+                            throw new CryptographicException(SR.Cryptography_Cms_InvalidSymmetricKey, ae);
+                        }
+
+                        using (decryptor)
+                        {
+                            // If we extend this library to accept additional algorithm providers
+                            // then a different array pool needs to be used.
+                            Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly);
+
+                            return decryptor.OneShot(
+                                encryptedContentArray,
+                                0,
+                                encryptedContentLength);
+                        }
                     }
                 }
                 catch (CryptographicException e)
index 6fae1db..8086b46 100644 (file)
   <data name="Cryptography_Cms_CertificateAlreadyInCollection" xml:space="preserve">
     <value>Certificate already present in the collection.</value>
   </data>
+  <data name="Cryptography_Cms_InvalidSymmetricKey" xml:space="preserve">
+    <value>The key in the enveloped message is not valid or could not be decoded.</value>
+  </data>
 </root>
index 425ead3..3c84d6e 100644 (file)
@@ -68,6 +68,60 @@ namespace System.Security.Cryptography.Pkcs.EnvelopedCmsTests.Tests
         }
 
         [Fact]
+        public static void DecryptSuccesfullyWithWrongKeyProducesInvalidSymmetricKey()
+        {
+            using (X509Certificate2 wrongRecipient = Certificates.RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4.TryGetCertificateWithPrivateKey())
+            {
+                // This is an enveloped CMS that is encrypted with one RSA key recipient
+                // but does not fail when decrypting the content encryption key (CEK).
+                // Though it did not fail, the CEK is wrong and cannot decrypt the data
+                // for the recipient. It might decrypt the CEK to a key that is an invalid
+                // for the symmetric algorithm, like a 120-bit key for AES. For that case,
+                // the symmetric decryption would throw an ArgumentException, not a
+                // CryptographicException. This tests that circumstance where we need to
+                // wrap the ArgumentException with a CryptographicException.
+                //
+                // This content can be re-created with trial-and-error with the managed PAL.
+                //
+                // Two certificates with the same SKI are required.
+                // using X509Certificate2 cert1 = ...
+                // using X509Certificate2 cert2 = ...
+                // while (true) {
+                //   EnvelopedCms ecms = new EnvelopedCms(..);
+                //   CmsRecipient recipient = new CmsRecipient(SubjectIdentifierType.SubjectKeyIdentifier, cert1);
+                //   ecms.Encrypt(recipient);
+                //   byte[] encoded = ecms.Encode();
+                //   ecms = new EnvelopedCms();
+                //   ecms.Decode(encoded);
+                //   try {
+                //     ecms.Decrypt(new X509Certificate2Collection(cert2));
+                //   }
+                //   catch (CryptographicException e) when e.Message == SR.Cryptography_Cms_InvalidSymmetricKey;
+                //     // If we get here, we've produced an EnvelopedCms with the needed criteria.
+                //     break;
+                //   }
+                // }
+                string encryptedContent =
+"3082018806092A864886F70D010703A082017930820175020102318201303082" +
+"012C0201028014B46B61938FF9864BD8494B3937DA19C9F06FA8D3300D06092A" +
+"864886F70D0101010500048201008198CBAFF1C67EE634C7A32C356729F996BC" +
+"E4125EE353B220A792CCFD37855B50E05916CC8EC6E25EF62B35B29620C8BF76" +
+"144032854D14E3E19B613C15A26E376A2014AD3AD492F80A92F0D61910B6C416" +
+"867985279CF4E26CDED351AFB84CE9E1BC105899280DB6B782688CE6B04B7003" +
+"E4C53B580DD2F21A71B973C2AB70E61F1AFBDD2616FE0101BB02BCDA14881CEC" +
+"037032C91FE803C76D91FA5E0A802ECB2FB2BBE71C8567F58B5B74638CD9765E" +
+"F658172AAD423963784C5BE49AC01682751796F0AFE4943373981FC074F24640" +
+"901201AD6884415788FC18721ECB201A60A7FE5859FF61DA8BB21D0D23593D28" +
+"86896886D3507906DB58FB056953303C06092A864886F70D010701301D060960" +
+"864801650304012A0410C85C553A73E9B55F98752E1133ACA645801099B22DFF" +
+"6A9D8984F8B3F63079CE9265";
+                EnvelopedCms ecms = new EnvelopedCms();
+                ecms.Decode(encryptedContent.HexToByteArray());
+                Assert.ThrowsAny<CryptographicException>(() => ecms.Decrypt(new X509Certificate2Collection(wrongRecipient)));
+            }
+        }
+
+        [Fact]
         public static void DecryptUsingCertificateWithSameSubjectKeyIdentifierButDifferentKeyPair()
         {
             using (X509Certificate2 recipientCert = Certificates.RSAKeyTransfer4_ExplicitSki.GetCertificate())