简体   繁体   English

.NET Framework x509Certificate2类,HasPrivateKey == true && PrivateKey == null?

[英].NET Framework x509Certificate2 Class, HasPrivateKey == true && PrivateKey == null?

I'm attempting to work with an X509 certificate that was originally imported into the CurrentUser keystore on a Windows 10 computer using the "Certificates" snap-in of an MMC. 我正在尝试使用最初导入到Windows 10计算机上的CurrentUser密钥库中的X509证书,该证书使用MMC的“证书”管理单元。 The same procedure has been tested on a Windows 8.1 computer with the same result. 已在Windows 8.1计算机上测试了相同的过程,但结果相同。

Using the standard PowerShell PKI module, I'm getting an X509Certificate2 object using Get-Item: 使用标准PowerShell PKI模块,我使用Get-Item获取X509Certificate2对象:

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

The output of $my_cert | fl * $my_cert | fl *的输出 $my_cert | fl * is as follows: $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

So HasPrivateKey == True, but PrivateKey == null. 所以HasPrivateKey == True,但PrivateKey == null。 I've been trying to figure out how to gain access to the private key to perform encryption and decryption. 我一直在试图弄清楚如何访问私钥来执行加密和解密。 The examples I've seen online all seem to indicate the PrivateKey property of the X509Certificate2 class should be readily available, but apparently I've missed something. 我在网上看到的例子似乎都表明X509Certificate2类的PrivateKey属性应该随时可用,但显然我错过了一些东西。

I've read similar questions here, such as Empty PrivateKey in x509certificate2 , but none seem to resolve my issue. 我在这里读过类似的问题,例如x509certificate2中的Empty PrivateKey ,但似乎没有解决我的问题。 I've also read Eight tips for working with X.509 certificates in .NET by Paul Stovell, which was very enlightening, but ultimately didn't help. 我还阅读了Paul Stovell 在.NET中使用X.509证书的八个技巧 ,这非常具有启发性,但最终没有帮助。 It did help me verify that the Private Key exists in the correct place and, as far as I can tell, with the correct permissions to be referenced by the x509Certificate2 class: 它确实帮助我验证私钥是否存在于正确的位置,据我所知,x509Certificate2类引用了正确的权限:

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

The name of the key file matches to the Subject Key Identifier on the certificate. 密钥文件的名称与证书上的主题密钥标识符匹配。

Edit: 编辑:

The output of certutil -user -store my "Serial Number" is: 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.

What "key" piece of information am I missing here? 我在这里遗失了哪些“关键”信息? Why isn't the private key conveniently referenced from the X509Certificate2 object? 为什么不通过X509Certificate2对象方便地引用私钥? How do I gain access to it? 我如何获得访问权限?

This may indicate one of the following: 这可能表示以下之一:

1) the private key is stored in the Key Storage Provider (rather than legacy crypto service provider) which is poorly supported by .NET and not supported by PrivateKey property of X509Certificate2 class at all. 1)私有密钥存储在其中是不好的.NET支持和不支持的密钥存储提供程序(而不是传统的加密服务提供商) PrivateKey的财产X509Certificate2类的。 You can check this by running the following command: 您可以通过运行以下命令来检查:

certutil -user -store my "<CertSerialNumber>"

2) the private key is missing. 2)私钥丢失。

HasPrivateKey property doesn't necessary reflect the actual picture and may True for non-existent keys or False for existing keys. HasPrivateKey财产并不一定反映真实的画面和可True不存在的键或False的现有密钥。 Run the certutil command above to make sure if the key is presented. 运行上面的certutil命令以确保是否显示密钥。

In the case if private key is presented, but the bindings are broken, you can try to restore bindings by running the following command: 如果显示私钥,但绑定被破坏,您可以尝试通过运行以下命令来恢复绑定:

certutil -user -repairstore my "<CertSerialNumber>"

Your certutil information shows Provider = Microsoft Software Key Storage Provider which means that the private key is stored under Windows CNG, instead of Windows CryptoAPI (CAPI). 您的certutil信息显示Provider = Microsoft Software Key Storage Provider ,这意味着私钥存储在Windows CNG下,而不是Windows CryptoAPI(CAPI)。

.NET 4.6 added the GetRSAPrivateKey (extension) method to facilitate the otherwise breaking change of letting the PrivateKey property return something which was not an RSACryptoServiceProvider (or DSACryptoServiceProvider). .NET 4.6添加了GetRSAPrivateKey(扩展)方法,以便于更改让PrivateKey属性返回不是RSACryptoServiceProvider(或DSACryptoServiceProvider)的东西。 If you have access to that method (I'm not sure what version of the framework PowerShell uses) then it would solve your problem. 如果您有权访问该方法(我不确定PowerShell使用的框架版本),那么它将解决您的问题。

Two things to be aware of, though: 但有两点需要注意:

  1. GetRSAPrivateKey returns a unique Disposable object each time. GetRSAPrivateKey每次都返回一个唯一的Disposable对象。 You should put it in a using statement/manually call Dispose when finished with it (as opposed to cert.PrivateKey, which isn't unique, so shouldn't be Disposed). 你应该把它放在一个using语句/完成后手动调用Dispose(而不是cert.PrivateKey,这不是唯一的,所以不应该是Disposed)。
  2. The sign/verify//encrypt/decrypt methods have moved down to the RSA base class (albiet with slightly different, more forward-looking, signatures). sign / verify //加密/解密方法已经向下移动到RSA基类(具有略微不同,更具前瞻性的签名的albiet)。

I resolved the problem. 我解决了这个问题。

For some reason, .NET framework can't import private keys from files, however, it can import from the built-in windows store, this is because the PrivateKey method only supports RSA and DSA keys are per the Microsoft Spec : read the notes under "Remarks" section. 出于某种原因,.NET框架无法从文件导入私有密钥,但是,它可以从导入内置Windows应用商店,这是因为专用密钥方法仅支持RSA和DSA密钥是按照微软规格 :阅读笔记在“备注”部分下。

Anyhow, to get a PrivateKey object to return the key info, You need to do the following: 无论如何,要获取PrivateKey对象以返回密钥信息,您需要执行以下操作:

1) Import your P12 file into the Windows Keystore by double-clicking it. 1)双击将P12文件导入Windows密钥库。 2) Select "Import" when prompted into "Current User" 3) Make sure you select "Make Key Exportable", 2)当提示“当前用户”时选择“导入”3)确保选择“使密钥可导出”,

if this option is not available then your certificate has no private key 如果此选项不可用,则您的证书没有私钥

. 4) Select "Automatically place into store" 4)选择“自动放入商店”

5) Write the following code to retrieve your certificate: 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) Run the code, if you get a YES then the private key is not NULL (or NOTHING), which is what you are looking for. 6)运行代码,如果你得到YES,那么私钥不是NULL(或NOTHING),这就是你要找的东西。

If you load the same certificate directly from the file, the Private key will always be NOTHING / NULL , but the HasPrivateKey will say YES, which means (I know there is a key, but yet, I don't know how to understand it. When you import it into the Windows store, then Windows does translate it into a .NET-Compatible format. 如果直接从文件加载相同的证书,私钥永远是NOTHING / NULL,但HasPrivateKey会说YES,这意味着(我知道有一个关键,不过不失,我不知道该怎么理解当您将其导入Windows商店时,Windows会将其转换为.NET兼容格式。

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM