Have consistent Dispose behaviors for AsymmetricAlgorithm objects
authorJeremy Barton <jbarton@microsoft.com>
Tue, 18 Jun 2019 01:25:56 +0000 (18:25 -0700)
committerGitHub <noreply@github.com>
Tue, 18 Jun 2019 01:25:56 +0000 (18:25 -0700)
The AsymmetricAlgorithm types weren't consistent in how they handled Dispose. There were four predominant behaviors on use-after-Dispose

* NullReferenceException (mainly from the OpenSsl implementations)
* Generate a new key, like the object was fresh (the public Cng implementations)
* Not notice that anything was Disposed, keep on keepin' on (the private Cng implementations in Algorithms)
* Indirect ObjectDisposedExceptions 95% of the time (the Windows CSP types)

This change makes all of the types consistently go into (and stay in) a Disposed state, throwing ObjectDisposedExceptions when asked to do work in that state.

Commit migrated from https://github.com/dotnet/corefx/commit/fb81332b9643ecf8c0bb34d7938da97aa5e13e03

51 files changed:
src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs
src/libraries/Common/src/System/Security/Cryptography/DSAOpenSsl.cs
src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs
src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs
src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs
src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs
src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs
src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs
src/libraries/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs
src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs
src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs
src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs
src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/EccTestData.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs
src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs
src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSACng.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECCngKey.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.Key.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.Key.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSACng.cs
src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs
src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/DSACng.cs
src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCng.cs
src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs
src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDsaCng.cs
src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/RSACng.cs
src/libraries/System.Security.Cryptography.Cng/tests/Configurations.props
src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Unix.cs
src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Windows.cs
src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Unix.cs
src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs
src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/DSAOpenSsl.cs
src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/RSAOpenSsl.cs
src/libraries/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslTests.cs
src/libraries/System.Security.Cryptography.OpenSsl/tests/RsaOpenSslTests.cs

index 8bed7de..748302f 100644 (file)
@@ -59,6 +59,24 @@ namespace System.Security.Cryptography
                 ImportKeyBlob(blob, hasPrivateKey);
             }
 
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
             public override byte[] ExportEncryptedPkcs8PrivateKey(
                 ReadOnlySpan<byte> passwordBytes,
                 PbeParameters pbeParameters)
index f4be1e7..28bc574 100644 (file)
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Buffers;
 using System.Diagnostics;
 using System.IO;
 using Internal.Cryptography;
@@ -35,7 +34,7 @@ namespace System.Security.Cryptography
             public DSAOpenSsl(int keySize)
             {
                 LegalKeySizesValue = s_legalKeySizes;
-                KeySize = keySize;
+                base.KeySize = keySize;
                 _key = new Lazy<SafeDsaHandle>(GenerateKey);
             }
 
@@ -51,6 +50,7 @@ namespace System.Security.Cryptography
                     // Set the KeySize before FreeKey so that an invalid value doesn't throw away the key
                     base.KeySize = value;
 
+                    ThrowIfDisposed();
                     FreeKey();
                     _key = new Lazy<SafeDsaHandle>(GenerateKey);
                 }
