Esempio n. 1
0
        public override bool TryEncrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten)
        {
            ArgumentNullException.ThrowIfNull(padding);

            ValidatePadding(padding);
            SafeEvpPKeyHandle?key = GetKey();

            return(TryEncrypt(key, data, destination, padding, out bytesWritten));
        }
Esempio n. 2
0
        public SafeFreeSslCredentials(SslStreamCertificateContext?context, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
            : base(IntPtr.Zero, true)
        {
            Debug.Assert(
                context == null || context.Certificate is X509Certificate2,
                "Only X509Certificate2 certificates are supported at this time");

            X509Certificate2?cert = context?.Certificate;

            if (cert != null)
            {
                Debug.Assert(cert.HasPrivateKey, "cert.HasPrivateKey");

                using (RSAOpenSsl? rsa = (RSAOpenSsl?)cert.GetRSAPrivateKey())
                {
                    if (rsa != null)
                    {
                        _certKeyHandle = rsa.DuplicateKeyHandle();
                        Interop.Crypto.CheckValidOpenSslHandle(_certKeyHandle);
                    }
                }

                if (_certKeyHandle == null)
                {
                    using (ECDsaOpenSsl? ecdsa = (ECDsaOpenSsl?)cert.GetECDsaPrivateKey())
                    {
                        if (ecdsa != null)
                        {
                            _certKeyHandle = ecdsa.DuplicateKeyHandle();
                            Interop.Crypto.CheckValidOpenSslHandle(_certKeyHandle);
                        }
                    }
                }

                if (_certKeyHandle == null)
                {
                    throw new NotSupportedException(SR.net_ssl_io_no_server_cert);
                }

                _certHandle = Interop.Crypto.X509UpRef(cert.Handle);
                Interop.Crypto.CheckValidOpenSslHandle(_certHandle);
            }

            _protocols = protocols;
            _policy    = policy;
            _context   = context;
        }
Esempio n. 3
0
        protected override byte[] ExportPkcs8(
            ICertificatePalCore certificatePal,
            ReadOnlySpan <char> password)
        {
            AsymmetricAlgorithm?alg        = null;
            SafeEvpPKeyHandle?  privateKey = ((OpenSslX509CertificateReader)certificatePal).PrivateKeyHandle;

            try
            {
                alg = new RSAOpenSsl(privateKey !);
            }
            catch (CryptographicException)
            {
            }

            if (alg == null)
            {
                try
                {
                    alg = new ECDsaOpenSsl(privateKey !);
                }
                catch (CryptographicException)
                {
                }
            }

            if (alg == null)
            {
                try
                {
                    alg = new DSAOpenSsl(privateKey !);
                }
                catch (CryptographicException)
                {
                }
            }

            Debug.Assert(alg != null);
            return(alg.ExportEncryptedPkcs8PrivateKey(password, s_windowsPbe));
        }
Esempio n. 4
0
        internal static void UpdateClientCertiticate(SafeSslHandle ssl, SslAuthenticationOptions sslAuthenticationOptions)
        {
            // Disable certificate selection callback. We either got certificate or we will try to proceed without it.
            Interop.Ssl.SslSetClientCertCallback(ssl, 0);

            if (sslAuthenticationOptions.CertificateContext == null)
            {
                return;
            }

            var               credential    = new SafeFreeSslCredentials(sslAuthenticationOptions.CertificateContext, sslAuthenticationOptions.EnabledSslProtocols, sslAuthenticationOptions.EncryptionPolicy, sslAuthenticationOptions.IsServer);
            SafeX509Handle?   certHandle    = credential.CertHandle;
            SafeEvpPKeyHandle?certKeyHandle = credential.CertKeyHandle;

            Debug.Assert(certHandle != null);
            Debug.Assert(certKeyHandle != null);

            int retVal = Ssl.SslUseCertificate(ssl, certHandle);

            if (1 != retVal)
            {
                throw CreateSslException(SR.net_ssl_use_cert_failed);
            }

            retVal = Ssl.SslUsePrivateKey(ssl, certKeyHandle);
            if (1 != retVal)
            {
                throw CreateSslException(SR.net_ssl_use_private_key_failed);
            }

            if (sslAuthenticationOptions.CertificateContext.IntermediateCertificates.Length > 0)
            {
                if (!Ssl.AddExtraChainCertificates(ssl, sslAuthenticationOptions.CertificateContext.IntermediateCertificates))
                {
                    throw CreateSslException(SR.net_ssl_use_cert_failed);
                }
            }
        }
Esempio n. 5
0
        internal static SafeSslHandle AllocateSslContext(SslProtocols protocols, SafeX509Handle?certHandle, SafeEvpPKeyHandle?certKeyHandle, EncryptionPolicy policy, SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?context = null;

            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            using (SafeSslContextHandle innerContext = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method))
            {
                if (innerContext.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (!Interop.Ssl.Tls13Supported)
                {
                    if (protocols != SslProtocols.None &&
                        CipherSuitesPolicyPal.WantsTls13(protocols))
                    {
                        protocols = protocols & (~SslProtocols.Tls13);
                    }
                }
                else if (CipherSuitesPolicyPal.WantsTls13(protocols) &&
                         CipherSuitesPolicyPal.ShouldOptOutOfTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy))
                {
                    if (protocols == SslProtocols.None)
                    {
                        // we are using default settings but cipher suites policy says that TLS 1.3
                        // is not compatible with our settings (i.e. we requested no encryption or disabled
                        // all TLS 1.3 cipher suites)
                        protocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
                    }
                    else
                    {
                        // user explicitly asks for TLS 1.3 but their policy is not compatible with TLS 1.3
                        throw new SslException(
                                  SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                    }
                }

                if (CipherSuitesPolicyPal.ShouldOptOutOfLowerThanTls13(sslAuthenticationOptions.CipherSuitesPolicy, policy))
                {
                    if (!CipherSuitesPolicyPal.WantsTls13(protocols))
                    {
                        // We cannot provide neither TLS 1.3 or non TLS 1.3, user disabled all cipher suites
                        throw new SslException(
                                  SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                    }

                    protocols = SslProtocols.Tls13;
                }

                // Configure allowed protocols. It's ok to use DangerousGetHandle here without AddRef/Release as we just
                // create the handle, it's rooted by the using, no one else has a reference to it, etc.
                Ssl.SetProtocolOptions(innerContext.DangerousGetHandle(), protocols);

                // Sets policy and security level
                if (!Ssl.SetEncryptionPolicy(innerContext, policy))
                {
                    throw new SslException(
                              SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                }

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(innerContext);

                byte[]? cipherList =
                    CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy);

                Debug.Assert(cipherList == null || (cipherList.Length >= 1 && cipherList[cipherList.Length - 1] == 0));

                byte[]? cipherSuites =
                    CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, policy);

                Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0));

                unsafe
                {
                    fixed(byte *cipherListStr = cipherList)
                    fixed(byte *cipherSuitesStr = cipherSuites)
                    {
                        if (!Ssl.SetCiphers(innerContext, cipherListStr, cipherSuitesStr))
                        {
                            Crypto.ErrClearError();
                            throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, policy));
                        }
                    }
                }

                bool hasCertificateAndKey =
                    certHandle != null && !certHandle.IsInvalid &&
                    certKeyHandle != null && !certKeyHandle.IsInvalid;

                if (hasCertificateAndKey)
                {
                    SetSslCertificate(innerContext, certHandle !, certKeyHandle !);
                }

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                {
                    Ssl.SslCtxSetVerify(innerContext, s_verifyClientCertificate);
                }

                GCHandle alpnHandle = default;
                try
                {
                    if (sslAuthenticationOptions.ApplicationProtocols != null)
                    {
                        if (sslAuthenticationOptions.IsServer)
                        {
                            alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                            Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle));
                        }
                        else
                        {
                            if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0)
                            {
                                throw CreateSslException(SR.net_alpn_config_failed);
                            }
                        }
                    }

                    context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer);
                    Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create");
                    if (context.IsInvalid)
                    {
                        context.Dispose();
                        throw CreateSslException(SR.net_allocate_ssl_context_failed);
                    }

                    if (!sslAuthenticationOptions.IsServer)
                    {
                        // The IdnMapping converts unicode input into the IDNA punycode sequence.
                        string punyCode = s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                        // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                        if (!Ssl.SslSetTlsExtHostName(context, punyCode))
                        {
                            Crypto.ErrClearError();
                        }
                    }

                    if (hasCertificateAndKey)
                    {
                        bool hasCertReference = false;
                        try
                        {
                            certHandle !.DangerousAddRef(ref hasCertReference);
                            using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle()))
                            {
                                X509Chain?chain = null;
                                try
                                {
                                    chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false);
                                    if (chain != null && !Ssl.AddExtraChainCertificates(context, chain))
                                    {
                                        throw CreateSslException(SR.net_ssl_use_cert_failed);
                                    }
                                }
                                finally
                                {
                                    if (chain != null)
                                    {
                                        int elementsCount = chain.ChainElements.Count;
                                        for (int i = 0; i < elementsCount; i++)
                                        {
                                            chain.ChainElements[i].Certificate !.Dispose();
                                        }

                                        chain.Dispose();
                                    }
                                }
                            }
                        }
                        finally
                        {
                            if (hasCertReference)
                            {
                                certHandle !.DangerousRelease();
                            }
                        }
                    }

                    context.AlpnHandle = alpnHandle;
                }
                catch
                {
                    if (alpnHandle.IsAllocated)
                    {
                        alpnHandle.Free();
                    }

                    throw;
                }
            }

            return(context);
        }
