尝试创建非自签名证书的异常 [英] Exception on attempt to create not self-signed certificate

查看:65
本文介绍了尝试创建非自签名证书的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从每个设备的自签名证书切换到一对证书,其中一个是之前生成的,放置在受信任的根证书颁发机构存储中,对所有设备都相同,并作为第二个证书的根 CA 工作,每个设备生成,并放置在个人存储中.

I want to switch from self-signed certificate per device to pair of certificates, one of which is previously generated, placed in Trusted Root Certificate Authorities store, is same for all devices, and works as root CA for second certificate, which is generated per device, and placed in Personal store.

我不想使用 makecert,因为创建签名证书会显示 UI,我想避免这种情况.此外,由于某些与许可证相关的内容(尽管我有可用的解决方案),因此无法使用 OpenSSL.所以,现在我正在使用基于 CertEnroll 库的小型 C# 工具.

I would like to not use makecert, since creating signed certificate shows up UI, which I want to avoid. Also, OpenSSL can't be used due to some license-related stuff (although I have working solution with it). So, for now I'm working with small C# tool, based on CertEnroll lib.

这就是我为第一个根 CA 证书创建 pfx 的方式.

This is how I create pfx for first, root CA certificate.

makecert -n "CN=Root CA" -cy authority -r -a sha256 -len 2048 -sv root.pvk root.cer
pvk2pfx -pvk root.pvk -spc root.cer -pfx root.pfx -pi 123 -po 123

要从 C# 代码创建证书,我参考了问题 如何以编程方式为 WCF 服务创建自签名证书?C# 使用 certenroll.dll 生成无 CA 的非自签名客户端 CX509Certificate 请求.

To create certificate from C# code, I've referenced questions How to create self-signed certificate programmatically for WCF service? and C# Generate a non self signed client CX509Certificate Request without a CA using the certenroll.dll.

到目前为止,我有以下代码.证书生成方法:

So far, I have following code. Method for certificate generation:

/// <summary>
/// Generates self-signed certificate with specified subject, which will expire after specified timespan.
/// </summary>
public X509Certificate2 CreateCertificate(string subjectName, TimeSpan expirationLength, X509Certificate2 issuer = null)
{
    // create DN for subject and issuer
    var dn = new CX500DistinguishedName();
    dn.Encode("CN=" + subjectName);

    var issuerName = new CX500DistinguishedName();
    if(issuer != null)
    {
        issuerName.Encode(issuer.Subject);
    }

    var privateKey = new CX509PrivateKey
    {
        ProviderName = "Microsoft Strong Cryptographic Provider",
        Length = 2048,
        KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE,
        KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG |
                       X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG,
        MachineContext = true,
        ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG
    };

    privateKey.Create();

    // Use the stronger SHA512 hashing algorithm
    var hashobj = new CObjectId();
    hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
            ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
            AlgorithmFlags.AlgorithmFlagsNone, "SHA512");

    var cert = new CX509CertificateRequestCertificate();
    cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
    cert.Subject = dn;
    if (issuer != null)
        cert.Issuer = issuerName;
    else
        cert.Issuer = dn;
    cert.NotBefore = DateTime.Now.Date;
    cert.NotAfter = cert.NotBefore + expirationLength;
    cert.HashAlgorithm = hashobj; // Specify the hashing algorithm

    if(issuer != null)
    {
        var signerCertificate = new CSignerCertificate();
        signerCertificate.Initialize(true, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_HEX, issuer.GetRawCertDataString());
        cert.SignerCertificate = signerCertificate; 
    }
    cert.Encode();

    // Do the final enrollment process
    var enroll = new CX509Enrollment();
    enroll.InitializeFromRequest(cert); // load the certificate
    enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name

    var csr = enroll.CreateRequest(); // Output the request in base64
    // and install it back as the response
    enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
            csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
    // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
    var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption
            PFXExportOptions.PFXExportChainWithRoot);

    // instantiate the target class with the PKCS#12 data (and the empty password)
    return new X509Certificate2(Convert.FromBase64String(base64encoded), "",
        X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}

查找现有证书并基于它创建新证书的简单应用程序:

Simple application to find existing certificate and create new one based on it:

static void Main(string[] args)
{
    var certificateGenerator = new CertificateGenerator();

    X509Certificate2 rootCA;

    using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.ReadWrite);

        rootCA = store.Certificates.OfType<X509Certificate2>()
            .FirstOrDefault(c => c.Subject.StartsWith("CN=Root CA", StringComparison.Ordinal));

        store.Close();
    }

    if (rootCA == null)
        throw new Exception("Can't find root CA certificate");

    var testCert = certificateGenerator.CreateCertificate("Test", new TimeSpan(3650, 0, 0, 0), rootCA);
    using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.ReadWrite);
        store.Add(testCert);
        store.Close();
    }
}

问题是,如果我尝试引用证书而不是在受信任的根证书颁发机构中,而是在个人中(即使我在证书上有密码),它也能很好地工作.但是,如果我尝试根据受信任的根证书颁发机构的 CA 证书创建证书,我会在 signerCertificate.Initialize 上收到异常,说

The thing is, that it works great, if I try to reference certificate not in Trusted Root Certificate Authorities, but in Personal (even if I have password on certificate). But if I try to create certificate based on CA certificate from Trusted Root Certificate Authorities, I receive exception on signerCertificate.Initialize, saying

Cannot find object or property. 0x80092004 (-2146885628 CRYPT_E_NOT_FOUND)

那么,我错过了什么?

推荐答案

ISignerCertificate::Initialize 要求通过 Requests 或 My store 绑定私钥:

ISignerCertificate::Initialize requires that the private key be bound via the Requests or My store:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa376832(v=vs.85).aspx:

如果需要私钥,只有个人和请求存储搜索.

If a private key is needed, only the personal and request stores are searched.

如果不需要私钥,根和中间 CA商店也会被搜索.

If a private key is not needed, the root and intermediate CA stores are also searched.

Windows 希望您只将 CA 的公共部分放入 CA(中间)或 Root/ThirdPartyRoot 存储中,并且如果您是发行者,您还可以将它安装(现在使用私钥)到CurrentUser\My 或 LocalMachine\My.

Windows expects that you only put the public portion of the CA into the CA (intermediate) or Root/ThirdPartyRoot stores, and that if you're the issuer you'll ALSO have it installed (with the private key now) into CurrentUser\My or LocalMachine\My.

这篇关于尝试创建非自签名证书的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