From 8e1eaebc582c0a2e42643a55fb51a3b0d868f7c3 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Mon, 17 Jun 2019 18:25:56 -0700 Subject: [PATCH] Have consistent Dispose behaviors for AsymmetricAlgorithm objects 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 --- .../Security/Cryptography/DSACng.ImportExport.cs | 18 +++++ .../src/System/Security/Cryptography/DSAOpenSsl.cs | 61 ++++++++++++-- .../Security/Cryptography/DSASecurityTransforms.cs | 50 +++++++++++- .../ECDiffieHellmanCng.ImportExport.cs | 5 ++ .../Cryptography/ECDiffieHellmanOpenSsl.Derive.cs | 7 +- .../Cryptography/ECDiffieHellmanOpenSsl.cs | 63 +++++++++++++-- .../ECDiffieHellmanOpenSslPublicKey.cs | 40 +++++++-- .../ECDiffieHellmanSecurityTransforms.cs | 64 ++++++++------- .../Security/Cryptography/ECDsaCng.ImportExport.cs | 8 ++ .../System/Security/Cryptography/ECDsaOpenSsl.cs | 68 +++++++++++++--- .../Cryptography/ECDsaSecurityTransforms.cs | 31 ++++++- .../Security/Cryptography/EccSecurityTransforms.cs | 36 ++++++++- .../Security/Cryptography/RSACng.ImportExport.cs | 6 ++ .../src/System/Security/Cryptography/RSAOpenSsl.cs | 73 ++++++++++++----- .../Security/Cryptography/RSASecurityTransforms.cs | 94 +++++++++++++++++++++- .../DSA/DSAImportExport.cs | 25 ++++++ .../DSA/DSAKeyFileTests.cs | 59 ++++++++++++++ .../AlgorithmImplementations/DSA/DSASignVerify.cs | 69 +++++++++++++++- .../AlgorithmImplementations/EC/ECKeyFileTests.cs | 60 ++++++++++++++ .../AlgorithmImplementations/EC/EccTestData.cs | 2 - .../ECDiffieHellman/ECDiffieHellmanTests.cs | 47 +++++++++++ .../AlgorithmImplementations/ECDsa/ECDsaTests.cs | 66 +++++++++++++++ .../ECDsa/ECDsaTests.netcoreapp.cs | 9 +++ .../AlgorithmImplementations/RSA/EncryptDecrypt.cs | 18 +++++ .../AlgorithmImplementations/RSA/ImportExport.cs | 22 +++++ .../RSA/RSAKeyFileTests.cs | 59 ++++++++++++++ .../AlgorithmImplementations/RSA/SignVerify.cs | 31 +++++++ .../ref/System.Security.Cryptography.Algorithms.cs | 32 ++++++++ .../src/System/Security/Cryptography/DSACng.cs | 28 +++++++ .../src/System/Security/Cryptography/ECCngKey.cs | 24 +++++- .../Cryptography/ECDiffieHellmanCng.Key.cs | 2 +- .../Security/Cryptography/ECDiffieHellmanCng.cs | 15 ++++ .../Cryptography/ECDiffieHellmanCngPublicKey.cs | 11 +++ .../System/Security/Cryptography/ECDsaCng.Key.cs | 2 +- .../src/System/Security/Cryptography/ECDsaCng.cs | 15 ++++ .../src/System/Security/Cryptography/RSACng.cs | 28 +++++++ .../src/Internal/Cryptography/CngAlgorithmCore.cs | 22 +++++ .../src/System/Security/Cryptography/DSACng.cs | 7 +- .../Security/Cryptography/ECDiffieHellmanCng.cs | 7 +- .../Cryptography/ECDiffieHellmanCngPublicKey.cs | 11 +++ .../src/System/Security/Cryptography/ECDsaCng.cs | 7 +- .../src/System/Security/Cryptography/RSACng.cs | 10 ++- .../tests/Configurations.props | 2 +- .../Cryptography/DSACryptoServiceProvider.Unix.cs | 16 ++++ .../DSACryptoServiceProvider.Windows.cs | 51 +++++++++--- .../Cryptography/RSACryptoServiceProvider.Unix.cs | 16 ++++ .../RSACryptoServiceProvider.Windows.cs | 48 +++++++++-- .../src/System/Security/Cryptography/DSAOpenSsl.cs | 2 + .../src/System/Security/Cryptography/RSAOpenSsl.cs | 2 + .../tests/DsaOpenSslTests.cs | 12 +++ .../tests/RsaOpenSslTests.cs | 10 +++ 51 files changed, 1346 insertions(+), 125 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs index 8bed7de..748302f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSACng.ImportExport.cs @@ -59,6 +59,24 @@ namespace System.Security.Cryptography ImportKeyBlob(blob, hasPrivateKey); } + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + public override byte[] ExportEncryptedPkcs8PrivateKey( ReadOnlySpan passwordBytes, PbeParameters pbeParameters) diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/DSAOpenSsl.cs index f4be1e7..28bc574 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSAOpenSsl.cs @@ -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(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(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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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 hash, Span 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 hash, ReadOnlySpan 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)); diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs index a5d796c..12a4207 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs @@ -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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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 source, out int bytesRead) { + ThrowIfDisposed(); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { using (MemoryManager manager = new PointerMemoryManager(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(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs index 92e2827..2d46606 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs @@ -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 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 source, out int bytesRead) { + ThrowIfDisposed(); CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( passwordBytes, source, @@ -117,6 +121,7 @@ namespace System.Security.Cryptography ReadOnlySpan source, out int bytesRead) { + ThrowIfDisposed(); CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( password, source, diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs index 8de7899..30a81b6 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs @@ -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, diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs index bf007e6..3be1be4 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs @@ -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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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 } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs index 5ecc47f..3bde2dd 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs @@ -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 } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs index 1b7f928..a190913 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs @@ -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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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; } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs index c229a14..ade3e6f 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs @@ -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 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 source, out int bytesRead) { + ThrowIfDisposed(); + CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( passwordBytes, source, @@ -127,6 +133,8 @@ namespace System.Security.Cryptography ReadOnlySpan source, out int bytesRead) { + ThrowIfDisposed(); + CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( password, source, diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs index 4f5d488..a87059a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs @@ -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 /// Size of the key to generate, in bits. 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); } /// @@ -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 hash, Span destination, out int bytesWritten) { + ThrowIfDisposed(); SafeEcKeyHandle key = _key.Value; byte[] converted; @@ -135,6 +136,8 @@ namespace System.Security.Cryptography public override bool VerifyHash(ReadOnlySpan hash, ReadOnlySpan 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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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 } diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs index c306066..42bf959 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs @@ -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 hash, ReadOnlySpan 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 source, Span 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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + public override void GenerateKey(ECCurve curve) { KeySizeValue = _ecc.GenerateKey(curve); diff --git a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs index bd2f4a9..2bf2bc2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs @@ -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 source, out int bytesRead) { + ThrowIfDisposed(); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs index cc01870..53d022a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSACng.ImportExport.cs @@ -112,6 +112,8 @@ namespace System.Security.Cryptography public override void ImportPkcs8PrivateKey(ReadOnlySpan 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 source, out int bytesRead) { + ThrowIfDisposed(); + CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( passwordBytes, source, @@ -137,6 +141,8 @@ namespace System.Security.Cryptography ReadOnlySpan source, out int bytesRead) { + ThrowIfDisposed(); + CngPkcs8.Pkcs8Response response = CngPkcs8.ImportEncryptedPkcs8PrivateKey( password, source, diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs index 88092e2..6741d28 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -37,7 +37,7 @@ namespace System.Security.Cryptography public RSAOpenSsl(int keySize) { - KeySize = keySize; + base.KeySize = keySize; _key = new Lazy(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(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 source, out int bytesRead) { + ThrowIfDisposed(); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) @@ -466,11 +464,30 @@ namespace System.Security.Cryptography } } + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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); diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs index f93f015..50b3b69 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs @@ -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 source, out int bytesRead) { + ThrowIfDisposed(); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) @@ -209,6 +215,8 @@ namespace System.Security.Cryptography public override unsafe void ImportRSAPublicKey(ReadOnlySpan source, out int bytesRead) { + ThrowIfDisposed(); + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) { using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) @@ -237,6 +245,24 @@ namespace System.Security.Cryptography } } + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan 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() => diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs index 129ec67..46e99d05 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs @@ -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(() => key.ExportParameters(false)); + Assert.Throws(() => key.ExportParameters(true)); + Assert.Throws(() => key.ImportParameters(DSATestData.GetDSA1024Params())); + } + internal static void AssertKeyEquals(in DSAParameters expected, in DSAParameters actual) { Assert.Equal(expected.G, actual.G); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs index eab4c28..b211d14 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs @@ -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(() => key.ImportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _)); + + Assert.Throws(() => key.ExportPkcs8PrivateKey()); + Assert.Throws(() => key.TryExportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters)); + Assert.Throws(() => key.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters)); + Assert.Throws(() => key.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ExportSubjectPublicKeyInfo()); + Assert.Throws(() => 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(); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + } + [Fact] public static void ReadWriteDsa512Pkcs8() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs index 1bb8a31..4548cef 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs @@ -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(() => dsa.CreateSignature(hash)); + Assert.Throws(() => 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)data, (ReadOnlySpan)signature, hashAlgorithm); + protected override void UseAfterDispose(DSA dsa, byte[] data, byte[] sig) + { + base.UseAfterDispose(dsa, data, sig); + byte[] hash = new byte[20]; + + Assert.Throws(() => dsa.TryCreateSignature(hash, sig, out _)); + Assert.Throws(() => dsa.VerifySignature(hash.AsSpan(), sig.AsSpan())); + } + private static byte[] TryWithOutputArray(Func 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(() => key.ImportParameters(DSATestData.GetDSA1024Params())); + + // Either set_KeySize or SignData should throw. + Assert.Throws( + () => + { + key.KeySize = 576; + SignData(key, data, HashAlgorithmName.SHA1); + }); + } + + protected virtual void UseAfterDispose(DSA dsa, byte[] data, byte[] sig) + { + Assert.Throws( + () => SignData(dsa, data, HashAlgorithmName.SHA1)); + + Assert.Throws( + () => VerifyData(dsa, data, sig, HashAlgorithmName.SHA1)); + } + + [ConditionalFact(nameof(SupportsKeyGeneration))] public void SignAndVerifyDataNew1024() { using (DSA dsa = DSAFactory.Create(1024)) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs index 071d5f7..cfe28d9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs @@ -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(() => ImportECPrivateKey(key, ecPrivate, out _)); + Assert.Throws(() => key.ImportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _)); + + Assert.Throws(() => ExportECPrivateKey(key)); + Assert.Throws(() => TryExportECPrivateKey(key, ecPrivate, out _)); + Assert.Throws(() => key.ExportPkcs8PrivateKey()); + Assert.Throws(() => key.TryExportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => key.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters)); + Assert.Throws(() => key.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters)); + Assert.Throws(() => key.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ExportSubjectPublicKeyInfo()); + Assert.Throws(() => 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(); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => key.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + } + [Fact] public void ReadWriteNistP521Pkcs8() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/EccTestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/EccTestData.cs index c864787..e5c91e6 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/EccTestData.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/EccTestData.cs @@ -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 } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs index 56760dc..dc0d0fd 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs @@ -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(() => key.DeriveKeyFromHash(pubKey, hash)); + Assert.Throws(() => key.DeriveKeyFromHmac(pubKey, hash, null)); + Assert.Throws(() => key.DeriveKeyFromHmac(pubKey, hash, new byte[3])); + Assert.Throws(() => key.DeriveKeyTls(pubKey, new byte[4], new byte[64])); + + pubKey = key.PublicKey; + } + + key.Dispose(); + + Assert.Throws(() => key.DeriveKeyFromHash(pubKey, hash)); + Assert.Throws(() => key.DeriveKeyFromHmac(pubKey, hash, null)); + Assert.Throws(() => key.DeriveKeyFromHmac(pubKey, hash, new byte[3])); + Assert.Throws(() => key.DeriveKeyTls(pubKey, new byte[4], new byte[64])); + Assert.Throws(() => key.GenerateKey(ECCurve.NamedCurves.nistP256)); + Assert.Throws(() => key.ImportParameters(EccTestData.GetNistP256ReferenceKey())); + + // Either set_KeySize or the ExportParameters should throw. + Assert.Throws( + () => + { + key.KeySize = 384; + key.ExportParameters(false); + }); + } + #if netcoreapp private static ECDiffieHellman OpenKnownKey() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs index 899b907..b6773b8 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs @@ -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(() => ecdsa.VerifyHash(hash, sig)); + Assert.Throws(() => 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(() => ecdsa.GenerateKey(ECCurve.NamedCurves.nistP256)); + + Assert.Throws( + () => ecdsa.ImportParameters(EccTestData.GetNistP256ReferenceKey())); + } + + // Either set_KeySize or SignData should throw. + Assert.Throws( + () => + { + ecdsa.KeySize = 384; + SignData(ecdsa, data, HashAlgorithmName.SHA256); + }); + } + + protected virtual void UseAfterDispose(ECDsa ecdsa, byte[] data, byte[] sig) + { + Assert.Throws( + () => SignData(ecdsa, data, HashAlgorithmName.SHA256)); + + Assert.Throws( + () => VerifyData(ecdsa, data, sig, HashAlgorithmName.SHA256)); + } + + [Theory] + [MemberData(nameof(RealImplementations))] public void SignData_MaxOffset_ZeroLength_NoThrow(ECDsa ecdsa) { // Explicitly larger than Array.Empty diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs index 7a0c55e..faa8ec1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.netcoreapp.cs @@ -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(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(() => ecdsa.VerifyHash(hash.AsSpan(), sig.AsSpan())); + Assert.Throws(() => ecdsa.TrySignHash(hash, sig, out _)); + } + [Theory, MemberData(nameof(RealImplementations))] public void SignData_InvalidArguments_Throws(ECDsa ecdsa) { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs index 513f28e..01ed1a3 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs @@ -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( + () => Decrypt(rsa, enc, RSAEncryptionPadding.Pkcs1)); + } + [Fact] public void DecryptSavedAnswer() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs index 65807a6..77f7be7 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs @@ -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(() => rsa.ExportParameters(false)); + Assert.Throws(() => rsa.ExportParameters(true)); + + if (!(PlatformDetection.IsFullFramework && rsa.GetType().Name.EndsWith("Cng"))) + { + Assert.Throws(() => rsa.ImportParameters(TestData.RSA1024Params)); + } + } + internal static void AssertKeyEquals(in RSAParameters expected, in RSAParameters actual) { Assert.Equal(expected.Modulus, actual.Modulus); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs index fa9a3984..8de550b 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs @@ -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(() => rsa.ImportRSAPublicKey(pkcs1Public, out _)); + Assert.Throws(() => rsa.ImportRSAPrivateKey(pkcs1Private, out _)); + Assert.Throws(() => rsa.ImportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => rsa.ImportEncryptedPkcs8PrivateKey(pwStr, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => rsa.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => rsa.ImportSubjectPublicKeyInfo(subjectPublicKeyInfo, out _)); + + Assert.Throws(() => rsa.ExportRSAPublicKey()); + Assert.Throws(() => rsa.TryExportRSAPublicKey(pkcs1Public, out _)); + Assert.Throws(() => rsa.ExportRSAPrivateKey()); + Assert.Throws(() => rsa.TryExportRSAPrivateKey(pkcs1Private, out _)); + Assert.Throws(() => rsa.ExportPkcs8PrivateKey()); + Assert.Throws(() => rsa.TryExportPkcs8PrivateKey(pkcs8Private, out _)); + Assert.Throws(() => rsa.ExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters)); + Assert.Throws(() => rsa.TryExportEncryptedPkcs8PrivateKey(pwStr, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => rsa.ExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters)); + Assert.Throws(() => rsa.TryExportEncryptedPkcs8PrivateKey(pwBytes, pbeParameters, pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => rsa.ExportSubjectPublicKeyInfo()); + Assert.Throws(() => 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(); + Assert.Throws(() => rsa.ImportEncryptedPkcs8PrivateKey("", pkcs8EncryptedPrivate, out _)); + Assert.Throws(() => rsa.ImportEncryptedPkcs8PrivateKey(pwBytes, pkcs8EncryptedPrivate, out _)); + } + [ConditionalFact(nameof(SupportsLargeExponent))] public static void ReadWriteBigExponentPrivatePkcs1() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs index dbb516b..882911f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/SignVerify.cs @@ -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( + () => VerifyData(rsa, sig, data, alg, padding)); + + Assert.Throws( + () => VerifyHash(rsa, sig, data, alg, padding)); + + // Either set_KeySize or SignData should throw. + Assert.Throws( + () => + { + rsa.KeySize = 1024 + 64; + SignData(rsa, data, alg, padding); + }); + } + [Fact] public void InvalidKeySize_DoesNotInvalidateKey() { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index 75d2462..7c8a86f 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -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 passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } public abstract void ImportParameters(System.Security.Cryptography.DSAParameters parameters); + public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan 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 hash, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } protected virtual bool TryHashData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; } public virtual bool TrySignData(System.ReadOnlySpan data, System.Span 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 source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { } + public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } public override string ToXmlString(bool includePrivateParameters) { throw null; } public virtual bool TryExportECPrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportSubjectPublicKeyInfo(System.Span 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 source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportParameters(System.Security.Cryptography.ECParameters parameters) { } + public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan 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 destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } protected virtual bool TryHashData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; } public virtual bool TrySignData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; } public virtual bool TrySignHash(System.ReadOnlySpan hash, System.Span 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 passwordBytes, System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.ReadOnlySpan source, out int bytesRead) { throw null; } public abstract void ImportParameters(System.Security.Cryptography.RSAParameters parameters); + public override void ImportPkcs8PrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportRSAPrivateKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } public virtual void ImportRSAPublicKey(System.ReadOnlySpan source, out int bytesRead) { throw null; } + public override void ImportSubjectPublicKeyInfo(System.ReadOnlySpan 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 data, System.Span destination, System.Security.Cryptography.RSAEncryptionPadding padding, out int bytesWritten) { throw null; } public virtual bool TryEncrypt(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.RSAEncryptionPadding padding, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportEncryptedPkcs8PrivateKey(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; } public virtual bool TryExportRSAPrivateKey(System.Span destination, out int bytesWritten) { throw null; } public virtual bool TryExportRSAPublicKey(System.Span destination, out int bytesWritten) { throw null; } + public override bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } protected virtual bool TryHashData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, out int bytesWritten) { throw null; } public virtual bool TrySignData(System.ReadOnlySpan data, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding, out int bytesWritten) { throw null; } public virtual bool TrySignHash(System.ReadOnlySpan hash, System.Span destination, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding, out int bytesWritten) { throw null; } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSACng.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSACng.cs index ec06de8..6c05638 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSACng.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSACng.cs @@ -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); + } } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECCngKey.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECCngKey.cs index 21e8e01..47dc3a2 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECCngKey.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECCngKey.cs @@ -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); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.Key.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.Key.cs index 0225a96..2210641 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.Key.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.Key.cs @@ -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); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.cs index 830f1dc..ba31142 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCng.cs @@ -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 ? diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs index 4f2e283..725e807 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs @@ -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 /// The key and explicit curve parameters used by the ECC object. 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 /// The key and named curve parameters used by the ECC object. public override ECParameters ExportParameters() { + if (_keyBlob == null) + { + throw new ObjectDisposedException(nameof(ECDiffieHellmanPublicKey)); + } + if (string.IsNullOrEmpty(_curveName)) { return ExportExplicitParameters(); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.Key.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.Key.cs index 2f38478..ca4f372 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.Key.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.Key.cs @@ -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); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.cs index e90339f..afc1421 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDsaCng.cs @@ -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 ? diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSACng.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSACng.cs index e6f3571..614ea7a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSACng.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSACng.cs @@ -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); + } } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs index 43ae9aa..72bd377 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngAlgorithmCore.cs @@ -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); + } } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/DSACng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/DSACng.cs index 8e6a66e..ffa0ac7 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/DSACng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/DSACng.cs @@ -8,6 +8,8 @@ namespace System.Security.Cryptography { public sealed partial class DSACng : DSA { + private CngAlgorithmCore _core = new CngAlgorithmCore(nameof(DSACng)); + /// /// Creates a new DSACng object that will use the specified key. The key's /// must be Dsa. This constructor @@ -33,6 +35,9 @@ namespace System.Security.Cryptography _core.Dispose(); } - private CngAlgorithmCore _core; + private void ThrowIfDisposed() + { + _core.ThrowIfDisposed(); + } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCng.cs index 9da4304..f2909c5 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCng.cs @@ -21,7 +21,7 @@ namespace System.Security.Cryptography /// 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(); diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs index 52d679d..563cb6f 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDiffieHellmanCngPublicKey.cs @@ -11,6 +11,7 @@ namespace System.Security.Cryptography { private CngKeyBlobFormat _format; private string _curveName; + private bool _disposed; /// /// 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 /// public CngKey Import() { + if (_disposed) + { + throw new ObjectDisposedException(nameof(ECDiffieHellmanCngPublicKey)); + } + return CngKey.Import(ToByteArray(), _curveName, BlobFormat); } diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDsaCng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDsaCng.cs index 4a148fb..61192c8 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDsaCng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/ECDsaCng.cs @@ -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; /// @@ -52,6 +52,11 @@ namespace System.Security.Cryptography _core.Dispose(); } + private void ThrowIfDisposed() + { + _core.ThrowIfDisposed(); + } + private void DisposeKey() { _core.DisposeKey(); diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/RSACng.cs b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/RSACng.cs index 38fb16e..f393ec7 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/RSACng.cs +++ b/src/libraries/System.Security.Cryptography.Cng/src/System/Security/Cryptography/RSACng.cs @@ -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)); + /// /// Creates a new RSACng object that will use the specified key. The key's /// must be Rsa. This constructor @@ -36,6 +35,9 @@ namespace System.Security.Cryptography _core.Dispose(); } - private CngAlgorithmCore _core; + private void ThrowIfDisposed() + { + _core.ThrowIfDisposed(); + } } } diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/Configurations.props b/src/libraries/System.Security.Cryptography.Cng/tests/Configurations.props index dd6fb5e..bde6cac 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/Configurations.props +++ b/src/libraries/System.Security.Cryptography.Cng/tests/Configurations.props @@ -6,4 +6,4 @@ uap-Windows_NT; - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Unix.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Unix.cs index 02b35e7..d2b7473 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Unix.cs @@ -119,6 +119,22 @@ namespace System.Security.Cryptography _publicOnly = (parameters.X == null); } + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + _impl.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + _impl.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + public override string KeyExchangeAlgorithm => _impl.KeyExchangeAlgorithm; public override int KeySize diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Windows.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Windows.cs index 015cff1..5b35d9f 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/DSACryptoServiceProvider.Windows.cs @@ -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; /// /// 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); } /// @@ -325,6 +330,8 @@ namespace System.Security.Cryptography /// A byte array that represents a DSA key blob. 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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + /// /// Computes the hash value of the specified input stream and signs the resulting hash value. /// @@ -522,5 +547,13 @@ namespace System.Security.Cryptography return true; } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(DSACryptoServiceProvider)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Unix.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Unix.cs index 8efc0dd..203ec0d 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Unix.cs @@ -151,6 +151,22 @@ namespace System.Security.Cryptography _publicOnly = (parameters.P == null || parameters.P.Length == 0); } + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + _impl.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + _impl.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + public override string KeyExchangeAlgorithm => _impl.KeyExchangeAlgorithm; public override int KeySize diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs index bc256af..3b060ea 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/RSACryptoServiceProvider.Windows.cs @@ -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 /// 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 /// 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 passwordBytes, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(passwordBytes, source, out bytesRead); + } + + public override void ImportEncryptedPkcs8PrivateKey( + ReadOnlySpan password, + ReadOnlySpan source, + out int bytesRead) + { + ThrowIfDisposed(); + base.ImportEncryptedPkcs8PrivateKey(password, source, out bytesRead); + } + /// /// 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)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/DSAOpenSsl.cs b/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/DSAOpenSsl.cs index b3535fe..c49f73d 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/DSAOpenSsl.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/DSAOpenSsl.cs @@ -10,6 +10,8 @@ namespace System.Security.Cryptography { public DSAOpenSsl(DSAParameters parameters) { + // Make _key be non-null before calling ImportParameters + _key = new Lazy(); ImportParameters(parameters); } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/RSAOpenSsl.cs index 2665b0e..adef23f 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -10,6 +10,8 @@ namespace System.Security.Cryptography { public RSAOpenSsl(RSAParameters parameters) { + // Make _key be non-null before calling ImportParameters + _key = new Lazy(); ImportParameters(parameters); } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslTests.cs index d94b2dc..98e96fb 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslTests.cs @@ -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(() => new DSAOpenSsl(pkey)); } } + + [Fact] + public static void VerifyParameterCtor() + { + using (DSA dsa = new DSAOpenSsl(DSATestData.Dsa576Parameters)) + { + DSAImportExport.AssertKeyEquals( + DSATestData.Dsa576Parameters, + dsa.ExportParameters(true)); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/RsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/RsaOpenSslTests.cs index e971377..abf79c8 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/RsaOpenSslTests.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/RsaOpenSslTests.cs @@ -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(() => new RSAOpenSsl(pkey)); } } + + [Fact] + public static void VerifyParameterCtor() + { + using (RSA rsa = new RSAOpenSsl(TestData.RSA1032Parameters)) + { + ImportExport.AssertKeyEquals(TestData.RSA1032Parameters, rsa.ExportParameters(true)); + } + } } } -- 2.7.4