使用 wincrypt 将主题备用名称添加到自签名证书

Add Subject Alternate Name to self signed certificate using wincrypt

如何在证书中添加主题备用名称。使用 wincrypt 我创建了证书并将其添加到 "MY" 和 "root" 路径,其中 CN 可以是机器完全限定的域名或主机名或 IP。作为主题备用名称,我想添加 following:DNS name=fully qualified domain name, DNS name=hostname 和 DNS 名称=IP 这该怎么做? 我不想使用 openssl。

    LPCTSTR cnName= fqdn;
    DWORD cbEncoded = 0;
if (!CertStrToName(X509_ASN_ENCODING, cnName, CERT_X500_NAME_STR, NULL, 
    pbdata, &cbData, NULL))
    {
    _tprintf(_T("CertStrToName failed 0x%x\n"), GetLastError());
    return 0;
    }
    if (!(pbdata = (BYTE *)malloc(cbData)))
    {
    _tprintf(_T("malloc Error 0x%x\n"), GetLastError());
    return 0;
    }

    if (!CertStrToName(X509_ASN_ENCODING, cnName, CERT_X500_NAME_STR, NULL, pbdata, &cbData, NULL))

{
    _tprintf(_T("CertStrToName failed 0x%x\n"), GetLastError());
    return 0;
}

CERT_NAME_BLOB IssuerBlob;
IssuerBlob.cbData = cbEncoded;
IssuerBlob.pbData = pbEncoded;

CertCreateSelfSignCertificate(NULL, &IssuerBlob, 0, &KeyProvInfo, &Alg, 0, &EndTime, 0);
OpenandAddCertificateToStore(pCertContext, L"MY");
OpenandAddCertificateToStore(pCertContext, L"Root");

This creates and adds certificate to the store without SAN in the certificate

我试过像下面这样传递扩展名列表:

CertCreateSelfSignCertificate(NULL, &IssuerBlob, 0, &KeyProvInfo, &Alg, 0, &EndTime, &myExtns_list);

CERT_EXTENSION myExtn;
myExtn.fCritical = TRUE;
myExtn.pszObjId = szOID_SUBJECT_ALT_NAME;
myExtn.Value = myBlobdata;

CERT_EXTENSIONS myExtns_list;
myExtns_list.cExtension = 1;
myExtns_list.rgExtension = &myExtn;

char cb[20] = { "DNS Names=abc.com" };
BYTE    *pbData = (BYTE*)cb;
CERT_NAME_BLOB myBlobdata;
myBlobdata.cbData = 20;
myBlobdata.pbData = pbData;

with this I could get the SAN as byte format in the left pane and the right pane shows my string "DNS Names=abc.com".

但我的要求是只显示 SAN 中的 DNS 名称。

我假设您构建扩展数据实际上发生在您调用 CertCreateSelfSignedCertificate 之前(同样,您构建 myBlobdata 发生在使用它之前)。

主要问题是您使用一个 SAN 条目(某种程度上)作为整个 SAN 扩展;这意味着您丢失了一些编码包装器。第二个问题是您使用了 szOID_SUBJECT_ALT_NAME,这是主题备用名称的不正确 OID...您实际上想要 szOID_SUBJECT_ALT_NAME2.

I want to add the following:DNS name=fully qualified domain name, DNS name=hostname and DNS name=IP

将 IP 地址添加为 DNS 名称是非标准的,应该会导致匹配失败。您实际上想要将 IP 地址添加为 IP 地址 SAN 条目。

CERT_ALT_NAME_ENTRY entries[3];
entries[0] = { CERT_ALT_NAME_DNS_NAME };
entries[0].pwszDNSName = L"example.org";

// IPv4 Address 10.12.1.130
BYTE ip4Bytes[] = { 10, 12, 1, 130 };
entries[1] = { CERT_ALT_NAME_IP_ADDRESS };
entries[1].IPAddress = { sizeof(ip4Bytes), ip4Bytes };

// ::1, big-endian
BYTE ip6Bytes[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
entries[2] = { CERT_ALT_NAME_IP_ADDRESS };
entries[2].IPAddress = { sizeof(ip6Bytes), ip6Bytes };

CERT_NAME_BLOB name = { cbEncoded, buf };
BYTE extBuf[1024] = { 0 };
cbEncoded = sizeof(extBuf);

CERT_ALT_NAME_INFO info = { sizeof(entries) / sizeof(CERT_ALT_NAME_ENTRY), entries };

if (!CryptEncodeObjectEx(
    X509_ASN_ENCODING,
    X509_ALTERNATE_NAME,
    &info,
    0,
    nullptr,
    extBuf,
    &cbEncoded))
{
    // Whatever your error handling story is.
    //
    // Note I didn't do a 0 buffer or CRYPT_ENCODE_ALLOC; I just knew
    // that my buffer would be big enough.
}

CERT_EXTENSION extension = { 0 };
extension.fCritical = 0;
extension.pszObjId = szOID_SUBJECT_ALT_NAME2;
extension.Value = { cbEncoded, extBuf };

CERT_EXTENSIONS extensions = { 1, &extension };

...

PCCERT_CONTEXT cert = CertCreateSelfSignCertificate(
    0,
    &name,
    0,
    &keyProvInfo,
    &sigAlg,
    0,
    &certExpirationDate,
    &extensions);

在 CertUI 中,给我预期的主题备用名称值:

DNS Name=example.org
IP Address=10.12.1.130
IP Address=0000:0000:0000:0000:0000:0000:0000:0001