【问题标题】:What is difference between manual installation of certificate and installation from code?手动安装证书和从代码安装有什么区别?
【发布时间】:2017-05-10 15:53:06
【问题描述】:

我正在开发一些证书签名工具,我目前的目标是创建方法,该方法将使用现有证书作为颁发者生成新证书。下面是生成证书的方法:

public X509Certificate2 CreateCertificate(string subjectName, TimeSpan certificateLifespan, X509Certificate2 issuer = null)
{
    var nameFormat = "CN={0}";

    var subject = string.Format(nameFormat, subjectName);

    // create DN for subject and issuer
    var dn = new CX500DistinguishedName();
    dn.Encode(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();

    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 = dn;
    else
    {
        var signerCertificate = new CSignerCertificate();
        signerCertificate.Initialize(true, X509PrivateKeyVerify.VerifyNone, EncodingType.XCN_CRYPT_STRING_HEXRAW, issuer.GetRawCertDataString());
                cert.SignerCertificate = signerCertificate;
    }
    cert.NotBefore = DateTime.Now.Date;
    cert.NotAfter = cert.NotBefore + certificateLifespan;
    cert.HashAlgorithm = hashobj;
    cert.Encode();

    var enroll = new CX509Enrollment();
    enroll.InitializeFromRequest(cert);
    enroll.CertificateFriendlyName = subjectName;

    var csr = enroll.CreateRequest();
    enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
                csr, EncodingType.XCN_CRYPT_STRING_BASE64, "");

    //InstallResponse automatically installs certificate to My store. We should remove it, and manage it manually.
    using(var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.ReadWrite);

        var certificate = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(
                    c => c.Subject.StartsWith(subject, StringComparison.Ordinal));

        if (certificate != null)
            store.Remove(certificate);
        store.Close();
    }

    //Self-signed certificates are also authomatically installed to Intermediate Authority store
    if(issuer == null)
    {
        using (var store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadWrite);

            var certificate = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(
                        c => c.Subject.StartsWith(subject, StringComparison.Ordinal));

            if (certificate != null)
                store.Remove(certificate);
            store.Close();
        }
    }

    var base64encoded = enroll.CreatePFX("", PFXExportOptions.PFXExportChainWithRoot);

    return new X509Certificate2(Convert.FromBase64String(base64encoded), "",
                X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}

问题是,它工作得非常好,但前提是我手动安装根证书,或使用证书管理器。但我正在尝试从代码中的嵌入式资源安装证书。

这是我将嵌入式证书提取到流的方法:

protected Stream ExtractResourceStream(string embeddedResourcePath)
{
    var assembly = GetType().Assembly;

    var pathConverted = embeddedResourcePath.Replace("\\", ".");

    var matchingResources = assembly.GetManifestResourceNames()
            .Where(n => n.EndsWith(pathConverted, StringComparison.InvariantCultureIgnoreCase))
            .ToArray();

    var resource = matchingResources.FirstOrDefault();
    if (resource == null)
        throw new InvalidOperationException(string.Format("Resource {0} not found.", embeddedResourcePath));
    if (matchingResources.Length > 1)
        throw new InvalidOperationException(string.Format("Resource {0} found more than once.", embeddedResourcePath));

    return assembly.GetManifestResourceStream(resource);
}

然后我下一步初始化它:

var bytes = new BinaryReader(stream).ReadBytes((int)stream.Length);
var root = new X509Certificate2(bytes, RootCertificatePassword);

这是我的证书安装方法:

private void AddCertificateToStore(X509Certificate2 certificate, StoreName storeName)
{
    using (var store = new X509Store(storeName, StoreLocation.LocalMachine))
    {
        store.Open(OpenFlags.ReadWrite);
        store.Add(certificate);
        store.Close();
    }
}

根证书似乎已正确安装,但在生成第二个证书时,signerCertificate.Initialize 调用时出现异常“未找到密钥”。

我正在使用受密码保护的 PFX 根证书。

所以,我的问题是 - 手动安装证书和从代码流中安装证书有什么区别?在代码中安装时我是否遗漏了某些部分?

【问题讨论】:

    标签: c# ssl-certificate certenroll


    【解决方案1】:

    嗯,这很愚蠢。显然,我缺少正确的键集。如果我像这样初始化根证书,它可以工作:

    var root = new X509Certificate2(bytes, RootCertificatePassword, 
                    X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
    

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 2021-09-08
      • 2021-04-26
      • 1970-01-01
      • 1970-01-01
      • 2019-11-30
      • 2014-07-16
      • 2021-10-13
      • 2023-04-05
      相关资源
      最近更新 更多