【问题标题】:X509FindType.FindBySubjectDistinguishedName not finding my certificate?X509FindType.FindBySubjectDistinguishedName 找不到我的证书?
【发布时间】:2021-02-02 18:45:58
【问题描述】:

我正在制作一个自签名的 X509 证书,如下所示:

public X509Certificate2 GenerateSelfSignedCertificate(Guid customerId)
{
    var distinguishedName = GetDistinguishedName(customerId);
    var rsa = RSA.Create(KeySize);
    var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithm, Padding);
    request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
    request.CertificateExtensions.Add(new X509KeyUsageExtension(
        X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.DataEncipherment |
        X509KeyUsageFlags.NonRepudiation, false));

    var cert = request.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.Add(CertificateLifespan));
    return cert;
}

然后我使用此方法生成证书并使用以下方法存储它:

public void StoreCertificate(X509Certificate2 certificate)
{
    using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadWrite);
    store.Add(certificate);
    store.Close();
}

接下来,我尝试使用以下方法从证书存储中获取证书:

public X509Certificate2 FetchPrivateCertificate(Guid customerId)
{
    var distinguishedName = GetDistinguishedName(customerId);
    using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
    var certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, distinguishedName, false);
    store.Close();
    var privateCert = certs.OfType<X509Certificate2>().FirstOrDefault(x => x.HasPrivateKey);
    return privateCert;
}

...使用相同的customerId,此结果始终为空。它甚至没有检查证书是否有私钥,证书集合总是空的。可分辨名称的生成方式完全相同。证书肯定在证书存储中。

我错过了什么?

更新

根据请求,我将添加专有名称的生成方式:

private string GetDistinguishedName(Guid customerId)
{
    var domain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
    var domainDistinguishedName = string.Join(",DC=", domain.Split("."));
    var distinguishedName = $"DC={Environment.MachineName},DC={domainDistinguishedName},O={customerId}";

    return distinguishedName;
}

【问题讨论】:

  • distinguishedName 变量的类型是什么?
  • @Crypt32 这是一个字符串。
  • 那么字符串与证书主题的格式化名称的主题不匹配。比如说,您的distinguishedName 可能看起来像CN=mysubject,O=example,C=us,但格式化的主题将是CN=mysubject, O=example, C=us(RDN 分隔符后有空格)。如果您能提供一个存储在变量中的内容以及返回证书的名称的示例,那就太好了。
  • @Crypt32 好的,我添加了它。我明白你在谈论空间,让我看看这是否有影响。
  • @Crypt32 是的,就是这样;当然不明显。如果你想写出来作为答案,我会接受。

标签: c# .net-standard x509certificate2


【解决方案1】:

问题(如 cmets 中所指出的)是您的 GetDistinguishedName 返回未格式化的 x.500 字符串,这足以构建 X.500 名称,但不足以进行搜索,因为搜索功能进行序数和不区分大小写的比较输入字符串和 *formatted* X.500 字符串。也就是说,您可以使用这些字符串格式来构建 X.500 名称:

CN=my common name,O=example LLC
CN = my common name,O = example LLC
CN= my common name, O =example LLC
<other variations>

CryptoAPI 使用 X.500 格式化程序将任何有效的 X.500 字符串格式化为外观一致的字符串,例如大写 RDN 属性名称,删除 = 字符周围的空格,在 RDN 分隔符后添加空格等等,该函数可能会重新排序字符串中的 RDN 属性。当按 X.500 名称搜索证书时,您必须将格式化/清理过的 X.500 名称传递给搜索功能。最简单的方法是更新您的 GetDistinguishedName 函数,如下所示:

private string GetDistinguishedName(Guid customerId)
{
    var domain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
    var domainDistinguishedName = string.Join(",DC=", domain.Split("."));
    var distinguishedName = $"DC={Environment.MachineName},DC={domainDistinguishedName},O={customerId}";

    return new X500DistinguishedName(distinguishedName).Name;
}

即在返回之前强制字符串格式化。

附言DC={Environment.MachineName} 使用了不正确的 RDN 属性。 DC 代表domainComponent。机器名称无论如何都不是域组件。使用CN(通用名)属性存储机器名称。而O 代表Organization。我怀疑它也被滥用了,因为customerId 几乎没有组织名称(只是一个假设)。您应该在主题/颁发者字符串中使用正确的 RDN 类型。

【讨论】:

  • 感谢您的帮助。
  • 有一点需要提一下,ToString() 不是用什么,它应该返回Name 属性。
  • 那是一个错字,晚上发布的。感谢您的通知,我已经更新了答案。
  • 更好的改变是只返回X500DistinguishedName(并将其传递给CertificateRequest构造函数),这样CertificateRequest就不必重新运行字符串处理。然后在 Find 调用中使用 distinctName.Name。
猜你喜欢
  • 1970-01-01
  • 2021-04-10
  • 2011-07-12
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 2020-09-22
  • 2016-07-18
  • 1970-01-01
相关资源
最近更新 更多