【问题标题】:.NET Framework x509Certificate2 Class, HasPrivateKey == true && PrivateKey == null?.NET Framework x509Certificate2 类,HasPrivateKey == true && PrivateKey == null?
【发布时间】:2016-12-05 13:45:01
【问题描述】:

我正在尝试使用 X509 证书,该证书最初是使用 MMC 的“证书”管理单元导入到 Windows 10 计算机上的 CurrentUser 密钥库中的。相同的过程已在 Windows 8.1 计算机上进行了测试,结果相同。

使用标准的 PowerShell PKI 模块,我使用 Get-Item 获取 X509Certificate2 对象:

$my_cert = Get-Item Cert:\CurrentUser\My\ADAA82188A17THUMBPRINTXXXXXXXXXXX

$my_cert | fl *的输出如下:

PSPath                   : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\XXXXXXXXXXXXXXXXXXX
PSParentPath             : Microsoft.PowerShell.Security\Certificate::CurrentUser\My
PSChildName              : XXXXXXXXXXXXXXXXXXX
PSDrive                  : Cert
PSProvider               : Microsoft.PowerShell.Security\Certificate
PSIsContainer            : False
EnhancedKeyUsageList     : {Secure Email (1.3.6.1.5.5.7.3.4), IP security user (1.3.6.1.5.5.7.3.7), Encrypting File
                           System (1.3.6.1.4.1.311.10.3.4), Document Signing (1.3.6.1.4.1.311.10.3.12)...}
DnsNameList              : {My Name}
SendAsTrustedIssuer      : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
PolicyId                 : {D52C406F-C279-4BF2-B7C2-EE704290DB3E}
Archived                 : False
Extensions               : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid,
                           System.Security.Cryptography.Oid, System.Security.Cryptography.Oid...}
FriendlyName             :
IssuerName               : System.Security.Cryptography.X509Certificates.X500DistinguishedName
NotAfter                 : 4/15/2017 2:15:16 PM
NotBefore                : 4/15/2016 2:15:16 PM
HasPrivateKey            : True
PrivateKey               :
PublicKey                : System.Security.Cryptography.X509Certificates.PublicKey
RawData                  : {56, 130, 19, 252...}
SerialNumber             : 4F0000002F700000000000000000000000
SubjectName              : System.Security.Cryptography.X509Certificates.X500DistinguishedName
SignatureAlgorithm       : System.Security.Cryptography.Oid
Thumbprint               : XXXXXXXXXXXXXXXXXXX
Version                  : 3
Handle                   : 2241663016272
Issuer                   : CN=Issuing CA, DC=My, DC=Domain, DC=us
Subject                  : E=my.name@my.domain.us, CN=My Name

所以 HasPrivateKey == True,但 PrivateKey == null。我一直在试图弄清楚如何访问私钥以执行加密和解密。我在网上看到的示例似乎都表明 X509Certificate2 类的 PrivateKey 属性应该很容易获得,但显然我错过了一些东西。

我在这里读过类似的问题,例如Empty PrivateKey in x509certificate2,但似乎没有一个可以解决我的问题。我还阅读了 Paul Stovell 的 Eight tips for working with X.509 certificates in .NET,这很有启发性,但最终没有帮助。它确实帮助我验证了私钥是否存在于正确的位置,并且据我所知,它具有 x509Certificate2 类引用的正确权限:

C:\Users\My.Name\AppData\Roaming\Microsoft\SystemCertificates\My\Keys

密钥文件的名称与证书上的主题密钥标识符匹配。

编辑:

certutil -user -store my "Serial Number" 的输出为:

Serial Number: 4f000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Issuer: CN=Issuing CA, DC=My, DC=Domain, DC=us
 NotBefore: 4/15/2016 2:15 PM
 NotAfter: 4/15/2017 2:15 PM
Subject: E=my.name@my.domain.us, CN=My Name
Non-root Certificate
Template: Userv1, User v1
Cert Hash(sha1): ad ab 82 18 8a 17 4d 75 11 04 48 7c 43 43 d4 05 b9 74 c8 4c
  Key Container = te-Userv1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  Unique container name: fcbba1aa0xxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  Provider = Microsoft Software Key Storage Provider
Encryption test passed
CertUtil: -store command completed successfully.

我在这里遗漏了哪些“关键”信息?为什么不能方便地从 X509Certificate2 对象中引用私钥?如何访问它?

【问题讨论】:

  • 试试$my_cert.get_PrivateKey()。它会抛出任何异常吗?
  • @PetSerAl,我确实遇到了一个异常 - “指定的提供程序类型无效。”

标签: c# powershell x509 x509certificate2


【解决方案1】:

这可能表示以下情况之一:

