.Net 程序未获得经过验证的服务器证书

.Net program does not get validated server certificate

我有一个相当简单的 LDAP 客户端,它在连接到 389 端口 (LDAP) 时工作​​正常,但在我尝试连接到 636 端口 (LDAPS) 时失败并显示 "LDAP server unavailable"。

namespace MyNS
{
  class ProgramLdap
  {
    private static LdapConnection CreateConnection(String baseDn, string usuario, string password)
    {
      LdapConnection ldapConnection = new LdapConnection(
        new LdapDirectoryIdentifier("myserver.example", 636, true, false));
      ldapConnection.SessionOptions.SecureSocketLayer = true;
      ldapConnection.SessionOptions.ProtocolVersion = 3;
      // [CODE MODIFICATION HERE]
      ldapConnection.Credential = new NetworkCredential(usuario, password);
      ldapConnection.AuthType = AuthType.Basic;
      ldapConnection.Timeout = new TimeSpan(1, 0, 0);
      return ldapConnection;
    }

    static void Main(string[] args)
    {

      LdapConnection ldapConnection = CreateConnection("", "myLdapUser", "noneOfYourBusiness");
      SearchRequest searchRequest = new SearchRequest(
        "ou=usuarios,dc=Dexter,dc=local",
         String.Format("(&(objectclass=person)(cn={0}))", user),
         SearchScope.Subtree,
         new string[0]);
       SearchResponse searchResponse =
          (SearchResponse) ldapConnection.SendRequest(searchRequest);
      System.Diagnostics.Debug.WriteLine("Resultados " + searchResponse.Entries.Count);
    }
  }
}

如果我在 [此处修改代码] 添加以下内容以接受所有服务器证书,它会起作用:

ldapConnection.SessionOptions.VerifyServerCertificate =
   new VerifyServerCertificateCallback((conn, certificate) => true);

该证书由自签名 CA 签署,我已将 CA public 证书添加到 "Trusted Root Certification Authorities"1 的本地计算机列表中。

如果我使用该 CA 的证书通过 openSSL 检查服务器证书,它会验证它。此外,我已尝试 LdapAdmin,当 CA 在该列表中时,连接到 LDAP 服务器时不会显示任何警告。

如果我使用 VerifyServerCertificateCallback 来打印证书的内容:

ldapConnection.SessionOptions.VerifyServerCertificate =
  new VerifyServerCertificateCallback(
    (conn, certificate) => {
       X509Certificate2 certificate2 = new X509Certificate2(certificate);
       bool verify = certificate2.Verify();
       System.Diagnostics.Debug.WriteLine(
         String.Format(
           "certificate2.Verify {0}; Name {1}; NameOID {2}; FriendlyName {3}; Thumbprint {4}; Algorithm FriendlyName {5}",
           verify,
           certificate2.SubjectName.Name,
           certificate2.SubjectName.Oid,
           certificate2.FriendlyName,
           certificate2.Thumbprint,
           certificate2.SignatureAlgorithm.FriendlyName));

           foreach (X509Extension extension in certificate2.Extensions) 
           {
             System.Diagnostics.Debug.WriteLine(extension.ToString() + " " + extension.Oid.FriendlyName + " " + Encoding.UTF8.GetString(extension.RawData));
            }
            return verify;
         });

它向我显示了服务器证书的指纹,但 验证 失败。

我能成为什么?似乎我缺少一些非常基本的东西,但我无法理解。


更新:

我检查了@FrankNielsen 的建议,并在 VerifyServerCertificateCallback 中添加了这段代码:

