1

我正在尝试使用 bouncycastle 建立一个小型证书颁发机构。

证书是使用 BouncyCastle 生成的,并与私钥一起存储在当前用户的证书存储中。

但是出于安全原因,密钥被标记为不可导出。我知道绕过这一点很容易,但它意味着一个额外的障碍。

现在,如果要签署新证书,我需要访问证书的私钥。我尝试使用此代码:

X509Certificate2 caCert = GetRootKey(); // Fetches the certificate from the store
AsymmetricCipherKeyPair caPrivKey = DotNetUtilities.GetKeyPair(caCert.PrivateKey);

不幸的是,我得到了 CryptographicException - 因为私钥被标记为不可导出,这就是我的意图;-)

我的问题是:如何在不访问证书的情况下使用证书的私钥?我很确定我错过了一些重要的点,因为没有任何方式存储私钥是没有任何意义的......

我现在为此搜索了几个小时,但在 stackoverflow 或 Google 上找不到任何东西。

谁能告诉我我做错了什么?有没有另一种方法来签署一些东西?!

提前致谢!

克里斯

4

3 回答 3

0

简短的回答:没有 BouncyCastle。

您不能将密钥放入 BouncyCastle(或 OpenSSL 或任何其他)库并从那里使用它。如果不绕过“禁止导出”标志并将其导出,就没有办法做到这一点,正如您所指出的,如果您是管理员并且拥有必要的 Fu,那么这是可能的。

您可以做的是获取密钥的 CryptoAPI 句柄,并要求 CryptoAPI 为您执行签名或加密操作。DotNet 加密类封装了 CyptoAPI。

所以基本上你需要的是一个关于如何使用 DotNet 加密类的教程。有很多,例如这篇 MSDN 杂志文章有一个大约四分之三的数字签名示例:

http://msdn.microsoft.com/en-us/magazine/cc163454.aspx

于 2012-05-17T20:05:52.547 回答
0

我不想将 C++/CLI 添加到我的项目中,因此想出了一个使用 System.Security.Pkcs.SignedCms 的纯 C# 解决方案。与 Searcher 的答案类似,这提供了 ISignatureFactory 的实现,可用于在 BouncyCastle 中签署证书。

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;

public class WindowsSignatureFactory : ISignatureFactory
{
    public AlgorithmIdentifier Algorithm { get; }
    public X509Certificate2 Cert { get; }

    public object AlgorithmDetails => Algorithm;

    public WindowsSignatureFactory(AlgorithmIdentifier algorithm,
            X509Certificate2 cert)
    {
        Algorithm = algorithm;
        Cert = cert;
    }

    public IStreamCalculator CreateCalculator()
    {
        return new WindowsStreamCalculator(Algorithm, Cert);
    }

    private class WindowsStreamCalculator : IStreamCalculator
    {
        public AlgorithmIdentifier Algorithm { get; }
        public X509Certificate2 Cert { get; }
        private MemoryStream MemoryStream { get; } = new MemoryStream();
        public Stream Stream => MemoryStream;

        public WindowsStreamCalculator(AlgorithmIdentifier algorithm,
                X509Certificate2 cert)
        {
            Algorithm = algorithm;
            Cert = cert;
        }

        public object GetResult()
        {
            var signer = new System.Security.Cryptography.Pkcs.CmsSigner(Cert);
            // This maps e.g. "rsa with sha256" (OID 1.2.840.113549.1.1.11) to
            // "sha256" (OID 2.16.840.1.101.3.4.1)
            var hashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(Algorithm);
            var hashOid = hashAlgorithm.Algorithm.Oid;
            signer.DigestAlgorithm = new System.Security.Cryptography.Oid(hashOid);

            var dataToSign = MemoryStream.ToArray();
            var info = new System.Security.Cryptography.Pkcs.ContentInfo(dataToSign);
            var cms = new System.Security.Cryptography.Pkcs.SignedCms(info, true);
            cms.ComputeSignature(signer, true);

            // This is the tricky part: usually you would call cms.Export() to get a signed
            // message, but we only want the signature
            var cmsSigner = cms.SignerInfos
                .Cast<System.Security.Cryptography.Pkcs.SignerInfo>().ToArray().Single();
            var signatureData = cmsSigner.GetSignature();

            return new SimpleBlockResult(signatureData);
        }
    }
}

它可以用作 Asn1SignatureFactory 的替代品,如下所示:

public static Org.BouncyCastle.X509.X509Certificate SignCertificate(
        X509V3CertificateGenerator generator,
        X509Certificate2 nonExportableSigningCertificate)
{
    var signer = new WindowsSignatureFactory(
        new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256),
        nonExportableSigningCertificate);

    var signed = generator.Generate(signer);

    // Make sure it worked
    var signingCertBouncy = DotNetUtilities.FromX509Certificate(nonExportableSigningCertificate);
    signed.Verify(signingCertBouncy.GetPublicKey());

    return signed;
}
于 2021-05-11T01:55:10.597 回答
0

这个问题正是我需要的,但答案有一个不起作用的链接。在搜索了一段时间后,我使用 NCrypt 库找到了这个实现: https ://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/security/cryptoapi/CertSign/CPP/Sign.cpp

不幸的是,这是用 C 编写的,而我需要一个 C# 实现。所以我创建了这个可用于托管 C# 代码的 C++\CLI 包装器 (SignerWrapper.h): https ://github.com/DmitriNymi/StoreCertificateSigner

我还编写了一个自定义 ISignatureFactory 以在 BouncyCastle 中使用,请参阅同一 repo 中的 PksAsn1SignatureFactory.cs。它只是为了创建我的自定义 ISigner (我的班级: PksEcdsaSigner )。

现在,您可以将 PksAsn1SignatureFactory 作为 ISignatureFactory 传递给 BouncyCastle 以创建新证书,例如,请参阅单元测试 PksAsn1SignatureFactoryTest.cs - 显式调用 ISignatureFactory 和 CertificateIssuerTest.cs - 使用帮助程序类创建由来自商店中另一个证书的私钥签名的新证书

于 2019-05-03T21:01:41.157 回答