@@ -828,13 +828,11 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
828828 {
829829 m_logArguments [ 1 ] = cs . CertificateFile ;
830830 Log . Error ( "Session{0} no private key included with CertificateFile '{1}'" , m_logArguments ) ;
831- throw new MySqlException ( "CertificateFile does not contain a private key. " +
832- "CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key" ) ;
831+ throw new MySqlException ( "CertificateFile does not contain a private key. " +
832+ "CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key" ) ;
833833 }
834- #if ! NET45
835834 m_clientCertificate = certificate ;
836- #endif
837- clientCertificates = new X509CertificateCollection { certificate } ;
835+ clientCertificates = new X509CertificateCollection { certificate } ;
838836 }
839837 catch ( CryptographicException ex )
840838 {
@@ -849,29 +847,64 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
849847 X509Chain caCertificateChain = null ;
850848 if ( cs . CACertificateFile != null )
851849 {
852- try
850+ var certificateChain = new X509Chain
853851 {
854- var caCertificate = new X509Certificate2 ( cs . CACertificateFile ) ;
855- #if ! NET45
856- m_serverCertificate = caCertificate ;
857- #endif
858- caCertificateChain = new X509Chain
859- {
860- ChainPolicy =
852+ ChainPolicy =
861853 {
862854 RevocationMode = X509RevocationMode . NoCheck ,
863855 VerificationFlags = X509VerificationFlags . AllowUnknownCertificateAuthority
864856 }
865- } ;
866- caCertificateChain . ChainPolicy . ExtraStore . Add ( caCertificate ) ;
867- }
868- catch ( CryptographicException ex )
857+ } ;
858+
859+ try
869860 {
861+ // read the CA Certificate File
870862 m_logArguments [ 1 ] = cs . CACertificateFile ;
871- Log . Error ( ex , "Session{0} couldn't load CA certificate from CertificateFile '{1}'" , m_logArguments ) ;
872- if ( ! File . Exists ( cs . CACertificateFile ) )
873- throw new MySqlException ( "Cannot find CA Certificate File" , ex ) ;
874- throw new MySqlException ( "The CA Certificate File is invalid" , ex ) ;
863+ Log . Debug ( "Session{0} loading CA certificate(s) from CertificateFile '{1}'" , m_logArguments ) ;
864+ byte [ ] certificateBytes ;
865+ try
866+ {
867+ certificateBytes = File . ReadAllBytes ( cs . CACertificateFile ) ;
868+ }
869+ catch ( Exception ex )
870+ {
871+ Log . Error ( ex , "Session{0} couldn't load CA certificate from CertificateFile '{1}'" , m_logArguments ) ;
872+ throw new MySqlException ( "Could not load CA Certificate File: " + cs . CACertificateFile , ex ) ;
873+ }
874+
875+ // find the index of each individual certificate in the file (assuming there may be multiple certificates concatenated together)
876+ for ( var index = 0 ; index != - 1 ; index = Utility . FindNextIndex ( certificateBytes , index + 1 , s_beginCertificateBytes ) )
877+ {
878+ try
879+ {
880+ // load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
881+ m_logArguments [ 1 ] = index ;
882+ Log . Debug ( "Session{0} loading certificate at Index {1} in the CA certificate file." , m_logArguments ) ;
883+ var caCertificate = new X509Certificate2 ( Utility . ArraySlice ( certificateBytes , index ) ) ;
884+ certificateChain . ChainPolicy . ExtraStore . Add ( caCertificate ) ;
885+ }
886+ catch ( CryptographicException ex )
887+ {
888+ m_logArguments [ 1 ] = cs . CACertificateFile ;
889+ Log . Error ( ex , "Session{0} couldn't load CA certificate from CertificateFile '{1}'" , m_logArguments ) ;
890+ if ( ! File . Exists ( cs . CACertificateFile ) )
891+ throw new MySqlException ( "The CA Certificate File is invalid" , ex ) ;
892+ }
893+ }
894+
895+ // success
896+ if ( Log . IsInfoEnabled ( ) )
897+ Log . Info ( "Session{0} loaded certificates from CertificateFile '{1}'; CertificateCount: {2}" , m_logArguments [ 0 ] , cs . CACertificateFile , certificateChain . ChainPolicy . ExtraStore . Count ) ;
898+ caCertificateChain = certificateChain ;
899+ certificateChain = null ;
900+ }
901+ finally
902+ {
903+ #if NET45
904+ certificateChain ? . Reset ( ) ;
905+ #else
906+ certificateChain ? . Dispose ( ) ;
907+ #endif
875908 }
876909 }
877910
@@ -944,6 +977,14 @@ bool ValidateRemoteCertificate(object rcbSender, X509Certificate rcbCertificate,
944977 throw new MySqlException ( "MySQL Server rejected client certificate" , ex ) ;
945978 throw ;
946979 }
980+ finally
981+ {
982+ #if NET45
983+ caCertificateChain ? . Reset ( ) ;
984+ #else
985+ caCertificateChain ? . Dispose ( ) ;
986+ #endif
987+ }
947988 }
948989
949990 // Some servers are exposed through a proxy, which handles the initial handshake and gives the proxy's
@@ -1015,9 +1056,11 @@ private void ShutdownSocket()
10151056 Utility . Dispose ( ref m_networkStream ) ;
10161057 SafeDispose ( ref m_tcpClient ) ;
10171058 SafeDispose ( ref m_socket ) ;
1018- #if ! NET45
1059+ #if NET45
1060+ m_clientCertificate ? . Reset ( ) ;
1061+ m_clientCertificate = null ;
1062+ #else
10191063 Utility . Dispose ( ref m_clientCertificate ) ;
1020- Utility . Dispose ( ref m_serverCertificate ) ;
10211064#endif
10221065 }
10231066
@@ -1198,6 +1241,7 @@ private enum State
11981241 Failed ,
11991242 }
12001243
1244+ static readonly byte [ ] s_beginCertificateBytes = new byte [ ] { 45 , 45 , 45 , 45 , 45 , 66 , 69 , 71 , 73 , 78 , 32 , 67 , 69 , 82 , 84 , 73 , 70 , 73 , 67 , 65 , 84 , 69 , 45 , 45 , 45 , 45 , 45 } ; // -----BEGIN CERTIFICATE-----
12011245 static int s_lastId ;
12021246 static byte [ ] s_connectionAttributes ;
12031247 static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager . CreateLogger ( nameof ( ServerSession ) ) ;
@@ -1211,10 +1255,7 @@ private enum State
12111255 Socket m_socket ;
12121256 NetworkStream m_networkStream ;
12131257 SslStream m_sslStream ;
1214- #if ! NET45
1215- IDisposable m_clientCertificate ;
1216- IDisposable m_serverCertificate ;
1217- #endif
1258+ X509Certificate2 m_clientCertificate ;
12181259 IPayloadHandler m_payloadHandler ;
12191260 int m_activeCommandId ;
12201261 bool m_useCompression ;
0 commit comments