Frage

I'm having trouble establishing a connection to a PostgreSQL database that is configured only to accept a valid SSL certificate. I can connect using pgAdmin III with the appropriate certificate and key, but I cannot get it working with Npgsql. When I try to open a connection to the database, I get System.IO.IOException: The authentication or decryption has failed. Here is my code:

NpgsqlConnectionStringBuilder csb = new NpgsqlConnectionStringBuilder();
csb.Database = "database";
csb.Host = "psql-server";
csb.UserName = "dreamlax"; // must match Common Name of client certificate
csb.SSL = true;
csb.SslMode = SslMode.Require;

NpgsqlConnection conn = new NpgsqlConnection(csb.ConnectionString);
conn.ProvideClientCertificatesCallback += new ProvideClientCertificatesCallback(Database_ProvideClientCertificatesCallback);
conn.CertificateSelectionCallback += new CertificateSelectionCallback(Database_CertificateSelectionCallback);
conn.CertificateValidationCallback += new CertificateValidationCallback(Database_CertificateValidationCallback);
conn.PrivateKeySelectionCallback += new PrivateKeySelectionCallback(Database_PrivateKeySelectionCallback);

conn.Open(); //System.IO.IOException: The authentication or decryption has failed

The callbacks are defined like this:

static void Database_ProvideClientCertificates(X509CertificateCollection clienteCertis)
{
    X509Certificate2 cert = new X509Certificate2("mycert.pfx", "passphrase");
    clienteCertis.Add(cert);
}

static X509Certificate Database_CertificateSelectionCallback(X509CertificateCollection clientCerts, X509Certificate serverCert, string host, X509CertificateCollection serverRequestedCerts)
{
    return clienteCertis[0];
}

static AsymmetricAlgorithm Database_PrivateKeySelectionCallback(X509Certificate cert, string host)
{
    X509Cerficate2 thisCert = cert as X509Certificate2;
    if (cert != null)
        return cert.PrivateKey;
    else
        return null;
}

static bool MyCertificateValidationCallback(X509Certificate certificate, int[] certificateErrors)
{
    // forego server validation for now
    return true;
}

I set breakpoints confirming that each callback was returning something valid, but still the IOException is thrown.

War es hilfreich?

Lösung

I fixed this problem by modifying the Npgsql source. Rather than using Mono's Mono.Security.Protocol.Tls.SslClientStream, I changed it to use System.Net.Security.SslStream instead. These were the steps I took:

  1. Modify NpgsqlClosedState.cs:

    1. Remove the using Mono.Security.Protocol.Tls; directive and the one below it.
    2. Add a using System.Net.Security; directive.
    3. In the NpgsqlClosedState.Open() method, where it says if (response == 'S'), changed it to:

      if (response == 'S')
      {
          //create empty collection
          X509CertificateCollection clientCertificates = new X509CertificateCollection();
      
          //trigger the callback to fetch some certificates
          context.DefaultProvideClientCertificatesCallback(clientCertificates);
      
          // Create SslStream, wrapping around NpgsqlStream
          SslStream sstream = new SslStream(stream, true, delegate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
          {
              // Create callback to validate server cert here
              return true;
          });
          sstream.AuthenticateAsClient(context.Host, clientCertificates, System.Security.Authentication.SslProtocols.Default, false);
          stream = sstream;
      }
      
  2. Modify NpgsqlConnection.cs, remove the using Mono... directives. This will cause a number of errors regarding missing types, in particular, these will be regarding 3 sets of delegate/event combos that use Mono types. The errors will always appear in groups of three because these callbacks all tie in with Mono's SslClientStream class. Remove each group of three and replace it with a single ValidateServerCertificate delegate/event. This single event should be used in the constructor for the SslStream class that was used in step 1.3 above.

  3. The changes to NpgsqlConnection.cs will trigger more errors in other files NpgsqlConnector.cs, NpgsqlConnectorPool.cs etc. but the fix is the same, replace the 3 Mono-based callbacks with the new ValidateServerCertificate.

Once all that is done, Npgsql can be used without Mono components and with (for me) working SSL certificate authentication.

My pull request on github can be found here.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top