@@ -76,9 +76,7 @@ namespace System.Security.Cryptography
             public override DSAParameters ExportParameters(bool includePrivateParameters)
             {
                 // It's entirely possible that this line will cause the key to be generated in the first place.
-                SafeDsaHandle key = _key.Value;
-
-                CheckInvalidKey(key);
+                SafeDsaHandle key = GetKey();
 
                 DSAParameters dsaParameters = Interop.Crypto.ExportDsaParameters(key, includePrivateParameters);
                 bool hasPrivateKey = dsaParameters.X != null;
@@ -108,6 +106,8 @@ namespace System.Security.Cryptography
                 if (hasPrivateKey && parameters.X.Length != parameters.Q.Length)
                     throw new ArgumentException(SR.Cryptography_InvalidDsaParameters_MismatchedQX);
 
+                ThrowIfDisposed();
+
                 SafeDsaHandle key;
                 if (!Interop.Crypto.DsaKeyCreateByExplicitParameters(
                     out key,
@@ -123,11 +123,30 @@ namespace System.Security.Cryptography
                 SetKey(key);
             }
 
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
                     FreeKey();
+                    _key = null;
                 }
 
                 base.Dispose(disposing);
@@ -188,7 +207,7 @@ namespace System.Security.Cryptography
                 if (rgbHash == null)
                     throw new ArgumentNullException(nameof(rgbHash));
 
-                SafeDsaHandle key = _key.Value;
+                SafeDsaHandle key = GetKey();
                 int signatureSize = Interop.Crypto.DsaEncodedSignatureSize(key);
                 byte[] signature = CryptoPool.Rent(signatureSize);
                 try
@@ -218,7 +237,7 @@ namespace System.Security.Cryptography
             public override bool TryCreateSignature(ReadOnlySpan<byte> hash, Span<byte> destination, out int bytesWritten)
             {
                 byte[] converted;
-                SafeDsaHandle key = _key.Value;
+                SafeDsaHandle key = GetKey();
                 int signatureSize = Interop.Crypto.DsaEncodedSignatureSize(key);
                 byte[] signature = CryptoPool.Rent(signatureSize);
                 try
@@ -269,7 +288,7 @@ namespace System.Security.Cryptography
 
             public override bool VerifySignature(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
             {
-                SafeDsaHandle key = _key.Value;
+                SafeDsaHandle key = GetKey();
 
                 int expectedSignatureBytes = Interop.Crypto.DsaSignatureFieldSize(key) * 2;
                 if (signature.Length != expectedSignatureBytes)
@@ -283,8 +302,34 @@ namespace System.Security.Cryptography
                 return Interop.Crypto.DsaVerify(key, hash, openSslFormat);
             }
 
+            private void ThrowIfDisposed()
+            {
+                if (_key == null)
+                {
+                    throw new ObjectDisposedException(
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+                        nameof(DSA)
+#else
+                        nameof(DSAOpenSsl)
+#endif
+                    );
+                }
+            }
+
+            private SafeDsaHandle GetKey()
+            {
+                ThrowIfDisposed();
+
+                SafeDsaHandle key = _key.Value;
+                CheckInvalidKey(key);
+
+                return key;
+            }
+
             private void SetKey(SafeDsaHandle newKey)
             {
+                // Do not call ThrowIfDisposed here, as it breaks the SafeEvpPKey ctor
+
                 // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere
                 // with the already loaded key.
                 ForceSetKeySize(BitsPerByte * Interop.Crypto.DsaKeySize(newKey));
index a5d796c..12a4207 100644 (file)
@@ -5,7 +5,6 @@
 using System.Buffers;
 using System.Diagnostics;
 using System.IO;
-using System.Numerics;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.Apple;
 using System.Security.Cryptography.Asn1;
@@ -28,6 +27,7 @@ namespace System.Security.Cryptography
             public sealed partial class DSASecurityTransforms : DSA
             {
                 private SecKeyPair _keys;
+                private bool _disposed;
 
                 public DSASecurityTransforms()
                     : this(1024)
@@ -36,7 +36,7 @@ namespace System.Security.Cryptography
 
                 public DSASecurityTransforms(int keySize)
                 {
-                    KeySize = keySize;
+                    base.KeySize = keySize;
                 }
 
                 internal DSASecurityTransforms(SafeSecKeyRefHandle publicKey)
@@ -71,6 +71,8 @@ namespace System.Security.Cryptography
                         // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
                         base.KeySize = value;
 
+                        ThrowIfDisposed();
+
                         if (_keys != null)
                         {
                             _keys.Dispose();
@@ -86,8 +88,7 @@ namespace System.Security.Cryptography
                     const string ExportPassword = "DotnetExportPassphrase";
                     SecKeyPair keys = GetKeys();
 
-                    if (keys.PublicKey == null ||
-                        (includePrivateParameters && keys.PrivateKey == null))
+                    if (includePrivateParameters && keys.PrivateKey == null)
                     { 
                         throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
                     }
@@ -152,6 +153,8 @@ namespace System.Security.Cryptography
                     if (parameters.Q.Length != 20)
                         throw new CryptographicException(SR.Cryptography_InvalidDsaParameters_QRestriction_ShortKey);
 
+                    ThrowIfDisposed();
+
                     if (hasPrivateKey)
                     {
                         SafeSecKeyRefHandle privateKey = ImportKey(parameters);
@@ -179,6 +182,24 @@ namespace System.Security.Cryptography
                     }
                 }
 
+                public override void ImportEncryptedPkcs8PrivateKey(
+                    ReadOnlySpan<byte> passwordBytes,
+                    ReadOnlySpan<byte> source,
+                    out int bytesRead)
+                {
+                    ThrowIfDisposed();
+                    base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+                }
+
+                public override void ImportEncryptedPkcs8PrivateKey(
+                    ReadOnlySpan<char> password,
+                    ReadOnlySpan<byte> source,
+                    out int bytesRead)
+                {
+                    ThrowIfDisposed();
+                    base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+                }
+
                 private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters)
                 {
                     if (parameters.X != null)
@@ -218,6 +239,8 @@ namespace System.Security.Cryptography
                     ReadOnlySpan<byte> source,
                     out int bytesRead)
                 {
+                    ThrowIfDisposed();
+
                     fixed (byte* ptr = &MemoryMarshal.GetReference(source))
                     {
                         using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
@@ -306,13 +329,30 @@ namespace System.Security.Cryptography
                             _keys.Dispose();
                             _keys = null;
                         }
+
+                        _disposed = true;
                     }
 
                     base.Dispose(disposing);
                 }
 
+                private void ThrowIfDisposed()
+                {
+                    // The other SecurityTransforms types use _keys.PublicKey == null,
+                    // but since Apple doesn't provide DSA key generation we can't easily tell
+                    // if a failed attempt to generate a key happened, or we're in a pristine state.
+                    //
+                    // So this type uses an explicit field, rather than inferred state.
+                    if (_disposed)
+                    {
+                        throw new ObjectDisposedException(nameof(DSA));
+                    }
+                }
+
                 internal SecKeyPair GetKeys()
                 {
+                    ThrowIfDisposed();
+
                     SecKeyPair current = _keys;
 
                     if (current != null)
@@ -330,6 +370,8 @@ namespace System.Security.Cryptography
 
                 private void SetKey(SecKeyPair newKeyPair)
                 {
+                    ThrowIfDisposed();
+
                     SecKeyPair current = _keys;
                     _keys = newKeyPair;
                     current?.Dispose();
index 92e2827..2d46606 100644 (file)
@@ -15,6 +15,8 @@ namespace System.Security.Cryptography
             public override void ImportParameters(ECParameters parameters)
             {
                 parameters.Validate();
+                ThrowIfDisposed();
+
                 ECCurve curve = parameters.Curve;
                 bool includePrivateParamerters = (parameters.D != null);
 
@@ -92,6 +94,7 @@ namespace System.Security.Cryptography
 
             public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)
             {
+                ThrowIfDisposed();
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportPkcs8PrivateKey(source, out int localRead);
 
                 ProcessPkcs8Response(response);
@@ -103,6 +106,7 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     passwordBytes,
                     source,
@@ -117,6 +121,7 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     password,
                     source,
index 8de7899..30a81b6 100644 (file)
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Buffers;
 using System.Diagnostics;
 using Microsoft.Win32.SafeHandles;
 
@@ -31,6 +30,8 @@ namespace System.Security.Cryptography
                 if (string.IsNullOrEmpty(hashAlgorithm.Name))
                     throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyFromHash(
                     otherPartyPublicKey,
                     hashAlgorithm,
@@ -51,6 +52,8 @@ namespace System.Security.Cryptography
                 if (string.IsNullOrEmpty(hashAlgorithm.Name))
                     throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyFromHmac(
                     otherPartyPublicKey,
                     hashAlgorithm,
@@ -69,6 +72,8 @@ namespace System.Security.Cryptography
                 if (prfSeed == null)
                     throw new ArgumentNullException(nameof(prfSeed));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyTls(
                     otherPartyPublicKey,
                     prfLabel,
index bf007e6..3be1be4 100644 (file)
@@ -2,6 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using Microsoft.Win32.SafeHandles;
+
 namespace System.Security.Cryptography
 {
 #if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
@@ -39,7 +41,8 @@ namespace System.Security.Cryptography
             {
                 if (disposing)
                 {
-                    _key.Dispose();
+                    _key?.Dispose();
+                    _key = null;
                 }
 
                 base.Dispose(disposing);
@@ -60,29 +63,77 @@ namespace System.Security.Cryptography
 
                     // Set the KeySize before FreeKey so that an invalid value doesn't throw away the key
                     base.KeySize = value;
-                    _key?.Dispose();
+
+                    ThrowIfDisposed();
+                    _key.Dispose();
                     _key = new ECOpenSsl(this);
                 }
             }
 
             public override void GenerateKey(ECCurve curve)
             {
+                ThrowIfDisposed();
                 KeySizeValue = _key.GenerateKey(curve);
             }
 
-            public override ECDiffieHellmanPublicKey PublicKey =>
-                new ECDiffieHellmanOpenSslPublicKey(_key.UpRefKeyHandle());
+            public override ECDiffieHellmanPublicKey PublicKey
+            {
+                get
+                {
+                    ThrowIfDisposed();
+                    return new ECDiffieHellmanOpenSslPublicKey(_key.UpRefKeyHandle());
+                }
+            }
 
             public override void ImportParameters(ECParameters parameters)
             {
+                ThrowIfDisposed();
                 KeySizeValue = _key.ImportParameters(parameters);
             }
 
             public override ECParameters ExportExplicitParameters(bool includePrivateParameters) =>
-                ECOpenSsl.ExportExplicitParameters(_key.Value, includePrivateParameters);
+                ECOpenSsl.ExportExplicitParameters(GetKey(), includePrivateParameters);
 
             public override ECParameters ExportParameters(bool includePrivateParameters) =>
-                ECOpenSsl.ExportParameters(_key.Value, includePrivateParameters);
+                ECOpenSsl.ExportParameters(GetKey(), includePrivateParameters);
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
+            private void ThrowIfDisposed()
+            {
+                if (_key == null)
+                {
+                    throw new ObjectDisposedException(
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+                        nameof(ECDiffieHellman)
+#else
+                        nameof(ECDiffieHellmanOpenSsl)
+#endif
+                    );
+                }
+            }
+
+            private SafeEcKeyHandle GetKey()
+            {
+                ThrowIfDisposed();
+                return _key.Value;
+            }
         }
 #if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
     }
index 5ecc47f..3bde2dd 100644 (file)
@@ -12,7 +12,7 @@ namespace System.Security.Cryptography
 #endif
         internal sealed class ECDiffieHellmanOpenSslPublicKey : ECDiffieHellmanPublicKey
         {
-            private readonly ECOpenSsl _key;
+            private ECOpenSsl _key;
 
             internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle)
             {
@@ -49,20 +49,28 @@ namespace System.Security.Cryptography
             }
 
             public override ECParameters ExportExplicitParameters() =>
-                ECOpenSsl.ExportExplicitParameters(_key.Value, includePrivateParameters: false);
+                ECOpenSsl.ExportExplicitParameters(GetKey(), includePrivateParameters: false);
 
             public override ECParameters ExportParameters() =>
-                ECOpenSsl.ExportParameters(_key.Value, includePrivateParameters: false);
+                ECOpenSsl.ExportParameters(GetKey(), includePrivateParameters: false);
 
-            internal bool HasCurveName => Interop.Crypto.EcKeyHasCurveName(_key.Value);
+            internal bool HasCurveName => Interop.Crypto.EcKeyHasCurveName(GetKey());
 
-            internal int KeySize => _key.KeySize;
+            internal int KeySize
+            {
+                get
+                {
+                    ThrowIfDisposed();
+                    return _key.KeySize;
+                }
+            }
 
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
                     _key?.Dispose();
+                    _key = null;
                 }
 
                 base.Dispose(disposing);
@@ -70,7 +78,7 @@ namespace System.Security.Cryptography
 
             internal SafeEvpPKeyHandle DuplicateKeyHandle()
             {
-                SafeEcKeyHandle currentKey = _key.Value;
+                SafeEcKeyHandle currentKey = GetKey();
                 SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate();
 
                 try
@@ -91,6 +99,26 @@ namespace System.Security.Cryptography
                     throw;
                 }
             }
+
+            private void ThrowIfDisposed()
+            {
+                if (_key == null)
+                {
+                    throw new ObjectDisposedException(
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+                        nameof(ECDiffieHellmanPublicKey)
+#else
+                        nameof(ECDiffieHellmanOpenSslPublicKey)
+#endif
+                    );
+                }
+            }
+
+            private SafeEcKeyHandle GetKey()
+            {
+                ThrowIfDisposed();
+                return _key.Value;
+            }
         }
 #if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
     }
index 1b7f928..a190913 100644 (file)
@@ -11,11 +11,11 @@ namespace System.Security.Cryptography
     {
         public sealed partial class ECDiffieHellmanSecurityTransforms : ECDiffieHellman
         {
-            private readonly EccSecurityTransforms _ecc = new EccSecurityTransforms();
+            private readonly EccSecurityTransforms _ecc = new EccSecurityTransforms(nameof(ECDiffieHellman));
 
             public ECDiffieHellmanSecurityTransforms()
             {
-                KeySize = 521;
+                base.KeySize = 521;
             }
 
             internal ECDiffieHellmanSecurityTransforms(SafeSecKeyRefHandle publicKey)
@@ -51,10 +51,15 @@ namespace System.Security.Cryptography
 
                     // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
                     base.KeySize = value;
-                    _ecc.Dispose();
+                    _ecc.DisposeKey();
                 }
             }
 
+            private void ThrowIfDisposed()
+            {
+                _ecc.ThrowIfDisposed();
+            }
+
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
@@ -87,6 +92,24 @@ namespace System.Security.Cryptography
                 KeySizeValue = _ecc.ImportSubjectPublicKeyInfo(source, out bytesRead);
             }
 
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
             public override void GenerateKey(ECCurve curve)
             {
                 KeySizeValue = _ecc.GenerateKey(curve);
@@ -111,6 +134,8 @@ namespace System.Security.Cryptography
                 if (string.IsNullOrEmpty(hashAlgorithm.Name))
                     throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyFromHash(
                     otherPartyPublicKey,
                     hashAlgorithm,
@@ -131,6 +156,8 @@ namespace System.Security.Cryptography
                 if (string.IsNullOrEmpty(hashAlgorithm.Name))
                     throw new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, nameof(hashAlgorithm));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyFromHmac(
                     otherPartyPublicKey,
                     hashAlgorithm,
@@ -150,6 +177,8 @@ namespace System.Security.Cryptography
                 if (prfSeed == null)
                     throw new ArgumentNullException(nameof(prfSeed));
 
+                ThrowIfDisposed();
+
                 return ECDiffieHellmanDerivation.DeriveKeyTls(
                     otherPartyPublicKey,
                     prfLabel,
@@ -235,7 +264,7 @@ namespace System.Security.Cryptography
                 public ECDiffieHellmanSecurityTransformsPublicKey(ECParameters ecParameters)
                 {
                     Debug.Assert(ecParameters.D == null);
-                    _ecc = new EccSecurityTransforms();
+                    _ecc = new EccSecurityTransforms(nameof(ECDiffieHellmanPublicKey));
                     _ecc.ImportParameters(ecParameters);
                 }
 
@@ -258,7 +287,6 @@ namespace System.Security.Cryptography
                     if (disposing)
                     {
                         _ecc.Dispose();
-                        _ecc = null;
                     }
 
                     base.Dispose(disposing);
@@ -267,29 +295,11 @@ namespace System.Security.Cryptography
                 public override ECParameters ExportExplicitParameters() =>
                     throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
 
-                public override ECParameters ExportParameters()
-                {
-                    if (_ecc == null)
-                    {
-                        throw new ObjectDisposedException(typeof(ECDiffieHellmanSecurityTransformsPublicKey).Name);
-                    }
+                public override ECParameters ExportParameters() =>
+                    _ecc.ExportParameters(includePrivateParameters: false, keySizeInBits: -1);
 
-                    return _ecc.ExportParameters(includePrivateParameters: false, keySizeInBits: -1);
-                }
-
-                internal SafeSecKeyRefHandle KeyHandle
-                {
-                    get
-                    {
-                        if (_ecc == null)
-                        {
-                            throw new ObjectDisposedException(
-                                typeof(ECDiffieHellmanSecurityTransformsPublicKey).Name);
-                        }
-
-                        return _ecc.GetOrGenerateKeys(-1).PublicKey;
-                    }
-                }
+                internal SafeSecKeyRefHandle KeyHandle =>
+                    _ecc.GetOrGenerateKeys(-1).PublicKey;
             }
         }
     }
index c229a14..ade3e6f 100644 (file)
@@ -30,6 +30,8 @@ namespace System.Security.Cryptography
             public override void ImportParameters(ECParameters parameters)
             {
                 parameters.Validate();
+                ThrowIfDisposed();
+
                 ECCurve curve = parameters.Curve;
                 bool includePrivateParameters = (parameters.D != null);
 
@@ -102,6 +104,8 @@ namespace System.Security.Cryptography
 
             public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportPkcs8PrivateKey(source, out int localRead);
 
                 ProcessPkcs8Response(response);
@@ -113,6 +117,8 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     passwordBytes,
                     source,
@@ -127,6 +133,8 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     password,
                     source,
index 4f5d488..a87059a 100644 (file)
@@ -2,8 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System.Buffers;
-using System.Diagnostics;
 using System.IO;
 using Internal.Cryptography;
 using Microsoft.Win32.SafeHandles;
@@ -43,9 +41,10 @@ namespace System.Security.Cryptography
             /// <param name="keySize">Size of the key to generate, in bits.</param>
             public ECDsaOpenSsl(int keySize)
             {
-                KeySize = keySize;
-                // Setting KeySize wakes up _key.
-                Debug.Assert(_key != null);
+                // Use the base setter to get the validation and field assignment without the
+                // side effect of dereferencing _key.
+                base.KeySize = keySize;
+                _key = new ECOpenSsl(this);
             }
 
             /// <summary>
@@ -78,6 +77,7 @@ namespace System.Security.Cryptography
                 if (hash == null)
                     throw new ArgumentNullException(nameof(hash));
 
+                ThrowIfDisposed();
                 SafeEcKeyHandle key = _key.Value;
                 int signatureLength = Interop.Crypto.EcDsaSize(key);
                 byte[] signature = new byte[signatureLength];
@@ -91,6 +91,7 @@ namespace System.Security.Cryptography
 
             public override bool TrySignHash(ReadOnlySpan<byte> hash, Span<byte> destination, out int bytesWritten)
             {
+                ThrowIfDisposed();
                 SafeEcKeyHandle key = _key.Value;
 
                 byte[] converted;
@@ -135,6 +136,8 @@ namespace System.Security.Cryptography
 
             public override bool VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
             {
+                ThrowIfDisposed();
+
                 // The signature format for .NET is r.Concat(s). Each of r and s are of length BitsToBytes(KeySize), even
                 // when they would have leading zeroes.  If it's the correct size, then we need to encode it from
                 // r.Concat(s) to SEQUENCE(INTEGER(r), INTEGER(s)), because that's the format that OpenSSL expects.
@@ -165,7 +168,8 @@ namespace System.Security.Cryptography
             {
                 if (disposing)
                 {
-                    _key.Dispose();
+                    _key?.Dispose();
+                    _key = null;
                 }
 
                 base.Dispose(disposing);
@@ -185,15 +189,15 @@ namespace System.Security.Cryptography
                     // Set the KeySize before FreeKey so that an invalid value doesn't throw away the key
                     base.KeySize = value;
 
-                    // This is the only place where _key can be null, because it's called by the constructor
-                    // which sets KeySize.
-                    _key?.Dispose();
+                    ThrowIfDisposed();
+                    _key.Dispose();
                     _key = new ECOpenSsl(this);
                 }
             }
 
             public override void GenerateKey(ECCurve curve)
             {
+                ThrowIfDisposed();
                 _key.GenerateKey(curve);
 
                 // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere
@@ -203,16 +207,54 @@ namespace System.Security.Cryptography
 
             public override void ImportParameters(ECParameters parameters)
             {
+                ThrowIfDisposed();
                 _key.ImportParameters(parameters);
                 ForceSetKeySize(_key.KeySize);
             }
 
-            public override ECParameters ExportExplicitParameters(bool includePrivateParameters) =>
-                ECOpenSsl.ExportExplicitParameters(_key.Value, includePrivateParameters);
+            public override ECParameters ExportExplicitParameters(bool includePrivateParameters)
+            {
+                ThrowIfDisposed();
+                return ECOpenSsl.ExportExplicitParameters(_key.Value, includePrivateParameters);
+            }
 
-            public override ECParameters ExportParameters(bool includePrivateParameters) =>
-                ECOpenSsl.ExportParameters(_key.Value, includePrivateParameters);
+            public override ECParameters ExportParameters(bool includePrivateParameters)
+            {
+                ThrowIfDisposed();
+                return ECOpenSsl.ExportParameters(_key.Value, includePrivateParameters);
+            }
 
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
+            private void ThrowIfDisposed()
+            {
+                if (_key == null)
+                {
+                    throw new ObjectDisposedException(
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+                        nameof(ECDsa)
+#else
+                        nameof(ECDsaOpenSsl)
+#endif
+                    );
+                }
+            }
         }
 #if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
     }
index c306066..42bf959 100644 (file)
@@ -49,11 +49,11 @@ namespace System.Security.Cryptography
         {
             public sealed partial class ECDsaSecurityTransforms : ECDsa
             {
-                private readonly EccSecurityTransforms _ecc = new EccSecurityTransforms();
+                private readonly EccSecurityTransforms _ecc = new EccSecurityTransforms(nameof(ECDsa));
 
                 public ECDsaSecurityTransforms()
                 {
-                    KeySize = 521;
+                    base.KeySize = 521;
                 }
 
                 internal ECDsaSecurityTransforms(SafeSecKeyRefHandle publicKey)
@@ -91,7 +91,7 @@ namespace System.Security.Cryptography
 
                         // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
                         base.KeySize = value;
-                        _ecc.Dispose();
+                        _ecc.DisposeKey();
                     }
                 }
 
@@ -157,6 +157,8 @@ namespace System.Security.Cryptography
 
                 public override bool VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
                 {
+                    ThrowIfDisposed();
+
                     // The signature format for .NET is r.Concat(s). Each of r and s are of length BitsToBytes(KeySize), even
                     // when they would have leading zeroes.  If it's the correct size, then we need to encode it from
                     // r.Concat(s) to SEQUENCE(INTEGER(r), INTEGER(s)), because that's the format that OpenSSL expects.
@@ -182,6 +184,11 @@ namespace System.Security.Cryptography
                 protected override bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten) =>
                     AsymmetricAlgorithmHelpers.TryHashData(source, destination, hashAlgorithm, out bytesWritten);
 
+                private void ThrowIfDisposed()
+                {
+                    _ecc.ThrowIfDisposed();
+                }
+
                 protected override void Dispose(bool disposing)
                 {
                     if (disposing)
@@ -207,6 +214,24 @@ namespace System.Security.Cryptography
                     KeySizeValue = _ecc.ImportParameters(parameters);
                 }
 
+                public override void ImportEncryptedPkcs8PrivateKey(
+                    ReadOnlySpan<byte> passwordBytes,
+                    ReadOnlySpan<byte> source,
+                    out int bytesRead)
+                {
+                    ThrowIfDisposed();
+                    base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+                }
+
+                public override void ImportEncryptedPkcs8PrivateKey(
+                    ReadOnlySpan<char> password,
+                    ReadOnlySpan<byte> source,
+                    out int bytesRead)
+                {
+                    ThrowIfDisposed();
+                    base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+                }
+
                 public override void GenerateKey(ECCurve curve)
                 {
                     KeySizeValue = _ecc.GenerateKey(curve);
index bd2f4a9..2bf2bc2 100644 (file)
@@ -7,23 +7,37 @@ using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography.Apple;
 using System.Security.Cryptography.Asn1;
-using Internal.Cryptography;
 
 namespace System.Security.Cryptography
 {
     internal sealed class EccSecurityTransforms : IDisposable
     {
         private SecKeyPair _keys;
+        private bool _disposed;
+        private readonly string _disposedName;
 
-        public void Dispose()
+        internal EccSecurityTransforms(string disposedTypeName)
+        {
+            Debug.Assert(disposedTypeName != null);
+            _disposedName = disposedTypeName;
+        }
+
+        internal void DisposeKey()
         {
             _keys?.Dispose();
             _keys = null;
         }
 
+        public void Dispose()
+        {
+            DisposeKey();
+            _disposed = true;
+        }
+
         internal int GenerateKey(ECCurve curve)
         {
             curve.Validate();
+            ThrowIfDisposed();
 
             if (!curve.IsNamed)
             {
@@ -64,8 +78,18 @@ namespace System.Security.Cryptography
             return newPair;
         }
 
+        internal void ThrowIfDisposed()
+        {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(_disposedName);
+            }
+        }
+
         internal SecKeyPair GetOrGenerateKeys(int keySizeInBits)
         {
+            ThrowIfDisposed();
+
             SecKeyPair current = _keys;
 
             if (current != null)
@@ -85,6 +109,8 @@ namespace System.Security.Cryptography
 
         private void SetKey(SecKeyPair keyPair)
         {
+            ThrowIfDisposed();
+
             SecKeyPair current = _keys;
             _keys = keyPair;
             current?.Dispose();
@@ -97,8 +123,7 @@ namespace System.Security.Cryptography
             const string ExportPassword = "DotnetExportPassphrase";
             SecKeyPair keys = GetOrGenerateKeys(keySizeInBits);
 
-            if (keys.PublicKey == null ||
-                (includePrivateParameters && keys.PrivateKey == null))
+            if (includePrivateParameters && keys.PrivateKey == null)
             { 
                 throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
             }
@@ -137,6 +162,7 @@ namespace System.Security.Cryptography
         internal int ImportParameters(ECParameters parameters)
         {
             parameters.Validate();
+            ThrowIfDisposed();
 
             bool isPrivateKey = parameters.D != null;
             SecKeyPair newKeys;
@@ -206,6 +232,8 @@ namespace System.Security.Cryptography
             ReadOnlySpan<byte> source,
             out int bytesRead)
         {
+            ThrowIfDisposed();
+
             fixed (byte* ptr = &MemoryMarshal.GetReference(source))
             {
                 using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
index cc01870..53d022a 100644 (file)
@@ -112,6 +112,8 @@ namespace System.Security.Cryptography
 
             public override void ImportPkcs8PrivateKey(ReadOnlySpan<byte> source, out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportPkcs8PrivateKey(source, out int localRead);
 
                 ProcessPkcs8Response(response);
@@ -123,6 +125,8 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     passwordBytes,
                     source,
@@ -137,6 +141,8 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey(
                     password,
                     source,
index 88092e2..6741d28 100644 (file)
@@ -37,7 +37,7 @@ namespace System.Security.Cryptography
 
         public RSAOpenSsl(int keySize)
         {
-            KeySize = keySize;
+            base.KeySize = keySize;
             _key = new Lazy<SafeRsaHandle>(GenerateKey);
         }
 
@@ -53,6 +53,7 @@ namespace System.Security.Cryptography
                 // Set the KeySize before FreeKey so that an invalid value doesn't throw away the key
                 base.KeySize = value;
 
+                ThrowIfDisposed();
                 FreeKey();
                 _key = new Lazy<SafeRsaHandle>(GenerateKey);
             }
@@ -88,8 +89,7 @@ namespace System.Security.Cryptography
                 throw new ArgumentNullException(nameof(padding));
 
             Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = _key.Value;
-            CheckInvalidKey(key);
+            SafeRsaHandle key = GetKey();
 
             int rsaSize = Interop.Crypto.RsaSize(key);
             byte[] buf = null;
@@ -127,8 +127,7 @@ namespace System.Security.Cryptography
             }
 
             Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = _key.Value;
-            CheckInvalidKey(key);
+            SafeRsaHandle key = GetKey();
 
             int keySizeBytes = Interop.Crypto.RsaSize(key);
 
@@ -261,9 +260,8 @@ namespace System.Security.Cryptography
                 throw new ArgumentNullException(nameof(padding));
 
             Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = _key.Value;
-            CheckInvalidKey(key);
-
+            SafeRsaHandle key = GetKey();
+            
             byte[] buf = new byte[Interop.Crypto.RsaSize(key)];
 
             bool encrypted = TryEncrypt(
@@ -291,8 +289,7 @@ namespace System.Security.Cryptography
             }
 
             Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = _key.Value;
-            CheckInvalidKey(key);
+            SafeRsaHandle key = GetKey();
 
             return TryEncrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten);
         }
@@ -375,9 +372,7 @@ namespace System.Security.Cryptography
         public override RSAParameters ExportParameters(bool includePrivateParameters)
         {
             // It's entirely possible that this line will cause the key to be generated in the first place.
-            SafeRsaHandle key = _key.Value;
-
-            CheckInvalidKey(key);
+            SafeRsaHandle key = GetKey();
 
             RSAParameters rsaParameters = Interop.Crypto.ExportRsaParameters(key, includePrivateParameters);
             bool hasPrivateKey = rsaParameters.D != null;
@@ -393,6 +388,7 @@ namespace System.Security.Cryptography
         public override void ImportParameters(RSAParameters parameters)
         {
             ValidateParameters(ref parameters);
+            ThrowIfDisposed();
 
             SafeRsaHandle key = Interop.Crypto.RsaCreate();
             bool imported = false;
@@ -443,6 +439,8 @@ namespace System.Security.Cryptography
 
         public override unsafe void ImportRSAPublicKey(ReadOnlySpan<byte> source, out int bytesRead)
         {
+            ThrowIfDisposed();
+
             fixed (byte* ptr = &MemoryMarshal.GetReference(source))
             {
                 using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
@@ -466,11 +464,30 @@ namespace System.Security.Cryptography
             }
         }
 
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<byte> passwordBytes,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+        }
+
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<char> password,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+        }
+
         protected override void Dispose(bool disposing)
         {
             if (disposing)
             {
                 FreeKey();
+                _key = null;
             }
 
             base.Dispose(disposing);
@@ -522,12 +539,32 @@ namespace System.Security.Cryptography
             return true;
         }
 
-        private static void CheckInvalidKey(SafeRsaHandle key)
+        private void ThrowIfDisposed()
         {
+            if (_key == null)
+            {
+                throw new ObjectDisposedException(
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+                    nameof(RSA)
+#else
+                    nameof(RSAOpenSsl)
+#endif
+                );
+            }
+        }
+
+        private SafeRsaHandle GetKey()
+        {
+            ThrowIfDisposed();    
+
+            SafeRsaHandle key = _key.Value;
+
             if (key == null || key.IsInvalid)
             {
                 throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
             }
+
+            return key;
         }
 
         private static void CheckReturn(int returnValue)
@@ -663,7 +700,7 @@ namespace System.Security.Cryptography
             if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
             {
                 int algorithmNid = GetAlgorithmNid(hashAlgorithm);
-                SafeRsaHandle rsa = _key.Value;
+                SafeRsaHandle rsa = GetKey();
 
                 int bytesRequired = Interop.Crypto.RsaSize(rsa);
 
@@ -695,7 +732,7 @@ namespace System.Security.Cryptography
             else if (padding.Mode == RSASignaturePaddingMode.Pss)
             {
                 RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
-                SafeRsaHandle rsa = _key.Value;
+                SafeRsaHandle rsa = GetKey();
 
                 int bytesRequired = Interop.Crypto.RsaSize(rsa);
 
@@ -766,13 +803,13 @@ namespace System.Security.Cryptography
             if (padding == RSASignaturePadding.Pkcs1)
             {
                 int algorithmNid = GetAlgorithmNid(hashAlgorithm);
-                SafeRsaHandle rsa = _key.Value;
+                SafeRsaHandle rsa = GetKey();
                 return Interop.Crypto.RsaVerify(algorithmNid, hash, signature, rsa);
             }
             else if (padding == RSASignaturePadding.Pss)
             {
                 RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
-                SafeRsaHandle rsa = _key.Value;
+                SafeRsaHandle rsa = GetKey();
 
                 int requiredBytes = Interop.Crypto.RsaSize(rsa);
 
index f93f015..50b3b69 100644 (file)
@@ -36,7 +36,7 @@ namespace System.Security.Cryptography
 
             public RSASecurityTransforms(int keySize)
             {
-                KeySize = keySize;
+                base.KeySize = keySize;
             }
 
             internal RSASecurityTransforms(SafeSecKeyRefHandle publicKey)
@@ -77,6 +77,8 @@ namespace System.Security.Cryptography
                     // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
                     base.KeySize = value;
 
+                    ThrowIfDisposed();
+
                     if (_keys != null)
                     {
                         _keys.Dispose();
@@ -92,8 +94,7 @@ namespace System.Security.Cryptography
                 const string ExportPassword = "DotnetExportPassphrase";
                 SecKeyPair keys = GetKeys();
 
-                if (keys.PublicKey == null ||
-                    (includePrivateParameters && keys.PrivateKey == null))
+                if (includePrivateParameters && keys.PrivateKey == null)
                 { 
                     throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
                 }
@@ -150,6 +151,9 @@ namespace System.Security.Cryptography
 
             public override void ImportParameters(RSAParameters parameters)
             {
+                ValidateParameters(parameters);
+                ThrowIfDisposed();
+
                 bool isPrivateKey = parameters.D != null;
 
                 if (isPrivateKey)
@@ -190,6 +194,8 @@ namespace System.Security.Cryptography
                 ReadOnlySpan<byte> source,
                 out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 fixed (byte* ptr = &MemoryMarshal.GetReference(source))
                 {
                     using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
@@ -209,6 +215,8 @@ namespace System.Security.Cryptography
 
             public override unsafe void ImportRSAPublicKey(ReadOnlySpan<byte> source, out int bytesRead)
             {
+                ThrowIfDisposed();
+
                 fixed (byte* ptr = &MemoryMarshal.GetReference(source))
                 {
                     using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
@@ -237,6 +245,24 @@ namespace System.Security.Cryptography
                 }
             }
 
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<byte> passwordBytes,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+            }
+
+            public override void ImportEncryptedPkcs8PrivateKey(
+                ReadOnlySpan<char> password,
+                ReadOnlySpan<byte> source,
+                out int bytesRead)
+            {
+                ThrowIfDisposed();
+                base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+            }
+
             public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
             {
                 if (data == null)
@@ -248,6 +274,8 @@ namespace System.Security.Cryptography
                     throw new ArgumentNullException(nameof(padding));
                 }
 
+                ThrowIfDisposed();
+
                 // The size of encrypt is always the keysize (in ceiling-bytes)
                 int outputSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);
                 byte[] output = new byte[outputSize];
@@ -269,6 +297,8 @@ namespace System.Security.Cryptography
                     throw new ArgumentNullException(nameof(padding));
                 }
 
+                ThrowIfDisposed();
+
                 int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);
 
                 if (destination.Length < rsaSize)
@@ -468,6 +498,8 @@ namespace System.Security.Cryptography
                 if (padding == null)
                     throw new ArgumentNullException(nameof(padding));
 
+                ThrowIfDisposed();
+
                 if (padding == RSASignaturePadding.Pkcs1)
                 {
                     SecKeyPair keys = GetKeys();
@@ -524,6 +556,8 @@ namespace System.Security.Cryptography
                     throw new ArgumentNullException(nameof(padding));
                 }
 
+                ThrowIfDisposed();
+
                 RsaPaddingProcessor processor = null;
 
                 if (padding.Mode == RSASignaturePaddingMode.Pss)
@@ -628,6 +662,8 @@ namespace System.Security.Cryptography
                     throw new ArgumentNullException(nameof(padding));
                 }
 
+                ThrowIfDisposed();
+
                 if (padding == RSASignaturePadding.Pkcs1)
                 {
                     Interop.AppleCrypto.PAL_HashAlgorithm palAlgId =
@@ -695,8 +731,8 @@ namespace System.Security.Cryptography
                 {
                     if (_keys != null)
                     {
+                        // Do not set _keys to null, in order to prevent rehydration.
                         _keys.Dispose();
-                        _keys = null;
                     }
                 }
 
@@ -736,8 +772,19 @@ namespace System.Security.Cryptography
                 throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name);
             }
 
+            private void ThrowIfDisposed()
+            {
+                SecKeyPair current = _keys;
+
+                if (current != null && current.PublicKey == null)
+                {
+                    throw new ObjectDisposedException(nameof(RSA));
+                }
+            }
+
             internal SecKeyPair GetKeys()
             {
+                ThrowIfDisposed();
                 SecKeyPair current = _keys;
 
                 if (current != null)
@@ -757,6 +804,8 @@ namespace System.Security.Cryptography
 
             private void SetKey(SecKeyPair newKeyPair)
             {
+                ThrowIfDisposed();
+
                 SecKeyPair current = _keys;
                 _keys = newKeyPair;
                 current?.Dispose();
@@ -784,6 +833,43 @@ namespace System.Security.Cryptography
                     }
                 }
             }
+
+            private static void ValidateParameters(in RSAParameters parameters)
+            {
+                if (parameters.Modulus == null || parameters.Exponent == null)
+                    throw new CryptographicException(SR.Argument_InvalidValue);
+
+                if (!HasConsistentPrivateKey(parameters))
+                    throw new CryptographicException(SR.Argument_InvalidValue);
+            }
+
+            private static bool HasConsistentPrivateKey(in RSAParameters parameters)
+            {
+                if (parameters.D == null)
+                {
+                    if (parameters.P != null ||
+                        parameters.DP != null ||
+                        parameters.Q != null ||
+                        parameters.DQ != null ||
+                        parameters.InverseQ != null)
+                    {
+                        return false;
+                    }
+                }
+                else
+                {
+                    if (parameters.P == null ||
+                        parameters.DP == null ||
+                        parameters.Q == null ||
+                        parameters.DQ == null ||
+                        parameters.InverseQ == null)
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
         }
 
         private static Exception HashAlgorithmNameNullOrEmpty() =>
index 129ec67..46e99d0 100644 (file)
@@ -132,6 +132,31 @@ namespace System.Security.Cryptography.Dsa.Tests
             }
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public static void ExportAfterDispose(bool importKey)
+        {
+            DSA key = importKey ? DSAFactory.Create(DSATestData.GetDSA1024Params()) : DSAFactory.Create(512);
+            byte[] hash = new byte[20];
+
+            // Ensure that the key got created, and then Dispose it.
+            using (key)
+            {
+                try
+                {
+                    key.CreateSignature(hash);
+                }
+                catch (PlatformNotSupportedException) when (!SupportsKeyGeneration)
+                {
+                }
+            }
+
+            Assert.Throws<ObjectDisposedException>(() => key.ExportParameters(false));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportParameters(true));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportParameters(DSATestData.GetDSA1024Params()));
+        }
+
         internal static void AssertKeyEquals(in DSAParameters expected, in DSAParameters actual)
         {
             Assert.Equal(expected.G, actual.G);
index eab4c28..b211d14 100644 (file)
@@ -12,6 +12,65 @@ namespace System.Security.Cryptography.Dsa.Tests
     {
         public static bool SupportsFips186_3 => DSAFactory.SupportsFips186_3;
 
+        [ConditionalFact(typeof(DSAFactory), nameof(DSAFactory.SupportsKeyGeneration))]
+        public static void UseAfterDispose_NewKey()
+        {
+            UseAfterDispose(false);
+        }
+
+        [Fact]
+        public static void UseAfterDispose_ImportedKey()
+        {
+            UseAfterDispose(true);
+        }
+
+        private static void UseAfterDispose(bool importKey)
+        {
+            DSA key = importKey ? DSAFactory.Create(DSATestData.GetDSA1024Params()) : DSAFactory.Create(512);
+
+            byte[] pkcs8Private;
+            byte[] pkcs8EncryptedPrivate;
+            byte[] subjectPublicKeyInfo;
+
+            string pwStr = "Hello";
+            // Because the PBE algorithm uses PBES2 the string->byte encoding is UTF-8.
+            byte[] pwBytes = Encoding.UTF8.GetBytes(pwStr);
+
+            PbeParameters pbeParameters = new PbeParameters(
+                PbeEncryptionAlgorithm.Aes192Cbc,
+                HashAlgorithmName.SHA256,
+                3072);
+
+            // Ensure the key was loaded, then dispose it.
+            // Also ensures all of the inputs are valid for the disposed tests.
+            using (key)
+            {
+                pkcs8Private = key.ExportPkcs8PrivateKey();
+                pkcs8EncryptedPrivate = key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters);
+                subjectPublicKeyInfo = key.ExportSubjectPublicKeyInfo();
+            }
+
+            Assert.Throws<ObjectDisposedException>(() => key.ImportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            Assert.Throws<ObjectDisposedException>(() => key.ExportPkcs8PrivateKey());
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportSubjectPublicKeyInfo());
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            // Check encrypted import with the wrong password.
+            // It shouldn't do enough work to realize it was wrong.
+            pwBytes = Array.Empty<byte>();
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+        }
+
         [Fact]
         public static void ReadWriteDsa512Pkcs8()
         {
index 1bb8a31..4548cef 100644 (file)
@@ -15,6 +15,15 @@ namespace System.Security.Cryptography.Dsa.Tests
         public override bool VerifyData(DSA dsa, byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm) =>
             dsa.VerifyData(data, signature, hashAlgorithm);
 
+        protected override void UseAfterDispose(DSA dsa, byte[] data, byte[] sig)
+        {
+            base.UseAfterDispose(dsa, data, sig);
+            byte[] hash = new byte[20];
+
+            Assert.Throws<ObjectDisposedException>(() => dsa.CreateSignature(hash));
+            Assert.Throws<ObjectDisposedException>(() => dsa.VerifySignature(hash, sig));
+        }
+
         [Fact]
         public void InvalidStreamArrayArguments_Throws()
         {
@@ -65,6 +74,7 @@ namespace System.Security.Cryptography.Dsa.Tests
         }
     }
 
+#if netcoreapp
     public sealed class DSASignVerify_Span : DSASignVerify
     {
         public override byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm) =>
@@ -73,6 +83,15 @@ namespace System.Security.Cryptography.Dsa.Tests
         public override bool VerifyData(DSA dsa, byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm) =>
             dsa.VerifyData((ReadOnlySpan<byte>)data, (ReadOnlySpan<byte>)signature, hashAlgorithm);
 
+        protected override void UseAfterDispose(DSA dsa, byte[] data, byte[] sig)
+        {
+            base.UseAfterDispose(dsa, data, sig);
+            byte[] hash = new byte[20];
+
+            Assert.Throws<ObjectDisposedException>(() => dsa.TryCreateSignature(hash, sig, out _));
+            Assert.Throws<ObjectDisposedException>(() => dsa.VerifySignature(hash.AsSpan(), sig.AsSpan()));
+        }
+
         private static byte[] TryWithOutputArray(Func<byte[], (bool, int)> func)
         {
             for (int length = 1; ; length = checked(length * 2))
@@ -87,7 +106,7 @@ namespace System.Security.Cryptography.Dsa.Tests
             }
         }
     }
-
+#endif
     public abstract partial class DSASignVerify
     {
         public abstract byte[] SignData(DSA dsa, byte[] data, HashAlgorithmName hashAlgorithm);
@@ -108,6 +127,54 @@ namespace System.Security.Cryptography.Dsa.Tests
         }
 
         [ConditionalFact(nameof(SupportsKeyGeneration))]
+        public void UseAfterDispose_NewKey()
+        {
+            UseAfterDispose(false);
+        }
+
+        [Fact]
+        public void UseAfterDispose_ImportedKey()
+        {
+            UseAfterDispose(true);
+        }
+
+        private void UseAfterDispose(bool importKey)
+        {
+            DSA key = importKey ? DSAFactory.Create(DSATestData.GetDSA1024Params()) : DSAFactory.Create(512);
+            byte[] data = { 1 };
+            byte[] sig;
+
+            // Ensure the key is populated, then dispose it.
+            using (key)
+            {
+                sig = SignData(key, data, HashAlgorithmName.SHA1);
+            }
+
+            key.Dispose();
+
+            UseAfterDispose(key, data, sig);
+
+            Assert.Throws<ObjectDisposedException>(() => key.ImportParameters(DSATestData.GetDSA1024Params()));
+
+            // Either set_KeySize or SignData should throw.
+            Assert.Throws<ObjectDisposedException>(
+                () =>
+                {
+                    key.KeySize = 576;
+                    SignData(key, data, HashAlgorithmName.SHA1);
+                });
+        }
+
+        protected virtual void UseAfterDispose(DSA dsa, byte[] data, byte[] sig)
+        {
+            Assert.Throws<ObjectDisposedException>(
+                () => SignData(dsa, data, HashAlgorithmName.SHA1));
+
+            Assert.Throws<ObjectDisposedException>(
+                () => VerifyData(dsa, data, sig, HashAlgorithmName.SHA1));
+        }
+
+        [ConditionalFact(nameof(SupportsKeyGeneration))]
         public void SignAndVerifyDataNew1024()
         {
             using (DSA dsa = DSAFactory.Create(1024))
index 071d5f7..cfe28d9 100644 (file)
@@ -27,6 +27,66 @@ namespace System.Security.Cryptography.Tests
             return EcDiffieHellman.Tests.ECDiffieHellmanFactory.IsCurveValid(oid);
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void UseAfterDispose(bool importKey)
+        {
+            T key = CreateKey();
+
+            if (importKey)
+            {
+                ImportParameters(key, EccTestData.GetNistP256ReferenceKey());
+            }
+
+            byte[] ecPrivate;
+            byte[] pkcs8Private;
+            byte[] pkcs8EncryptedPrivate;
+            byte[] subjectPublicKeyInfo;
+
+            string pwStr = "Hello";
+            // Because the PBE algorithm uses PBES2 the string->byte encoding is UTF-8.
+            byte[] pwBytes = Encoding.UTF8.GetBytes(pwStr);
+
+            PbeParameters pbeParameters = new PbeParameters(
+                PbeEncryptionAlgorithm.Aes192Cbc,
+                HashAlgorithmName.SHA256,
+                3072);
+
+            // Ensure the key was loaded, then dispose it.
+            // Also ensures all of the inputs are valid for the disposed tests.
+            using (key)
+            {
+                ecPrivate = ExportECPrivateKey(key);
+                pkcs8Private = key.ExportPkcs8PrivateKey();
+                pkcs8EncryptedPrivate = key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters);
+                subjectPublicKeyInfo = key.ExportSubjectPublicKeyInfo();
+            }
+
+            Assert.Throws<ObjectDisposedException>(() => ImportECPrivateKey(key, ecPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            Assert.Throws<ObjectDisposedException>(() => ExportECPrivateKey(key));
+            Assert.Throws<ObjectDisposedException>(() => TryExportECPrivateKey(key, ecPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportPkcs8PrivateKey());
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ExportSubjectPublicKeyInfo());
+            Assert.Throws<ObjectDisposedException>(() => key.TryExportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            // Check encrypted import with the wrong password.
+            // It shouldn't do enough work to realize it was wrong.
+            pwBytes = Array.Empty<byte>();
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+        }
+
         [Fact]
         public void ReadWriteNistP521Pkcs8()
         {
index c864787..e5c91e6 100644 (file)
@@ -20,7 +20,6 @@ namespace System.Security.Cryptography.Tests
             ("a232cec7be26319e53db0d48470232d37793b06b99e8ed82fac1996b3d1596449087769927d64af657cce62d853c4cf7ff4c"
            + "d069eda230d1c524d225756ffbaf").HexToByteArray();
 
-#if netcoreapp
         internal static ECCurve GetNistP256ExplicitCurve()
         {
             // SEC2-Ver-1.0, 2.7.2
@@ -239,6 +238,5 @@ namespace System.Security.Cryptography.Tests
                 },
                 D = "00B4F1AE1E7FDCD4B0E82053C08A908852B26231E6C01670FCC6C3EA2C5D3FED40EDF037".HexToByteArray(),
             };
-#endif // netcoreapp
     }
 }
index 56760dc..dc0d0fd 100644 (file)
@@ -111,6 +111,53 @@ namespace System.Security.Cryptography.EcDiffieHellman.Tests
             }
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public static void UseAfterDispose(bool importKey)
+        {
+            ECDiffieHellman key = ECDiffieHellmanFactory.Create();
+            ECDiffieHellmanPublicKey pubKey;
+            HashAlgorithmName hash = HashAlgorithmName.SHA256;
+
+            if (importKey)
+            {
+                key.ImportParameters(EccTestData.GetNistP256ReferenceKey());
+            }
+
+            // Ensure the key is populated, then dispose it.
+            using (key)
+            {
+                pubKey = key.PublicKey;
+                key.DeriveKeyFromHash(pubKey, hash);
+
+                pubKey.Dispose();
+                Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHash(pubKey, hash));
+                Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHmac(pubKey, hash, null));
+                Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHmac(pubKey, hash, new byte[3]));
+                Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyTls(pubKey, new byte[4], new byte[64]));
+
+                pubKey = key.PublicKey;
+            }
+
+            key.Dispose();
+
+            Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHash(pubKey, hash));
+            Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHmac(pubKey, hash, null));
+            Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyFromHmac(pubKey, hash, new byte[3]));
+            Assert.Throws<ObjectDisposedException>(() => key.DeriveKeyTls(pubKey, new byte[4], new byte[64]));
+            Assert.Throws<ObjectDisposedException>(() => key.GenerateKey(ECCurve.NamedCurves.nistP256));
+            Assert.Throws<ObjectDisposedException>(() => key.ImportParameters(EccTestData.GetNistP256ReferenceKey()));
+
+            // Either set_KeySize or the ExportParameters should throw.
+            Assert.Throws<ObjectDisposedException>(
+                () =>
+                {
+                    key.KeySize = 384;
+                    key.ExportParameters(false);
+                });
+        }
+
 #if netcoreapp
         private static ECDiffieHellman OpenKnownKey()
         {
index 899b907..b6773b8 100644 (file)
@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Security.Cryptography.Tests;
 using System.Text;
 using Xunit;
 
@@ -18,6 +19,15 @@ namespace System.Security.Cryptography.EcDsa.Tests
         protected override byte[] SignData(ECDsa ecdsa, byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
             ecdsa.SignData(data, offset, count, hashAlgorithm);
 
+        protected override void UseAfterDispose(ECDsa ecdsa, byte[] data, byte[] sig)
+        {
+            base.UseAfterDispose(ecdsa, data, sig);
+            byte[] hash = new byte[32];
+
+            Assert.Throws<ObjectDisposedException>(() => ecdsa.VerifyHash(hash, sig));
+            Assert.Throws<ObjectDisposedException>(() => ecdsa.SignHash(hash));
+        }
+
         [Theory, MemberData(nameof(RealImplementations))]
         public void SignData_InvalidArguments_Throws(ECDsa ecdsa)
         {
@@ -133,6 +143,62 @@ namespace System.Security.Cryptography.EcDsa.Tests
 
         [Theory]
         [MemberData(nameof(RealImplementations))]
+        public void UseAfterDispose_Import(ECDsa ecdsa)
+        {
+            ecdsa.ImportParameters(EccTestData.GetNistP256ReferenceKey());
+            UseAfterDispose(ecdsa);
+        }
+
+        [Theory]
+        [MemberData(nameof(RealImplementations))]
+        public void UseAfterDispose_NewKey(ECDsa ecdsa)
+        {
+            UseAfterDispose(ecdsa);
+        }
+
+        private void UseAfterDispose(ECDsa ecdsa)
+        {
+            byte[] data = { 1 };
+            byte[] sig;
+
+            // Ensure the key is populated, then dispose it.
+            using (ecdsa)
+            {
+                sig = SignData(ecdsa, data, HashAlgorithmName.SHA256);
+            }
+
+            ecdsa.Dispose();
+
+            UseAfterDispose(ecdsa, data, sig);
+
+            if (!(PlatformDetection.IsFullFramework && ecdsa.GetType().Name.EndsWith("Cng")))
+            {
+                Assert.Throws<ObjectDisposedException>(() => ecdsa.GenerateKey(ECCurve.NamedCurves.nistP256));
+
+                Assert.Throws<ObjectDisposedException>(
+                    () => ecdsa.ImportParameters(EccTestData.GetNistP256ReferenceKey()));
+            }
+
+            // Either set_KeySize or SignData should throw.
+            Assert.Throws<ObjectDisposedException>(
+                () =>
+                {
+                    ecdsa.KeySize = 384;
+                    SignData(ecdsa, data, HashAlgorithmName.SHA256);
+                });
+        }
+
+        protected virtual void UseAfterDispose(ECDsa ecdsa, byte[] data, byte[] sig)
+        {
+            Assert.Throws<ObjectDisposedException>(
+                () => SignData(ecdsa, data, HashAlgorithmName.SHA256));
+
+            Assert.Throws<ObjectDisposedException>(
+                () => VerifyData(ecdsa, data, sig, HashAlgorithmName.SHA256));
+        }
+
+        [Theory]
+        [MemberData(nameof(RealImplementations))]
         public void SignData_MaxOffset_ZeroLength_NoThrow(ECDsa ecdsa)
         {
             // Explicitly larger than Array.Empty
index 7a0c55e..faa8ec1 100644 (file)
@@ -15,6 +15,15 @@ namespace System.Security.Cryptography.EcDsa.Tests
         protected override byte[] SignData(ECDsa ecdsa, byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
             TryWithOutputArray(dest => ecdsa.TrySignData(new ReadOnlySpan<byte>(data, offset, count), dest, hashAlgorithm, out int bytesWritten) ? (true, bytesWritten) : (false, 0));
 
+        protected override void UseAfterDispose(ECDsa ecdsa, byte[] data, byte[] sig)
+        {
+            base.UseAfterDispose(ecdsa, data, sig);
+            byte[] hash = new byte[32];
+
+            Assert.Throws<ObjectDisposedException>(() => ecdsa.VerifyHash(hash.AsSpan(), sig.AsSpan()));
+            Assert.Throws<ObjectDisposedException>(() => ecdsa.TrySignHash(hash, sig, out _));
+        }
+
         [Theory, MemberData(nameof(RealImplementations))]
         public void SignData_InvalidArguments_Throws(ECDsa ecdsa)
         {
index 513f28e..01ed1a3 100644 (file)
@@ -43,6 +43,24 @@ namespace System.Security.Cryptography.Rsa.Tests
             }
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void UseAfterDispose(bool importKey)
+        {
+            RSA rsa = importKey ? RSAFactory.Create(TestData.RSA2048Params) : RSAFactory.Create(1024);
+            byte[] data = TestData.HelloBytes;
+            byte[] enc;
+
+            using (rsa)
+            {
+                enc = Encrypt(rsa, data, RSAEncryptionPadding.Pkcs1);
+            }
+
+            Assert.Throws<ObjectDisposedException>(
+                () => Decrypt(rsa, enc, RSAEncryptionPadding.Pkcs1));
+        }
+
         [Fact]
         public void DecryptSavedAnswer()
         {
index 65807a6..77f7be7 100644 (file)
@@ -289,6 +289,28 @@ namespace System.Security.Cryptography.Rsa.Tests
             }
         }
 
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public static void ExportAfterDispose(bool importKey)
+        {
+            RSA rsa = importKey ? RSAFactory.Create(TestData.RSA2048Params) : RSAFactory.Create(1024);
+            
+            // Ensure that the key got created, and then Dispose it.
+            using (rsa)
+            {
+                rsa.Encrypt(TestData.HelloBytes, RSAEncryptionPadding.Pkcs1);
+            }
+
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportParameters(false));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportParameters(true));
+
+            if (!(PlatformDetection.IsFullFramework && rsa.GetType().Name.EndsWith("Cng")))
+            {
+                Assert.Throws<ObjectDisposedException>(() => rsa.ImportParameters(TestData.RSA1024Params));
+            }
+        }
+
         internal static void AssertKeyEquals(in RSAParameters expected, in RSAParameters actual)
         {
             Assert.Equal(expected.Modulus, actual.Modulus);
index fa9a398..8de550b 100644 (file)
@@ -13,6 +13,65 @@ namespace System.Security.Cryptography.Rsa.Tests
         public static bool Supports384BitPrivateKey { get; } = RSAFactory.Supports384PrivateKey;
         public static bool SupportsLargeExponent { get; } = RSAFactory.SupportsLargeExponent;
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public static void UseAfterDispose(bool importKey)
+        {
+            RSA rsa = importKey ? RSAFactory.Create(TestData.RSA2048Params) : RSAFactory.Create(1024);
+            byte[] pkcs1Public;
+            byte[] pkcs1Private;
+            byte[] pkcs8Private;
+            byte[] pkcs8EncryptedPrivate;
+            byte[] subjectPublicKeyInfo;
+
+            string pwStr = "Hello";
+            // Because the PBE algorithm uses PBES2 the string->byte encoding is UTF-8.
+            byte[] pwBytes = TestData.HelloBytes;
+
+            PbeParameters pbeParameters = new PbeParameters(
+                PbeEncryptionAlgorithm.Aes192Cbc,
+                HashAlgorithmName.SHA256,
+                3072);
+
+            // Ensure the key was loaded, then dispose it.
+            // Also ensures all of the inputs are valid for the disposed tests.
+            using (rsa)
+            {
+                pkcs1Public = rsa.ExportRSAPublicKey();
+                pkcs1Private = rsa.ExportRSAPrivateKey();
+                pkcs8Private = rsa.ExportPkcs8PrivateKey();
+                pkcs8EncryptedPrivate = rsa.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters);
+                subjectPublicKeyInfo = rsa.ExportSubjectPublicKeyInfo();
+            }
+
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportRSAPublicKey(pkcs1Public, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportRSAPrivateKey(pkcs1Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportRSAPublicKey());
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportRSAPublicKey(pkcs1Public, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportRSAPrivateKey());
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportRSAPrivateKey(pkcs1Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportPkcs8PrivateKey());
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportPkcs8PrivateKey(pkcs8Private, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters));
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ExportSubjectPublicKeyInfo());
+            Assert.Throws<ObjectDisposedException>(() => rsa.TryExportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _));
+
+            // Check encrypted import with the wrong password.
+            // It shouldn't do enough work to realize it was wrong.
+            pwBytes = Array.Empty<byte>();
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _));
+            Assert.Throws<ObjectDisposedException>(() => rsa.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _));
+        }
+
         [ConditionalFact(nameof(SupportsLargeExponent))]
         public static void ReadWriteBigExponentPrivatePkcs1()
         {
index dbb516b..882911f 100644 (file)
@@ -75,6 +75,37 @@ namespace System.Security.Cryptography.Rsa.Tests
             }
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void UseAfterDispose(bool importKey)
+        {
+            RSA rsa = importKey ? RSAFactory.Create(TestData.RSA2048Params) : RSAFactory.Create(1024);
+            byte[] data = TestData.HelloBytes;
+            byte[] sig;
+            HashAlgorithmName alg = HashAlgorithmName.SHA1;
+            RSASignaturePadding padding = RSASignaturePadding.Pkcs1;
+
+            using (rsa)
+            {
+                sig = SignData(rsa, data, alg, padding);
+            }
+
+            Assert.Throws<ObjectDisposedException>(
+                () => VerifyData(rsa, sig, data, alg, padding));
+
+            Assert.Throws<ObjectDisposedException>(
+                () => VerifyHash(rsa, sig, data, alg, padding));
+
+            // Either set_KeySize or SignData should throw.
+            Assert.Throws<ObjectDisposedException>(
+                () =>
+                {
+                    rsa.KeySize = 1024 + 64;
+                    SignData(rsa, data, alg, padding);
+                });
+        }
+
         [Fact]
         public void InvalidKeySize_DoesNotInvalidateKey()
         {
index 75d2462..7c8a86f 100644 (file)
@@ -130,12 +130,20 @@ namespace System.Security.Cryptography
         public override void FromXmlString(string xmlString) { }
         protected virtual byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public abstract void ImportParameters(System.Security.Cryptography.DSAParameters parameters);
+        public override void ImportPkcs8PrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual byte[] SignData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public byte[] SignData(byte[] data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public virtual byte[] SignData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public override string ToXmlString(bool includePrivateParameters) { throw null; }
         public virtual bool TryCreateSignature(System.ReadOnlySpan<byte> hash, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten) { throw null; }
         protected virtual bool TryHashData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; }
         public virtual bool TrySignData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; }
         public bool VerifyData(byte[] data, byte[] signature, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
@@ -246,9 +254,17 @@ namespace System.Security.Cryptography
         public override void FromXmlString(string xmlString) { }
         public virtual void GenerateKey(System.Security.Cryptography.ECCurve curve) { }
         public virtual void ImportECPrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { }
+        public override void ImportPkcs8PrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public override string ToXmlString(bool includePrivateParameters) { throw null; }
         public virtual bool TryExportECPrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten) { throw null; }
     }
     public abstract partial class ECDiffieHellmanPublicKey : System.IDisposable
     {
@@ -278,13 +294,21 @@ namespace System.Security.Cryptography
         protected virtual byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public virtual void ImportECPrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { }
+        public override void ImportPkcs8PrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual byte[] SignData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public virtual byte[] SignData(byte[] data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public virtual byte[] SignData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         public abstract byte[] SignHash(byte[] hash);
         public override string ToXmlString(bool includePrivateParameters) { throw null; }
         public virtual bool TryExportECPrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten) { throw null; }
         protected virtual bool TryHashData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; }
         public virtual bool TrySignData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; }
         public virtual bool TrySignHash(System.ReadOnlySpan<byte> hash, System.Span<byte> destination, out int bytesWritten) { throw null; }