Esempio n. 6
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);
                }
            }
        }
Esempio n. 7
0
        // This essentially wraps SSL_CTX* aka SSL_CTX_new + setting
        internal static SafeSslContextHandle AllocateSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions, SslProtocols protocols)
        {
            SafeX509Handle?   certHandle    = credential.CertHandle;
            SafeEvpPKeyHandle?certKeyHandle = credential.CertKeyHandle;

            // Always use SSLv23_method, regardless of protocols.  It supports negotiating to the highest
            // mutually supported version and can thus handle any of the set protocols, and we then use
            // SetProtocolOptions to ensure we only allow the ones requested.
            SafeSslContextHandle sslCtx = Ssl.SslCtxCreate(Ssl.SslMethods.SSLv23_method);

            try
            {
                if (sslCtx.IsInvalid)
                {
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                Ssl.SslCtxSetProtocolOptions(sslCtx, protocols);

                if (sslAuthenticationOptions.EncryptionPolicy != EncryptionPolicy.RequireEncryption)
                {
                    // Sets policy and security level
                    if (!Ssl.SetEncryptionPolicy(sslCtx, sslAuthenticationOptions.EncryptionPolicy))
                    {
                        throw new SslException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                    }
                }

                byte[]? cipherList =
                    CipherSuitesPolicyPal.GetOpenSslCipherList(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);

                Debug.Assert(cipherList == null || (cipherList.Length >= 1 && cipherList[cipherList.Length - 1] == 0));

                byte[]? cipherSuites =
                    CipherSuitesPolicyPal.GetOpenSslCipherSuites(sslAuthenticationOptions.CipherSuitesPolicy, protocols, sslAuthenticationOptions.EncryptionPolicy);

                Debug.Assert(cipherSuites == null || (cipherSuites.Length >= 1 && cipherSuites[cipherSuites.Length - 1] == 0));

                unsafe
                {
                    fixed(byte *cipherListStr = cipherList)
                    fixed(byte *cipherSuitesStr = cipherSuites)
                    {
                        if (!Ssl.SslCtxSetCiphers(sslCtx, cipherListStr, cipherSuitesStr))
                        {
                            Crypto.ErrClearError();
                            throw new PlatformNotSupportedException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy));
                        }
                    }
                }

                // The logic in SafeSslHandle.Disconnect is simple because we are doing a quiet
                // shutdown (we aren't negotiating for session close to enable later session
                // restoration).
                //
                // If you find yourself wanting to remove this line to enable bidirectional
                // close-notify, you'll probably need to rewrite SafeSslHandle.Disconnect().
                // https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
                Ssl.SslCtxSetQuietShutdown(sslCtx);

                if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    unsafe
                    {
                        Interop.Ssl.SslCtxSetAlpnSelectCb(sslCtx, &AlpnServerSelectCallback, IntPtr.Zero);
                    }
                }

                bool hasCertificateAndKey =
                    certHandle != null && !certHandle.IsInvalid &&
                    certKeyHandle != null && !certKeyHandle.IsInvalid;

                if (hasCertificateAndKey)
                {
                    SetSslCertificate(sslCtx, certHandle !, certKeyHandle !);
                }

                if (sslAuthenticationOptions.CertificateContext != null && sslAuthenticationOptions.CertificateContext.IntermediateCertificates.Length > 0)
                {
                    if (!Ssl.AddExtraChainCertificates(sslCtx, sslAuthenticationOptions.CertificateContext.IntermediateCertificates))
                    {
                        throw CreateSslException(SR.net_ssl_use_cert_failed);
                    }
                }
            }
            catch
            {
                sslCtx.Dispose();
                throw;
            }

            return(sslCtx);
        }