(conn, certificate) => {
  X509Chain ch = new X509Chain();
  ch.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
  ch.Build(new X509Certificate2(certificate));
  System.Diagnostics.Debug.WriteLine("Chain Information");
  System.Diagnostics.Debug.WriteLine(String.Format("Chain revocation flag: {0}", ch.ChainPolicy.RevocationFlag));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain revocation mode: {0}", ch.ChainPolicy.RevocationMode));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain verification flag: {0}", ch.ChainPolicy.VerificationFlags));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain verification time: {0}", ch.ChainPolicy.VerificationTime));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain status length: {0}", ch.ChainStatus.Length));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain application policy count: {0}", ch.ChainPolicy.ApplicationPolicy.Count));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain certificate policy count: {0} {1}", ch.ChainPolicy.CertificatePolicy.Count, Environment.NewLine));

  System.Diagnostics.Debug.WriteLine("Chain Element Information");
  System.Diagnostics.Debug.WriteLine(String.Format("Number of chain elements: {0}", ch.ChainElements.Count));
  System.Diagnostics.Debug.WriteLine(String.Format("Chain elements synchronized? {0} {1}", ch.ChainElements.IsSynchronized, Environment.NewLine));
  foreach (X509ChainElement element in ch.ChainElements)
  {
    System.Diagnostics.Debug.WriteLine(String.Format("Element issuer name: {0}", element.Certificate.Issuer));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate valid from: {0}", element.Certificate.NotBefore));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate valid until: {0}", element.Certificate.NotAfter));
    System.Diagnostics.Debug.WriteLine(String.Format("Element certificate is valid: {0}", element.Certificate.Verify()));
    System.Diagnostics.Debug.WriteLine(String.Format("Element error status length: {0}", element.ChainElementStatus.Length));
    System.Diagnostics.Debug.WriteLine(String.Format("Element information: {0}", element.Information));
    System.Diagnostics.Debug.WriteLine(String.Format("Thumbprint: {0}", element.Certificate.Thumbprint));
    System.Diagnostics.Debug.WriteLine(String.Format("Number of element extensions: {0}{1}", element.Certificate.Extensions.Count, Environment.NewLine));
    if (ch.ChainStatus.Length > 1)
    {
      for (int index = 0; index < element.ChainElementStatus.Length; index++)
      {
         System.Diagnostics.Debug.WriteLine(element.ChainElementStatus[index].Status);
        System.Diagnostics.Debug.WriteLine(element.ChainElementStatus[index].StatusInformation);
       }
     }
   }
   return true;
 });

它 returns:

Chain Information
Chain revocation flag: ExcludeRoot Chain revocation mode: NoCheck
Chain verification flag: NoFlag
Chain verification time: 07/10/2019 15:53:00
Chain status length: 0
Chain application policy count: 0
Chain certificate policy count: 0

Chain Element Information
Number of chain elements: 2
Chain elements synchronized? False

Element issuer name: CN=dexter-SCPDPRDEXTER01V-CA, DC=dexter, DC=local
Element certificate valid from: 02/09/2019 12:24:22
Element certificate valid until: 01/09/2020 12:24:22
Element certificate is valid: False
Element error status length: 0
Element information:
Thumbprint: 63DCF4EFE0C96EF021BCC9CE662E2627A3CDF399
Number of element extensions: 9

Element issuer name: CN=dexter-SCPDPRDEXTER01V-CA, DC=dexter, DC=local
Element certificate valid from: 11/06/2019 7:39:01
Element certificate valid until: 11/06/2069 7:49:01
Element certificate is valid: True
Element error status length: 0 Element information:
Thumbprint: 7BD9C718E336A50FA006CAEF539895C7E3EA5DA0
Number of element extensions: 4

证书符合预期(检索 CA),CA return 符合 Verify() 但服务器证书 return Verify().

为假

1另外,我也试过将它添加到 "Intermediate Certification Authorities" 但无济于事。

请在每个域控制器上完成此步骤以在 LDAP 上启用 SSL。

  1. 转到每个域控制器,打开 MMC
  2. 单击文件,添加管理单元
  3. Select证书
  4. Select 服务帐户并单击下一步
  5. Select 本地计算机并单击下一步
  6. Select Active Directory 服务并单击完成以完成对话框
  7. 单击“确定”将管理单元添加到 MMC

现在在证书管理单元中将您的证书添加到 NTDS/Personal,如果需要,将 CA 证书添加到 NTDS/Trusted 根证书颁发机构

我来自德国系统,如果技术名称与您的不匹配,我很抱歉。

来源:https://www.active-directory-faq.de/2012/08/ldap-over-ssl-oder-sercure-ldap-ldaps-mit-server-2008r2/

最后正确调试它的方法是检查 ChainStatus 的元素,如 X509Certificate2.Verify() returns false always

中所述

这样我确实发现我的程序无法连接到证书吊销列表URL。

解决方案(除了开放对 URL 的访问)是使用 X509Chain class 在回调中验证证书并将其设置为不检查 CRL:

ldapConnection.SessionOptions.VerifyServerCertificate =
    new VerifyServerCertificateCallback(
        (conn, certificate) =>
            {
                X509Chain x509Chain = new X509Chain();
                x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                X509Certificate2 cert2 = new X509Certificate2(certificate);
                bool buildResult = x509Chain.Build(cert2);
                if (!buildResult)
                {
                    foreach (X509ChainStatus chainStatus in x509Chain.ChainStatus)
                    {
                        System.Diagnostics.Debug.WriteLine(
                            String.Format(
                                "Chain Status {0} : {1}", chainStatus.Status, chainStatus.StatusInformation));
                    }
                }
                return buildResult;
            });