@@ -490,9 +514,13 @@ namespace System.Security.Cryptography
         public override void FromXmlString(string xmlString) { }
         protected virtual byte[] HashData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
         protected virtual byte[] HashData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public abstract void ImportParameters(System.Security.Cryptography.RSAParameters parameters);
+        public override void ImportPkcs8PrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual void ImportRSAPrivateKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual void ImportRSAPublicKey(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
+        public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan<byte> source, out int bytesRead) { throw null; }
         public virtual byte[] SignData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { throw null; }
         public byte[] SignData(byte[] data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { throw null; }
         public virtual byte[] SignData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding) { throw null; }
@@ -500,8 +528,12 @@ namespace System.Security.Cryptography
         public override string ToXmlString(bool includePrivateParameters) { throw null; }
         public virtual bool TryDecrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.RSAEncryptionPadding padding, out int bytesWritten) { throw null; }
         public virtual bool TryEncrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.RSAEncryptionPadding padding, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<byte> passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan<char> password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportPkcs8PrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
         public virtual bool TryExportRSAPrivateKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
         public virtual bool TryExportRSAPublicKey(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override bool TryExportSubjectPublicKeyInfo(System.Span<byte> destination, out int bytesWritten) { throw null; }
         protected virtual bool TryHashData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; }
         public virtual bool TrySignData(System.ReadOnlySpan<byte> data, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding, out int bytesWritten) { throw null; }
         public virtual bool TrySignHash(System.ReadOnlySpan<byte> hash, System.Span<byte> destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding, out int bytesWritten) { throw null; }
