Exemple #1
0
        /// <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);
        }
Exemple #2
0
        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;
            }
        }
Exemple #3
0
        /// <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);
        }
Exemple #4
0
        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();
            }
        }
Exemple #5
0
        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));
        }
Exemple #6
0
 private void FreeKey()
 {
     if (_key != null && _key.IsValueCreated)
     {
         SafeEvpPKeyHandle handle = _key.Value;
         handle?.Dispose();
     }
 }
Exemple #7
0
        /// <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);
        }
Exemple #8
0
        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;
        }
Exemple #9
0
        /// <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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        /// <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;
            }
        }
Exemple #12
0
        /// <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);
                }
            }
        }
Exemple #13
0
        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);
        }