-
Type: Bug
-
Resolution: Done
-
Priority: Major - P3
-
Affects Version/s: 2.3
-
Component/s: Connectivity, Security
-
None
-
Fully Compatible
The use of certificates for authentication appears to be currently broken.
We recently updated from 2.2.3 to 2.4.1, after which our clients saw connection issues against all clusters using certificates.
Specifically, on the client side we encountered timeouts of the form:
{{System.TimeoutException: A timeout occured after 30000ms selecting a server using CompositeServerSelector{ Selectors = ReadPreferenceServerSelector{ ReadPreference =
{ Mode : Primary }}, LatencyLimitingServerSelector
{ AllowedLatencyRange = 00:00:00.0150000 }}. Client view of cluster state is { ClusterId : "1", ConnectionMode : "Automatic", Type : "Unknown", State : "Disconnected", Servers : [{ ServerId: "
{ ClusterId : 1, EndPoint : "Unspecified/wint-dev-vm102:27017" }", EndPoint: "Unspecified/wint-dev-vm102:27017", State: "Disconnected", Type: "Unknown", HeartbeatException: "MongoDB.Driver.MongoConnectionException: An exception occurred while opening a connection to the server. ---> System.InvalidOperationException: Invalid BinaryConnection state transition from 2 to Failed.}}
On the server side:
{{[initandlisten] connection accepted from 10.105.0.229:53377 #673429 (425 connections now open)
[conn673429] no SSL certificate provided by peer; connection rejected
[conn673429] end connection 10.105.0.229:53377 (424 connections now open)}}
Having looked into it, it appears that since 2.3.0 the private key is dropped on all X509Certificate2 certificates. This occurs in the SslSettings.CloneCertificate method at https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver/SslSettings.cs#L260 and can be replicated using the following program:
{{using System;
using System.Security.Cryptography.X509Certificates;
namespace CertificateTest
{
public class Program
{
public static void Main(string[] args)
{
string certificate = @"C:/Certificates/testcert.pfx";
//string certificate = @"C:\Certificates\testcert.pfx";
string passphrase = "password";
var cert = new X509Certificate2(certificate, passphrase, X509KeyStorageFlags.Exportable);
Console.WriteLine($"cert.HasPrivateKey:
{cert.HasPrivateKey}");
Console.WriteLine();
Console.WriteLine($"new X509Certificate2(cert.Export(X509ContentType.Cert)).HasPrivateKey:
");
Console.WriteLine($"new X509Certificate2(cert.RawData).HasPrivateKey:
");
Console.WriteLine();
Console.WriteLine($"new X509Certificate2(cert.Handle).HasPrivateKey:
");
Console.WriteLine($"new X509Certificate2(cert.Export(X509ContentType.Pfx)).HasPrivateKey:
");
Console.WriteLine($"new X509Certificate2(cert.Export(X509ContentType.Pkcs12)).HasPrivateKey:
");
Console.ReadLine();
}
}
}}}
Output:
{{cert.HasPrivateKey: True
new X509Certificate2(cert.Export(X509ContentType.Cert)).HasPrivateKey: False
new X509Certificate2(cert.RawData).HasPrivateKey: False
new X509Certificate2(cert.Handle).HasPrivateKey: True
new X509Certificate2(cert.Export(X509ContentType.Pfx)).HasPrivateKey: True
new X509Certificate2(cert.Export(X509ContentType.Pkcs12)).HasPrivateKey: True}}