.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。
- 转到每个域控制器,打开 MMC
- 单击文件,添加管理单元
- Select证书
- Select 服务帐户并单击下一步
- Select 本地计算机并单击下一步
- Select Active Directory 服务并单击完成以完成对话框
- 单击“确定”将管理单元添加到 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;
});
我有一个相当简单的 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: 0Chain Element Information
Number of chain elements: 2
Chain elements synchronized? FalseElement 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: 9Element 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。
- 转到每个域控制器,打开 MMC
- 单击文件,添加管理单元
- Select证书
- Select 服务帐户并单击下一步
- Select 本地计算机并单击下一步
- Select Active Directory 服务并单击完成以完成对话框
- 单击“确定”将管理单元添加到 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;
});