/// <summary> /// Create another instance of SafeEvpPKeyHandle which has an independent lifetime /// from this instance, but tracks the same resource. /// </summary> /// <returns>An equivalent SafeEvpPKeyHandle with a different lifetime</returns> public SafeEvpPKeyHandle DuplicateHandle() { if (IsInvalid) { throw new InvalidOperationException(SR.Cryptography_OpenInvalidHandle); } // Reliability: Allocate the SafeHandle before calling UpRefEvpPkey so // that we don't lose a tracked reference in low-memory situations. SafeEvpPKeyHandle safeHandle = new SafeEvpPKeyHandle(); int success = Interop.Crypto.UpRefEvpPkey(this); if (success != 1) { Debug.Fail("Called UpRefEvpPkey on a key which was already marked for destruction"); Exception e = Interop.Crypto.CreateOpenSslCryptographicException(); safeHandle.Dispose(); throw e; } // Since we didn't actually create a new handle, copy the handle // to the new SafeHandle. safeHandle.SetHandle(handle); return(safeHandle); }
internal SafeEvpPKeyHandle UpRefKeyHandle() { SafeEcKeyHandle currentKey = _key.Value; Debug.Assert(currentKey != null, "null TODO"); SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate(); try { // Wrapping our key in an EVP_PKEY will up_ref our key. // When the EVP_PKEY is Disposed it will down_ref the key. // So everything should be copacetic. if (!Interop.Crypto.EvpPkeySetEcKey(pkeyHandle, currentKey)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } return(pkeyHandle); } catch { pkeyHandle.Dispose(); throw; } }
/// <summary> /// Create an RSAOpenSsl from an <see cref="SafeEvpPKeyHandle"/> whose value is an existing /// OpenSSL <c>EVP_PKEY*</c> wrapping an <c>RSA*</c> /// </summary> /// <param name="pkeyHandle">A SafeHandle for an OpenSSL <c>EVP_PKEY*</c></param> /// <exception cref="ArgumentNullException"><paramref name="pkeyHandle"/> is <c>null</c></exception> /// <exception cref="ArgumentException"> /// <paramref name="pkeyHandle"/> <see cref="Runtime.InteropServices.SafeHandle.IsInvalid" /> /// </exception> /// <exception cref="CryptographicException"><paramref name="pkeyHandle"/> is not a valid enveloped <c>RSA*</c></exception> public RSAOpenSsl(SafeEvpPKeyHandle pkeyHandle) { if (pkeyHandle == null) { throw new ArgumentNullException(nameof(pkeyHandle)); } if (pkeyHandle.IsInvalid) { throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(pkeyHandle)); } SafeEvpPKeyHandle newKey = Interop.Crypto.EvpPkeyCreate(); using (SafeRsaHandle rsa = Interop.Crypto.EvpPkeyGetRsa(pkeyHandle)) { if (rsa.IsInvalid || !Interop.Crypto.EvpPkeySetRsa(newKey, rsa)) { newKey.Dispose(); throw Interop.Crypto.CreateOpenSslCryptographicException(); } } // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere // with the already loaded key. ForceSetKeySize(BitsPerByte * Interop.Crypto.EvpPKeySize(newKey)); _key = new Lazy <SafeEvpPKeyHandle>(newKey); }
public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } ValidatePadding(padding); SafeEvpPKeyHandle key = GetPKey(); int rsaSize = Interop.Crypto.EvpPKeySize(key); byte[]? buf = null; Span <byte> destination = default; try { buf = CryptoPool.Rent(rsaSize); destination = new Span <byte>(buf, 0, rsaSize); int bytesWritten = Decrypt(key, data, destination, padding); return(destination.Slice(0, bytesWritten).ToArray()); } finally { CryptographicOperations.ZeroMemory(destination); CryptoPool.Return(buf !, clearSize: 0); // Until EVP_PKEY is what gets stored, free the temporary key handle. key.Dispose(); } }
public override void ImportParameters(RSAParameters parameters) { ValidateParameters(ref parameters); ThrowIfDisposed(); SafeRsaHandle key = Interop.Crypto.RsaCreate(); SafeEvpPKeyHandle pkey = Interop.Crypto.EvpPkeyCreate(); bool imported = false; Interop.Crypto.CheckValidOpenSslHandle(key); try { if (!Interop.Crypto.SetRsaParameters( key, parameters.Modulus, parameters.Modulus != null ? parameters.Modulus.Length : 0, parameters.Exponent, parameters.Exponent != null ? parameters.Exponent.Length : 0, parameters.D, parameters.D != null ? parameters.D.Length : 0, parameters.P, parameters.P != null ? parameters.P.Length : 0, parameters.DP, parameters.DP != null ? parameters.DP.Length : 0, parameters.Q, parameters.Q != null ? parameters.Q.Length : 0, parameters.DQ, parameters.DQ != null ? parameters.DQ.Length : 0, parameters.InverseQ, parameters.InverseQ != null ? parameters.InverseQ.Length : 0)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } imported = true; } finally { if (!imported) { key.Dispose(); } } if (!Interop.Crypto.EvpPkeySetRsa(pkey, key)) { pkey.Dispose(); key.Dispose(); throw Interop.Crypto.CreateOpenSslCryptographicException(); } key.Dispose(); FreeKey(); _key = new Lazy <SafeEvpPKeyHandle>(pkey); // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere // with the already loaded key. ForceSetKeySize(BitsPerByte * Interop.Crypto.EvpPKeySize(pkey)); }
private void FreeKey() { if (_key != null && _key.IsValueCreated) { SafeEvpPKeyHandle handle = _key.Value; handle?.Dispose(); } }
/// <summary> /// Obtain a SafeHandle version of an EVP_PKEY* which wraps an RSA* equivalent /// to the current key for this instance. /// </summary> /// <returns>A SafeHandle for the RSA key in OpenSSL</returns> public SafeEvpPKeyHandle DuplicateKeyHandle() { SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate(); using (SafeRsaHandle rsa = Interop.Crypto.EvpPkeyGetRsa(GetKey())) { if (rsa.IsInvalid || !Interop.Crypto.EvpPkeySetRsa(pkeyHandle, rsa)) { pkeyHandle.Dispose(); throw Interop.Crypto.CreateOpenSslCryptographicException(); } } return(pkeyHandle); }
public override void ImportRSAPublicKey(ReadOnlySpan <byte> source, out int bytesRead) { ThrowIfDisposed(); int read; try { AsnDecoder.ReadEncodedValue( source, AsnEncodingRules.BER, out _, out _, out read); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } SafeEvpPKeyHandle pkey = Interop.Crypto.EvpPkeyCreate(); SafeRsaHandle key = Interop.Crypto.DecodeRsaPublicKey(source.Slice(0, read)); Interop.Crypto.CheckValidOpenSslHandle(key); if (!Interop.Crypto.EvpPkeySetRsa(pkey, key)) { key.Dispose(); pkey.Dispose(); throw Interop.Crypto.CreateOpenSslCryptographicException(); } key.Dispose(); FreeKey(); _key = new Lazy <SafeEvpPKeyHandle>(pkey); // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere // with the already loaded key. ForceSetKeySize(BitsPerByte * Interop.Crypto.EvpPKeySize(pkey)); bytesRead = read; }
/// <summary> /// Create an RSAOpenSsl from an existing <see cref="IntPtr"/> whose value is an /// existing OpenSSL <c>RSA*</c>. /// </summary> /// <remarks> /// This method will increase the reference count of the <c>RSA*</c>, the caller should /// continue to manage the lifetime of their reference. /// </remarks> /// <param name="handle">A pointer to an OpenSSL <c>RSA*</c></param> /// <exception cref="ArgumentException"><paramref name="handle" /> is invalid</exception> public RSAOpenSsl(IntPtr handle) { if (handle == IntPtr.Zero) { throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(handle)); } SafeEvpPKeyHandle pkey = Interop.Crypto.EvpPkeyCreate(); if (!Interop.Crypto.EvpPkeySetRsa(pkey, handle)) { pkey.Dispose(); throw Interop.Crypto.CreateOpenSslCryptographicException(); } // Use ForceSet instead of the property setter to ensure that LegalKeySizes doesn't interfere // with the already loaded key. ForceSetKeySize(BitsPerByte * Interop.Crypto.EvpPKeySize(pkey)); _key = new Lazy <SafeEvpPKeyHandle>(pkey); }
private bool TrySignHash( ReadOnlySpan <byte> hash, Span <byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, bool allocateSignature, out int bytesWritten, out byte[]?signature) { Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); Debug.Assert(padding != null); ValidatePadding(padding); signature = null; IntPtr digestAlgorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithm.Name); SafeEvpPKeyHandle key = GetPKey(); int bytesRequired = Interop.Crypto.EvpPKeySize(key); if (allocateSignature) { Debug.Assert(destination.Length == 0); signature = new byte[bytesRequired]; destination = signature; } else if (destination.Length < bytesRequired) { bytesWritten = 0; return(false); } int written = Interop.Crypto.RsaSignHash(key, padding.Mode, digestAlgorithm, hash, destination); Debug.Assert(written == bytesRequired); bytesWritten = written; // Until EVP_PKEY is what gets stored, free the temporary key handle. key.Dispose(); return(true); }
/// <summary> /// Obtain a SafeHandle version of an EVP_PKEY* which wraps an RSA* equivalent /// to the current key for this instance. /// </summary> /// <returns>A SafeHandle for the RSA key in OpenSSL</returns> public SafeEvpPKeyHandle DuplicateKeyHandle() { SafeRsaHandle currentKey = _key.Value; SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate(); try { // Wrapping our key in an EVP_PKEY will up_ref our key. // When the EVP_PKEY is Disposed it will down_ref the key. // So everything should be copacetic. if (!Interop.Crypto.EvpPkeySetRsa(pkeyHandle, currentKey)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } return(pkeyHandle); } catch { pkeyHandle.Dispose(); throw; } }
/// <summary> /// Get the secret agreement generated between two parties /// </summary> private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher) { Debug.Assert(otherPartyPublicKey != null); // Ensure that this ECDH object contains a private key by attempting a parameter export // which will throw an OpenSslCryptoException if no private key is available ECParameters thisKeyExplicit = ExportExplicitParameters(true); bool thisIsNamed = Interop.Crypto.EcKeyHasCurveName(_key.Value); ECDiffieHellmanOpenSslPublicKey?otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey; bool disposeOtherKey = false; if (otherKey == null) { disposeOtherKey = true; ECParameters otherParameters = thisIsNamed ? otherPartyPublicKey.ExportParameters() : otherPartyPublicKey.ExportExplicitParameters(); otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters); } bool otherIsNamed = otherKey.HasCurveName; SafeEvpPKeyHandle?ourKey = null; SafeEvpPKeyHandle?theirKey = null; byte[]? rented = null; int secretLength = 0; try { if (otherKey.KeySize != KeySize) { throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey)); } if (otherIsNamed == thisIsNamed) { ourKey = _key.UpRefKeyHandle(); theirKey = otherKey.DuplicateKeyHandle(); } else if (otherIsNamed) { ourKey = _key.UpRefKeyHandle(); using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters())) { theirKey = tmp.UpRefKeyHandle(); } } else { using (ECOpenSsl tmp = new ECOpenSsl(thisKeyExplicit)) { ourKey = tmp.UpRefKeyHandle(); } theirKey = otherKey.DuplicateKeyHandle(); } using (SafeEvpPKeyCtxHandle ctx = Interop.Crypto.EvpPKeyCtxCreate(ourKey, theirKey, out uint secretLengthU)) { if (ctx == null || ctx.IsInvalid || secretLengthU == 0 || secretLengthU > int.MaxValue) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } secretLength = (int)secretLengthU; // Indicate that secret can hold stackallocs from nested scopes Span <byte> secret = stackalloc byte[0]; // Arbitrary limit. But it covers secp521r1, which is the biggest common case. const int StackAllocMax = 66; if (secretLength > StackAllocMax) { rented = CryptoPool.Rent(secretLength); secret = new Span <byte>(rented, 0, secretLength); } else { secret = stackalloc byte[secretLength]; } Interop.Crypto.EvpPKeyDeriveSecretAgreement(ctx, secret); if (hasher == null) { return(secret.ToArray()); } else { hasher.AppendData(secret); return(null); } } } finally { theirKey?.Dispose(); ourKey?.Dispose(); if (disposeOtherKey) { otherKey.Dispose(); } if (rented != null) { CryptoPool.Return(rented, secretLength); } } }
public override bool TryDecrypt( ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { if (padding == null) { throw new ArgumentNullException(nameof(padding)); } ValidatePadding(padding); SafeEvpPKeyHandle key = GetPKey(); int keySizeBytes = Interop.Crypto.EvpPKeySize(key); // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size. // So if the destination is too small, use a temporary buffer so we can match // Windows behavior of succeeding so long as the buffer can hold the final output. if (destination.Length < keySizeBytes) { // RSA up through 4096 bits use a stackalloc Span <byte> tmp = stackalloc byte[512]; byte[]? rent = null; if (keySizeBytes > tmp.Length) { rent = CryptoPool.Rent(keySizeBytes); tmp = rent; } int written = Decrypt(key, data, tmp, padding); // Until EVP_PKEY is what gets stored, free the temporary key handle. key.Dispose(); bool ret; if (destination.Length < written) { bytesWritten = 0; ret = false; } else { tmp.Slice(0, written).CopyTo(destination); bytesWritten = written; ret = true; } // Whether a stackalloc or a rented array, clear our copy of // the decrypted content. CryptographicOperations.ZeroMemory(tmp.Slice(0, written)); if (rent != null) { // Already cleared. CryptoPool.Return(rent, clearSize: 0); } return(ret); } bytesWritten = Decrypt(key, data, destination, padding); // Until EVP_PKEY is what gets stored, free the temporary key handle. key.Dispose(); return(true); }