private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { X509CertificateCollection clientCertificates = null; if (cs.CertificateFile != null) { try { var certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword); #if !NET45 m_clientCertificate = certificate; #endif clientCertificates = new X509CertificateCollection { certificate }; } catch (CryptographicException ex) { if (!File.Exists(cs.CertificateFile)) { throw new MySqlException("Cannot find Certificate File", ex); } throw new MySqlException("Either the Certificate Password is incorrect or the Certificate File is invalid", ex); } } X509Chain caCertificateChain = null; if (cs.CACertificateFile != null) { try { var caCertificate = new X509Certificate2(cs.CACertificateFile); #if !NET45 m_serverCertificate = caCertificate; #endif caCertificateChain = new X509Chain { ChainPolicy = { RevocationMode = X509RevocationMode.NoCheck, VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority } }; caCertificateChain.ChainPolicy.ExtraStore.Add(caCertificate); } catch (CryptographicException ex) { if (!File.Exists(cs.CACertificateFile)) { throw new MySqlException("Cannot find CA Certificate File", ex); } throw new MySqlException("The CA Certificate File is invalid", ex); } } X509Certificate ValidateLocalCertificate(object lcbSender, string lcbTargetHost, X509CertificateCollection lcbLocalCertificates, X509Certificate lcbRemoteCertificate, string[] lcbAcceptableIssuers) => lcbLocalCertificates[0]; bool ValidateRemoteCertificate(object rcbSender, X509Certificate rcbCertificate, X509Chain rcbChain, SslPolicyErrors rcbPolicyErrors) { if (cs.SslMode == MySqlSslMode.Preferred || cs.SslMode == MySqlSslMode.Required) { return(true); } if ((rcbPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 && caCertificateChain != null) { if (caCertificateChain.Build((X509Certificate2)rcbCertificate)) { var chainStatus = caCertificateChain.ChainStatus[0].Status & ~X509ChainStatusFlags.UntrustedRoot; if (chainStatus == X509ChainStatusFlags.NoError) { rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors; } } } if (cs.SslMode == MySqlSslMode.VerifyCA) { rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch; } return(rcbPolicyErrors == SslPolicyErrors.None); } SslStream sslStream; if (clientCertificates == null) { sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate); } else { sslStream = new SslStream(m_networkStream, false, ValidateRemoteCertificate, ValidateLocalCertificate); } // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101 var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11; if (!Utility.IsWindows()) { sslProtocols |= SslProtocols.Tls12; } var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull; var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(serverCapabilities, cs, m_useCompression))); await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false); try { if (ioBehavior == IOBehavior.Asynchronous) { await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); } else { #if NETSTANDARD1_3 await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); #else sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation); #endif } var sslByteHandler = new StreamByteHandler(sslStream); m_payloadHandler.ByteHandler = sslByteHandler; m_isSecureConnection = true; } catch (Exception ex) { sslStream.Dispose(); ShutdownSocket(); m_hostname = ""; lock (m_lock) m_state = State.Failed; if (ex is AuthenticationException) { throw new MySqlException("SSL Authentication Error", ex); } if (ex is IOException && clientCertificates != null) { throw new MySqlException("MySQL Server rejected client certificate", ex); } throw; } }
private async Task InitSslAsync(ConnectionSettings cs, IOBehavior ioBehavior, CancellationToken cancellationToken) { X509Certificate2 certificate; try { certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword); } catch (CryptographicException ex) { if (!File.Exists(cs.CertificateFile)) { throw new MySqlException("Cannot find SSL Certificate File", ex); } throw new MySqlException("Either the SSL Certificate Password is incorrect or the SSL Certificate File is invalid", ex); } Func <object, string, X509CertificateCollection, X509Certificate, string[], X509Certificate> localCertificateCb = (lcbSender, lcbTargetHost, lcbLocalCertificates, lcbRemoteCertificate, lcbAcceptableIssuers) => lcbLocalCertificates[0]; Func <object, X509Certificate, X509Chain, SslPolicyErrors, bool> remoteCertificateCb = (rcbSender, rcbCertificate, rcbChain, rcbPolicyErrors) => { switch (rcbPolicyErrors) { case SslPolicyErrors.None: return(true); case SslPolicyErrors.RemoteCertificateNameMismatch: return(cs.SslMode != MySqlSslMode.VerifyFull); default: return(cs.SslMode == MySqlSslMode.Required); } }; var sslStream = new SslStream(m_networkStream, false, new RemoteCertificateValidationCallback(remoteCertificateCb), new LocalCertificateSelectionCallback(localCertificateCb)); var clientCertificates = new X509CertificateCollection { certificate }; // SslProtocols.Tls1.2 throws an exception in Windows, see https://github.com/mysql-net/MySqlConnector/pull/101 var sslProtocols = SslProtocols.Tls | SslProtocols.Tls11; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { sslProtocols |= SslProtocols.Tls12; } var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull; var initSsl = new PayloadData(new ArraySegment <byte>(HandshakeResponse41Packet.InitSsl(cs))); await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false); try { if (ioBehavior == IOBehavior.Asynchronous) { await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); } else { #if NETSTANDARD1_3 await sslStream.AuthenticateAsClientAsync(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation).ConfigureAwait(false); #else sslStream.AuthenticateAsClient(m_hostname, clientCertificates, sslProtocols, checkCertificateRevocation); #endif } var sslByteHandler = new StreamByteHandler(sslStream); m_payloadHandler.ByteHandler = sslByteHandler; } catch (AuthenticationException ex) { ShutdownSocket(); m_hostname = ""; m_state = State.Failed; throw new MySqlException("SSL Authentication Error", ex); } }