【问题标题】:Exporting X.509 certificate WITHOUT private key导出没有私钥的 X.509 证书
【发布时间】:2009-05-21 16:15:27
【问题描述】:

我认为这很简单,但显然不是。我安装了一个证书,该证书具有一个可导出的私钥,我想仅使用公钥以编程方式导出它。换句话说,当通过 certmgr 导出并导出到 .CER 时,我想要一个等同于选择“不导出私钥”的结果。

似乎所有 X509Certificate2.Export 方法都将导出私钥(如果存在),如 PKCS #12,这与我想要的相反。

有什么方法可以使用 C# 来完成这个,还是我需要开始深入研究 CAPICOM?

【问题讨论】:

    标签: c# .net ssl-certificate x509certificate2


    【解决方案1】:

    对于其他可能偶然发现此问题的人,我想通了。如果将X509ContentType.Cert 指定为X509Certificate.Export 的第一个(也是唯一一个)参数,它只会导出公钥。另一方面,指定X509ContentType.Pfx 包括私钥(如果存在)。

    我可以发誓上周我看到了不同的行为,但我在测试时一定已经安装了私钥。今天删除了那个证书,重新开始的时候,发现导出的证书里面没有私钥。

    【讨论】:

    • 你知道有没有办法只导出私钥而不导出整个证书?,我必须将私钥解压为字节数组,我没有找到任何方法。 ..
    • @RRR:无论您尝试做什么,我都建议您不要这样做,因为证书的“私钥”不仅仅是一个字节数组,它是一个密码算法,具体是AsymmetricAlgorithm,不同的证书可能有完全不同的算法。如果您丢失了这些信息,将很难重建和解密/验证任何由公钥加密/签名的内容。如果您真的想尝试弄乱它,请查看 X509Certificate2.PrivateKey 并从那里开始工作。
    • @Aaronaught:您通常不想将私钥与证书一起导出。私钥必须保密。您可以验证使用仅具有证书的私钥签名的任何内容——证书仅包含公钥,这就是验证签名所需的全部内容。您通常不想使用私钥来加密数据。此外,私钥和公钥不可互换——给定公钥几乎不可能猜出私钥,反之亦然。所以,把私钥放在家里。
    • 请注意,这会产生不同的文件格式,因此不再存储为 .pfx/.p12 文件。
    【解决方案2】:

    我发现以下程序有助于让自己确信证书的 RawData 属性仅包含公钥(MSDN 对此不清楚),并且上面关于 X509ContentType.CertX509ContentType.Pfx 的答案可以作为预期:

    using System;
    using System.Linq;
    using System.IdentityModel.Tokens;
    using System.Security.Cryptography.X509Certificates;
    
    class Program
    {
        static void Main( string[] args )
        {
            var certPath = @"C:\blah\somecert.pfx";
            var certPassword = "somepassword";
    
            var orig = new X509Certificate2( certPath, certPassword, X509KeyStorageFlags.Exportable );
            Console.WriteLine( "Orig   : RawData.Length = {0}, HasPrivateKey = {1}", orig.RawData.Length, orig.HasPrivateKey );
    
            var certBytes = orig.Export( X509ContentType.Cert );
            var certA = new X509Certificate2( certBytes );
            Console.WriteLine( "cert A : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certA.RawData.Length, certA.HasPrivateKey, certBytes.Length );
    
            // NOTE that this the only place the byte count differs from the others
            certBytes = orig.Export( X509ContentType.Pfx );
            var certB = new X509Certificate2( certBytes );
            Console.WriteLine( "cert B : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certB.RawData.Length, certB.HasPrivateKey, certBytes.Length );
    
            var keyIdentifier = ( new X509SecurityToken( orig ) ).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
            certBytes = keyIdentifier.GetX509RawData();
            var certC = new X509Certificate2( certBytes );
            Console.WriteLine( "cert C : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certC.RawData.Length, certC.HasPrivateKey, certBytes.Length );
    
            Console.WriteLine( "RawData equals original RawData: {0}", certC.RawData.SequenceEqual( orig.RawData ) );
    
            Console.ReadLine();
        }
    }
    

    它输出以下内容:

    原始:RawData.Length = 1337,HasPrivateKey = True 证书 A:RawData.Length = 1337,HasPrivateKey = False,certBytes.Length = 1337 证书 B:RawData.Length = 1337,HasPrivateKey = True,certBytes.Length = 3187 证书 C:RawData.Length = 1337,HasPrivateKey = False,certBytes.Length = 1337 RawData 等于原始 RawData: True

    【讨论】:

      【解决方案3】:

      有一个OpenSSL .NET wrapper 你可能会觉得有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-09
        • 1970-01-01
        • 2013-05-05
        • 1970-01-01
        • 2019-07-02
        • 2020-01-25
        • 1970-01-01
        相关资源
        最近更新 更多