1

我无法与配置为仅接受有效 SSL 证书的 PostgreSQL 数据库建立连接。我可以使用带有适当证书和密钥的 pgAdmin III 进行连接,但我无法让它与 Npgsql 一起使用。当我尝试打开与数据库的连接时,我得到System.IO.IOException: The authentication or decryption has failed. 这是我的代码:

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

回调定义如下:

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;
}

我设置了断点,确认每个回调都返回了有效的东西,但仍然抛出了 IOException。

4

1 回答 1

4

我通过修改 Npgsql 源解决了这个问题。我没有使用 Mono's Mono.Security.Protocol.Tls.SslClientStream,而是将其改为使用System.Net.Security.SslStream。这些是我采取的步骤:

  1. 修改NpgsqlClosedState.cs

    1. 删除该using Mono.Security.Protocol.Tls;指令及其下方的指令。
    2. 添加using System.Net.Security;指令。
    3. NpgsqlClosedState.Open()方法中,它说if (response == 'S'),将其更改为:

      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. 修改NpgsqlConnection.cs,删除using Mono...指令。这将导致有关缺少类型的许多错误,特别是,这些将与使用 Mono 类型的 3 组委托/事件组合有关。错误总是以三个一组的形式出现,因为这些回调都与 Mono 的SslClientStream类相关。删除每三个一组并用单个ValidateServerCertificate代表/事件替换它。这个单一事件应该在SslStream上面步骤 1.3 中使用的类的构造函数中使用。

  3. 更改NpgsqlConnection.cs将在其他文件等中引发更多错误NpgsqlConnector.csNpgsqlConnectorPool.cs但修复是相同的,将 3 个基于 Mono 的回调替换为新的ValidateServerCertificate.

一旦完成所有这些,Npgsql 可以在没有 Mono 组件的情况下使用,并且(对我而言)工作 SSL 证书身份验证。

我在 github 上的 pull request 可以在这里找到。

于 2013-06-15T11:47:00.350 回答