Use invalid password hresult in managed p12 reader
authorKevin Jones <kevin@vcsjones.com>
Wed, 11 Dec 2019 14:42:32 +0000 (09:42 -0500)
committerKevin Jones <kevin@vcsjones.com>
Wed, 11 Dec 2019 14:42:32 +0000 (09:42 -0500)
The managed PKCS12 reader was not setting the HResult on exceptions
when the MAC of the PKCS12 failed (typically the result of a bad password).
This made the CryptographicException indistinguishable from other
PKCS12 CryptographicExceptions without relying on the exception message.

Some other applications used the HResult to determine if the PKCS12
could not be read due to an invalid password.

src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_Collection.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/PfxFormatTests_SingleCert.cs

index 12a6826..1ef51eb 100644 (file)
@@ -19,6 +19,7 @@ namespace Internal.Cryptography.Pal
     internal abstract class UnixPkcs12Reader : IDisposable
     {
         private const string DecryptedSentinel = nameof(UnixPkcs12Reader);
+        private const int ErrorInvalidPasswordHResult = unchecked((int)0x80070056);
 
         private PfxAsn _pfxAsn;
         private ContentInfoAsn[] _safeContentsValues;
@@ -190,7 +191,10 @@ namespace Internal.Cryptography.Pal
             }
             catch (Exception e)
             {
-                throw new CryptographicException(SR.Cryptography_Pfx_BadPassword, e);
+                throw new CryptographicException(SR.Cryptography_Pfx_BadPassword, e)
+                {
+                    HResult = ErrorInvalidPasswordHResult
+                };
             }
             finally
             {
@@ -225,7 +229,10 @@ namespace Internal.Cryptography.Pal
                 return;
             }
 
-            throw new CryptographicException(SR.Cryptography_Pfx_BadPassword);
+            throw new CryptographicException(SR.Cryptography_Pfx_BadPassword)
+            {
+                HResult = ErrorInvalidPasswordHResult
+            };
         }
 
         private void Decrypt(ReadOnlySpan<char> password, ReadOnlyMemory<byte> authSafeContents)
index 246fb63..6120831 100644 (file)
@@ -15,6 +15,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
         // Use a MAC count of 1 because we're not persisting things, and the password
         // is with the test... just save some CPU cycles.
         private const int MacCount = 1;
+        protected const int ErrorInvalidPasswordHResult = unchecked((int)0x80070056);
 
         // Use SHA-1 for Windows 7-8.1 support.
         private static readonly HashAlgorithmName s_digestAlgorithm = HashAlgorithmName.SHA1;
@@ -764,7 +765,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
                 cert2.Attributes.Add(id2);
                 key1.Attributes.Add(id3);
                 key2.Attributes.Add(id4);
-                
+
                 AddContents(keyContents, builder, pw, encrypt: false);
                 AddContents(certContents, builder, pw, encrypt: true);
                 builder.SealWithMac(pw, s_digestAlgorithm, MacCount);
@@ -815,7 +816,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
                     -2146893819);
             }
         }
-        
+
         [Theory]
         [InlineData(false, false)]
         [InlineData(false, true)]
index 8cf7f45..b4f205c 100644 (file)
@@ -84,6 +84,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
                 () => coll.Import(pfxBytes, wrongPassword, s_importFlags));
 
             AssertMessageContains("password", ex);
+            Assert.Equal(ErrorInvalidPasswordHResult, ex.HResult);
         }
 
         protected override void ReadUnreadablePfx(
index 17e60e7..0466235 100644 (file)
@@ -58,6 +58,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
                 () => new X509Certificate2(pfxBytes, wrongPassword, s_importFlags));
 
             AssertMessageContains("password", ex);
+            Assert.Equal(ErrorInvalidPasswordHResult, ex.HResult);
         }
 
         protected override void ReadUnreadablePfx(