1) 私钥存储在密钥存储提供程序(而不是传统的加密服务提供程序)中,.NET 对它的支持很差,X509Certificate2 类的 PrivateKey 属性根本不支持。您可以通过运行以下命令来检查这一点:

certutil -user -store my "<CertSerialNumber>"

2) 私钥丢失。

HasPrivateKey 属性不一定反映实际情况,可能True 用于不存在的键或False 用于现有键。运行上面的 certutil 命令以确保密钥是否存在。

如果提供了私钥,但绑定已损坏,您可以尝试通过运行以下命令来恢复绑定:

certutil -user -repairstore my "<CertSerialNumber>"

【讨论】:

  • 我已将 CertUtil 的输出添加到我的原始帖子中。看起来您是对的 - 私钥由密钥存储提供程序 (KSP) 存储。我现在正在寻找使用 KSP 和 CNG 密钥的好例子……知道吗?
  • 您到底需要什么?
  • 我想我什么都不需要。我只是在寻找通过示例理解的捷径,但我设法将其破解...感谢您对此的帮助!
  • 一般来说,为了在 .NET 中使用 KSP,您必须运行一些非托管 C 风格的函数:CryptAcquireCertificatePrivateKey 并使用返回的密钥句柄来实例化一个 CngKey 类,该类可用于执行常见的加密操作。
  • 只是一个在 PowerShell 中使用纯原生 CryptoAPI 函数的示例:sysadmins.lv/blog-en/sign-data-with-ecc-cng-certificate.aspx
【解决方案2】:

您的 certutil 信息显示 Provider = Microsoft Software Key Storage Provider,这意味着私钥存储在 Windows CNG 下,而不是 Windows CryptoAPI (CAPI)。

.NET 4.6 添加了 GetRSAPrivateKey(扩展)方法,以促进让 PrivateKey 属性返回不是 RSACryptoServiceProvider(或 DSACryptoServiceProvider)的内容的破坏性更改。如果您可以访问该方法(我不确定 PowerShell 使用哪个版本的框架),那么它将解决您的问题。

不过有两点需要注意:

  1. GetRSAPrivateKey 每次都返回一个唯一的 Disposable 对象。您应该将其放入 using 语句/在完成后手动调用 Dispose(与 cert.PrivateKey 相反,它不是唯一的,因此不应被 Disposed)。
  2. sign/verify//encrypt/decrypt 方法已下移至 RSA 基类(尽管签名略有不同,但更具前瞻性)。

【讨论】:

    【解决方案3】:

    我解决了这个问题。

    由于某种原因,.NET 框架不能从文件中导入私钥,但是,它可以从内置的 windows 存储中导入,这是因为 PrivateKey 方法只支持 RSA 和 DSA 密钥是每个 Microsoft Spec :阅读“备注”部分下的注释。

    无论如何,要获取一个 PrivateKey 对象以返回密钥信息,您需要执行以下操作:

    1) 通过双击将您的 P12 文件导入 Windows 密钥库。 2)当提示进入“当前用户”时选择“导入” 3)确保选择“使密钥可导出”,

    如果此选项不可用,则您的证书没有私有 关键

    。 4) 选择“自动入库”

    5) 编写以下代码来检索您的证书:

    Dim CertDataInfo As System.Security.Cryptography.X509Certificates.X509Certificate2 
    Dim Store As New System.Security.Cryptography.X509Certificates.X509Store("MY", Security.Cryptography.X509Certificates.StoreLocation.CurrentUser)
    
    Store.Open(Security.Cryptography.X509Certificates.OpenFlags.ReadOnly)
    Console.writeline (Store.Certificates.Count)
    
    For I = 0 To Store.Certificates.Count - 1
     If Store.Certificates.Item(I).FriendlyName = "Heider Sati's Cert(48F57XTHVE)" Then
      CertDataInfo = Store.Certificates.Item(I)
     End If
    
     Console.writeline ("Cert: " & Store.Certificates.Item(I).FriendlyName)
    
    Next
    Store.Close()
    
    If CertDataInfo.PrivateKey Is Nothing Then
     MsgBox("NULL")
    Else
     MsgBox("YES")
    End If
    

    6) 运行代码,如果得到“是”,则私钥不为 NULL(或 NOTHING),这正是您要查找的。

    如果您直接从文件中加载相同的证书,则 Private key 将始终为 NOTHING / NULL ,但 HasPrivateKey 会说 YES,这意味着(我知道有一个密钥,但是我不知道如何理解它。当您将其导入 Windows 商店时,Windows 会将其转换为与 .NET 兼容的格式。

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-09
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      • 2021-11-25
      • 2020-05-04
      • 1970-01-01
      相关资源
      最近更新 更多