index ec06de8..6c05638 100644 (file)
@@ -21,9 +21,20 @@ namespace System.Security.Cryptography
         {
             private SafeNCryptKeyHandle _keyHandle;
             private int _lastKeySize;
+            private bool _disposed;
+
+            private void ThrowIfDisposed()
+            {
+                if (_disposed)
+                {
+                    throw new ObjectDisposedException(nameof(RSA));
+                }
+            }
 
             private SafeNCryptKeyHandle GetDuplicatedKeyHandle()
             {
+                ThrowIfDisposed();
+
                 int keySize = KeySize;
 
                 if (_lastKeySize != keySize)
@@ -82,6 +93,8 @@ namespace System.Security.Cryptography
 
             private void ImportKeyBlob(byte[] dsaBlob, bool includePrivate)
             {
+                ThrowIfDisposed();
+
                 // Use generic blob type for multiple version support
                 string blobType = includePrivate ?
                     Interop.BCrypt.KeyBlobType.BCRYPT_PRIVATE_KEY_BLOB :
@@ -93,6 +106,8 @@ namespace System.Security.Cryptography
 
             private void AcceptImport(CngPkcs8.Pkcs8Response response)
             {
+                ThrowIfDisposed();
+
                 SafeNCryptKeyHandle keyHandle = response.KeyHandle;
                 SetKeyHandle(keyHandle);
             }
@@ -117,6 +132,19 @@ namespace System.Security.Cryptography
                 ForceSetKeySize(newKeySize);
                 _lastKeySize = newKeySize;
             }
+
+            protected override void Dispose(bool disposing)
+            {
+                if (disposing)
+                {
+                    _keyHandle?.Dispose();
+                    _keyHandle = null;
+                    _disposed = true;
+                }
+
+
+                base.Dispose(disposing);
+            }
         }
     }
 }
index 21e8e01..47dc3a2 100644 (file)
@@ -13,15 +13,18 @@ namespace System.Security.Cryptography
         private SafeNCryptKeyHandle _keyHandle;
         private int _lastKeySize;
         private string _lastAlgorithm;
+        private bool _disposed;
         private readonly string _algorithmGroup;
+        private readonly string _disposedName;
 
-        internal ECCngKey(string algorithmGroup)
+        internal ECCngKey(string algorithmGroup, string disposedName)
         {
             Debug.Assert(
                 algorithmGroup == BCryptNative.AlgorithmName.ECDH ||
                 algorithmGroup == BCryptNative.AlgorithmName.ECDsa);
 
             _algorithmGroup = algorithmGroup;
+            _disposedName = disposedName;
         }
 
         internal int KeySize { get; private set; }
@@ -46,6 +49,8 @@ namespace System.Security.Cryptography
 
         internal SafeNCryptKeyHandle GetDuplicatedKeyHandle(int callerKeySizeProperty)
         {
+            ThrowIfDisposed();
+
             if (ECCng.IsECNamedCurve(_lastAlgorithm))
             {
                 // Curve was previously created, so use that
@@ -100,6 +105,7 @@ namespace System.Security.Cryptography
         internal void GenerateKey(ECCurve curve)
         {
             curve.Validate();
+            ThrowIfDisposed();
 
             if (_keyHandle != null)
             {
@@ -192,6 +198,12 @@ namespace System.Security.Cryptography
             KeySize = keySize;
         }
 
+        internal void FullDispose()
+        {
+            DisposeKey();
+            _disposed = true;
+        }
+
         internal void DisposeKey()
         {
             if (_keyHandle != null)
@@ -206,6 +218,8 @@ namespace System.Security.Cryptography
 
         internal void SetHandle(SafeNCryptKeyHandle keyHandle, string algorithmName)
         {
+            ThrowIfDisposed();
+
             _keyHandle?.Dispose();
             _keyHandle = keyHandle;
             _lastAlgorithm = algorithmName;
@@ -213,5 +227,13 @@ namespace System.Security.Cryptography
             KeySize = CngKeyLite.GetKeyLength(keyHandle);
             _lastKeySize = KeySize;
         }
+
+        internal void ThrowIfDisposed()
+        {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(_disposedName);
+            }
+        }
     }
 }
index 0225a96..2210641 100644 (file)
@@ -11,7 +11,7 @@ namespace System.Security.Cryptography
     {
         public sealed partial class ECDiffieHellmanCng : ECDiffieHellman
         {
-            private readonly ECCngKey _key = new ECCngKey(BCryptNative.AlgorithmName.ECDH);
+            private readonly ECCngKey _key = new ECCngKey(BCryptNative.AlgorithmName.ECDH, nameof(ECDiffieHellman));
 
             private string GetCurveName(out string oidValue) => _key.GetCurveName(KeySize, out oidValue);
 
index 830f1dc..ba31142 100644 (file)
@@ -12,6 +12,21 @@ namespace System.Security.Cryptography
     {
         public sealed partial class ECDiffieHellmanCng : ECDiffieHellman
         {
+            protected override void Dispose(bool disposing)
+            {
+                if (disposing)
+                {
+                    _key.FullDispose();
+                }
+
+                base.Dispose(disposing);
+            }
+
+            private void ThrowIfDisposed()
+            {
+                _key.ThrowIfDisposed();
+            }
+
             private void ImportFullKeyBlob(byte[] ecfullKeyBlob, bool includePrivateParameters)
             {
                 string blobType = includePrivateParameters ?
index 4f2e283..725e807 100644 (file)
@@ -15,6 +15,7 @@ namespace System.Security.Cryptography
 
             protected override void Dispose(bool disposing)
             {
+                _keyBlob = null;
                 base.Dispose(disposing);
             }
 
@@ -48,6 +49,11 @@ namespace System.Security.Cryptography
             /// <returns>The key and explicit curve parameters used by the ECC object.</returns>
             public override ECParameters ExportExplicitParameters()
             {
+                if (_keyBlob == null)
+                {
+                    throw new ObjectDisposedException(nameof(ECDiffieHellmanPublicKey));
+                }
+
                 ECParameters ecparams = new ECParameters();
                 ECCng.ExportPrimeCurveParameters(ref ecparams, _keyBlob, includePrivateParameters: false);
                 return ecparams;
@@ -64,6 +70,11 @@ namespace System.Security.Cryptography
             /// <returns>The key and named curve parameters used by the ECC object.</returns>
             public override ECParameters ExportParameters()
             {
+                if (_keyBlob == null)
+                {
+                    throw new ObjectDisposedException(nameof(ECDiffieHellmanPublicKey));
+                }
+
                 if (string.IsNullOrEmpty(_curveName))
                 {
                     return ExportExplicitParameters();
index 2f38478..ca4f372 100644 (file)
@@ -11,7 +11,7 @@ namespace System.Security.Cryptography
     {
         public sealed partial class ECDsaCng : ECDsa
         {
-            private readonly ECCngKey _key = new ECCngKey(AlgorithmName.ECDsa);
+            private readonly ECCngKey _key = new ECCngKey(AlgorithmName.ECDsa, nameof(ECDsa));
 
             private string GetCurveName(out string oidValue) => _key.GetCurveName(KeySize, out oidValue);
             
index e90339f..afc1421 100644 (file)
@@ -48,6 +48,21 @@ namespace System.Security.Cryptography
     {
         public sealed partial class ECDsaCng : ECDsa
         {
+            protected override void Dispose(bool disposing)
+            {
+                if (disposing)
+                {
+                    _key?.FullDispose();
+                }
+
+                base.Dispose(disposing);
+            }
+
+            private void ThrowIfDisposed()
+            {
+                _key.ThrowIfDisposed();
+            }
+
             private void ImportFullKeyBlob(byte[] ecfullKeyBlob, bool includePrivateParameters)
             {
                 string blobType = includePrivateParameters ?
index e6f3571..614ea7a 100644 (file)
@@ -22,9 +22,20 @@ namespace System.Security.Cryptography
         {
             private SafeNCryptKeyHandle _keyHandle;
             private int _lastKeySize;
+            private bool _disposed;
+
+            private void ThrowIfDisposed()
+            {
+                if (_disposed)
+                {
+                    throw new ObjectDisposedException(nameof(RSA));
+                }
+            }
 
             private SafeNCryptKeyHandle GetDuplicatedKeyHandle()
             {
+                ThrowIfDisposed();
+
                 int keySize = KeySize;
 
                 if (_lastKeySize != keySize)
@@ -82,6 +93,8 @@ namespace System.Security.Cryptography
 
             private void ImportKeyBlob(byte[] rsaBlob, bool includePrivate)
             {
+                ThrowIfDisposed();
+
                 string blobType = includePrivate
                     ? Interop.BCrypt.KeyBlobType.BCRYPT_RSAPRIVATE_BLOB
                     : Interop.BCrypt.KeyBlobType.BCRYPT_RSAPUBLIC_KEY_BLOB;
@@ -92,6 +105,8 @@ namespace System.Security.Cryptography
 
             private void AcceptImport(CngPkcs8.Pkcs8Response response)
             {
+                ThrowIfDisposed();
+
                 SafeNCryptKeyHandle keyHandle = response.KeyHandle;
                 SetKeyHandle(keyHandle);
             }
@@ -121,6 +136,19 @@ namespace System.Security.Cryptography
                 ForceSetKeySize(newKeySize);
                 _lastKeySize = newKeySize;
             }
+
+            protected override void Dispose(bool disposing)
+            {
+                if (disposing)
+                {
+                    _keyHandle?.Dispose();
+                    _keyHandle = null;
+                    _disposed = true;
+                }
+
+
+                base.Dispose(disposing);
+            }
         }
     }
 }
index 43ae9aa..72bd377 100644 (file)
@@ -15,8 +15,15 @@ namespace Internal.Cryptography
     //
     internal struct CngAlgorithmCore
     {
+        private readonly string _disposedName;
         public CngAlgorithm DefaultKeyType;
         private CngKey _lazyKey;
+        private bool _disposed;
+
+        public CngAlgorithmCore(string disposedName) : this()
+        {
+            _disposedName = disposedName;
+        }
 
         public static CngKey Duplicate(CngKey key)
         {
@@ -28,6 +35,7 @@ namespace Internal.Cryptography
 
         public bool IsKeyGeneratedNamedCurve()
         {
+            ThrowIfDisposed();
             return (_lazyKey != null && _lazyKey.IsECNamedCurve());
         }
 
@@ -42,6 +50,8 @@ namespace Internal.Cryptography
 
         public CngKey GetOrGenerateKey(int keySize, CngAlgorithm algorithm)
         {
+            ThrowIfDisposed();
+
             // If our key size was changed, we need to generate a new key.
             if (_lazyKey != null)
             {
@@ -68,6 +78,8 @@ namespace Internal.Cryptography
 
         public CngKey GetOrGenerateKey(ECCurve? curve)
         {
+            ThrowIfDisposed();
+
             if (_lazyKey != null)
             {
                 return _lazyKey;
@@ -123,6 +135,7 @@ namespace Internal.Cryptography
         public void SetKey(CngKey key)
         {
             Debug.Assert(key != null);
+            ThrowIfDisposed();
 
             // If we already have a key, clear it out.
             DisposeKey();
@@ -133,6 +146,15 @@ namespace Internal.Cryptography
         public void Dispose()
         {
             DisposeKey();
+            _disposed = true;
+        }
+
+        internal void ThrowIfDisposed()
+        {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(_disposedName);
+            }
         }
     }
 }
index 8e6a66e..ffa0ac7 100644 (file)
@@ -8,6 +8,8 @@ namespace System.Security.Cryptography
 {
     public sealed partial class DSACng : DSA
     {
+        private CngAlgorithmCore _core = new CngAlgorithmCore(nameof(DSACng));
+
         /// <summary>
         ///     Creates a new DSACng object that will use the specified key. The key's
         ///     <see cref="CngKey.AlgorithmGroup" /> must be Dsa. This constructor
@@ -33,6 +35,9 @@ namespace System.Security.Cryptography
             _core.Dispose();
         }
 
-        private CngAlgorithmCore _core;
+        private void ThrowIfDisposed()
+        {
+            _core.ThrowIfDisposed();
+        }
     }
 }
index 9da4304..f2909c5 100644 (file)
@@ -21,7 +21,7 @@ namespace System.Security.Cryptography
     /// </summary>
     public sealed partial class ECDiffieHellmanCng : ECDiffieHellman
     {
-        private CngAlgorithmCore _core = new CngAlgorithmCore { DefaultKeyType = CngAlgorithm.ECDiffieHellman };
+        private CngAlgorithmCore _core = new CngAlgorithmCore(nameof(ECDiffieHellmanCng)) { DefaultKeyType = CngAlgorithm.ECDiffieHellman };
         private CngAlgorithm _hashAlgorithm = CngAlgorithm.Sha256;
         private ECDiffieHellmanKeyDerivationFunction _kdf = ECDiffieHellmanKeyDerivationFunction.Hash;
         private byte[] _hmacKey;
@@ -141,6 +141,11 @@ namespace System.Security.Cryptography
             _core.Dispose();
         }
 
+        private void ThrowIfDisposed()
+        {
+            _core.ThrowIfDisposed();
+        }
+
         private void DisposeKey()
         {
             _core.DisposeKey();
index 52d679d..563cb6f 100644 (file)
@@ -11,6 +11,7 @@ namespace System.Security.Cryptography
     {
         private CngKeyBlobFormat _format;
         private string _curveName;
+        private bool _disposed;
 
         /// <summary>
         /// Wrap a CNG key
@@ -24,6 +25,11 @@ namespace System.Security.Cryptography
 
         protected override void Dispose(bool disposing)
         {
+            if (disposing)
+            {
+                _disposed = true;
+            }
+
             base.Dispose(disposing);
         }
 
@@ -84,6 +90,11 @@ namespace System.Security.Cryptography
         /// <returns></returns>
         public CngKey Import()
         {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(nameof(ECDiffieHellmanCngPublicKey));
+            }
+
             return CngKey.Import(ToByteArray(), _curveName, BlobFormat);
         }
 
index 4a148fb..61192c8 100644 (file)
@@ -9,7 +9,7 @@ namespace System.Security.Cryptography
 {
     public sealed partial class ECDsaCng : ECDsa
     {
-        private CngAlgorithmCore _core;
+        private CngAlgorithmCore _core = new CngAlgorithmCore(nameof(ECDsaCng));
         private CngAlgorithm _hashAlgorithm = CngAlgorithm.Sha256;
 
         /// <summary>
@@ -52,6 +52,11 @@ namespace System.Security.Cryptography
             _core.Dispose();
         }
 
+        private void ThrowIfDisposed()
+        {
+            _core.ThrowIfDisposed();
+        }
+
         private void DisposeKey()
         {
             _core.DisposeKey();
index 38fb16e..f393ec7 100644 (file)
@@ -2,15 +2,14 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
-using System.Diagnostics;
-
 using Internal.Cryptography;
 
 namespace System.Security.Cryptography
 {
     public sealed partial class RSACng : RSA
     {
+        private CngAlgorithmCore _core = new CngAlgorithmCore(nameof(RSACng));
+
         /// <summary>
         ///     Creates a new RSACng object that will use the specified key. The key's
         ///     <see cref="CngKey.AlgorithmGroup" /> must be Rsa. This constructor
@@ -36,6 +35,9 @@ namespace System.Security.Cryptography
             _core.Dispose();
         }
 
-        private CngAlgorithmCore _core;
+        private void ThrowIfDisposed()
+        {
+            _core.ThrowIfDisposed();
+        }
     }
 }
index 02b35e7..d2b7473 100644 (file)
@@ -119,6 +119,22 @@ namespace System.Security.Cryptography
             _publicOnly = (parameters.X == null);
         }
 
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<byte> passwordBytes,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            _impl.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+        }
+
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<char> password,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            _impl.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+        }
+
         public override string KeyExchangeAlgorithm => _impl.KeyExchangeAlgorithm;
 
         public override int KeySize
index 015cff1..5b35d9f 100644 (file)
@@ -5,7 +5,6 @@
 using System.Diagnostics;
 using System.IO;
 using Internal.NativeCrypto;
-using Microsoft.Win32.SafeHandles;
 
 namespace System.Security.Cryptography
 {
@@ -18,6 +17,7 @@ namespace System.Security.Cryptography
         private SafeProvHandle _safeProvHandle;
         private SHA1 _sha1;
         private static volatile CspProviderFlags s_useMachineKeyStore = 0;
+        private bool _disposed;
 
         /// <summary>
         /// Initializes a new instance of the DSACryptoServiceProvider class.
@@ -268,17 +268,22 @@ namespace System.Security.Cryptography
 
         protected override void Dispose(bool disposing)
         {
-            base.Dispose(disposing);
-
-            if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
+            if (disposing)
             {
-                _safeKeyHandle.Dispose();
-            }
+                if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
+                {
+                    _safeKeyHandle.Dispose();
+                }
 
-            if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
-            {
-                _safeProvHandle.Dispose();
+                if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
+                {
+                    _safeProvHandle.Dispose();
+                }
+
+                _disposed = true;
             }
+
+            base.Dispose(disposing);
         }
 
         /// <summary>
@@ -325,6 +330,8 @@ namespace System.Security.Cryptography
         /// <param name="keyBlob">A byte array that represents a DSA key blob.</param>
         public void ImportCspBlob(byte[] keyBlob)
         {
+            ThrowIfDisposed();
+
             SafeKeyHandle safeKeyHandle;
 
             if (IsPublic(keyBlob))
@@ -350,6 +357,24 @@ namespace System.Security.Cryptography
             ImportCspBlob(keyBlob);
         }
 
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<byte> passwordBytes,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+        }
+
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<char> password,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+        }
+
         /// <summary>
         /// Computes the hash value of the specified input stream and signs the resulting hash value.
         /// </summary>
@@ -522,5 +547,13 @@ namespace System.Security.Cryptography
 
             return true;
         }
+
+        private void ThrowIfDisposed()
+        {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(nameof(DSACryptoServiceProvider));
+            }
+        }
     }
 }
index 8efc0dd..203ec0d 100644 (file)
@@ -151,6 +151,22 @@ namespace System.Security.Cryptography
             _publicOnly = (parameters.P == null || parameters.P.Length == 0);
         }
 
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<byte> passwordBytes,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            _impl.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+        }
+
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<char> password,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            _impl.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+        }
+
         public override string KeyExchangeAlgorithm => _impl.KeyExchangeAlgorithm;
 
         public override int KeySize
index bc256af..3b060ea 100644 (file)
@@ -19,6 +19,7 @@ namespace System.Security.Cryptography
         private SafeKeyHandle _safeKeyHandle;
         private SafeProvHandle _safeProvHandle;
         private static volatile CspProviderFlags s_useMachineKeyStore = 0;
+        private bool _disposed;
 
         public RSACryptoServiceProvider()
             : this(0, new CspParameters(CapiHelper.DefaultRsaProviderType,
@@ -288,15 +289,19 @@ namespace System.Security.Cryptography
         /// </summary>
         protected override void Dispose(bool disposing)
         {
-            base.Dispose(disposing);
-
-            if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
-            {
-                _safeKeyHandle.Dispose();
-            }
-            if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
+            if (disposing)
             {
-                _safeProvHandle.Dispose();
+                if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
+                {
+                    _safeKeyHandle.Dispose();
+                }
+
+                if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
+                {
+                    _safeProvHandle.Dispose();
+                }
+
+                _disposed = true;
             }
         }
 
@@ -375,6 +380,7 @@ namespace System.Security.Cryptography
         /// <param name="keyBlob"></param>
         public void ImportCspBlob(byte[] keyBlob)
         {
+            ThrowIfDisposed();
             SafeKeyHandle safeKeyHandle;
 
             if (IsPublic(keyBlob))
@@ -403,6 +409,24 @@ namespace System.Security.Cryptography
             ImportCspBlob(keyBlob);
         }
 
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<byte> passwordBytes,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead);
+        }
+
+        public override void ImportEncryptedPkcs8PrivateKey(
+            ReadOnlySpan<char> password,
+            ReadOnlySpan<byte> source,
+            out int bytesRead)
+        {
+            ThrowIfDisposed();
+            base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead);
+        }
+
         /// <summary>
         /// Computes the hash value of a subset of the specified byte array using the
         /// specified hash algorithm, and signs the resulting hash value.
@@ -727,5 +751,13 @@ namespace System.Security.Cryptography
         {
             return new ArgumentException(SR.Cryptography_HashAlgorithmNameNullOrEmpty, "hashAlgorithm");
         }
+
+        private void ThrowIfDisposed()
+        {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException(nameof(DSACryptoServiceProvider));
+            }
+        }
     }
 }
index b3535fe..c49f73d 100644 (file)
@@ -10,6 +10,8 @@ namespace System.Security.Cryptography
     {
         public DSAOpenSsl(DSAParameters parameters)
         {
+            // Make _key be non-null before calling ImportParameters
+            _key = new Lazy<SafeDsaHandle>();
             ImportParameters(parameters);
         }
 
index 2665b0e..adef23f 100644 (file)
@@ -10,6 +10,8 @@ namespace System.Security.Cryptography
     {
         public RSAOpenSsl(RSAParameters parameters)
         {
+            // Make _key be non-null before calling ImportParameters
+            _key = new Lazy<SafeRsaHandle>();
             ImportParameters(parameters);
         }
 
index d94b2dc..98e96fb 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Security.Cryptography.Dsa.Tests;
 using Test.Cryptography;
 using Xunit;
 
@@ -97,5 +98,16 @@ namespace System.Security.Cryptography.OpenSsl.Tests
                 Assert.ThrowsAny<CryptographicException>(() => new DSAOpenSsl(pkey));
             }
         }
+
+        [Fact]
+        public static void VerifyParameterCtor()
+        {
+            using (DSA dsa = new DSAOpenSsl(DSATestData.Dsa576Parameters))
+            {
+                DSAImportExport.AssertKeyEquals(
+                    DSATestData.Dsa576Parameters,
+                    dsa.ExportParameters(true));
+            }
+        }
     }
 }
index e971377..abf79c8 100644 (file)
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Security.Cryptography.Rsa.Tests;
 using Test.Cryptography;
 using Xunit;
 
@@ -97,5 +98,14 @@ namespace System.Security.Cryptography.OpenSsl.Tests
                 Assert.ThrowsAny<CryptographicException>(() => new RSAOpenSsl(pkey));
             }
         }
+
+        [Fact]
+        public static void VerifyParameterCtor()
+        {
+            using (RSA rsa = new RSAOpenSsl(TestData.RSA1032Parameters))
+            {
+                ImportExport.AssertKeyEquals(TestData.RSA1032Parameters, rsa.ExportParameters(true));
+            }
+        }
     }
 }