Identification of private key in Pkcs11Interop.PDF library - c#

I am trying to setup up an example application with the Pkcs11Interop.PDF extension. I am unfortunately getting a System.ArgumentNullException setting the ckaId. What would work here? I tried different numbers here which all gave me a System.ArgumentOutOfRangeException.
The setup is using the VirtualCryptoki-64-1.0.6.7.exe application to simulate a Smartcard.
The troubling call is:
pkcs11RsaSignature = new Pkcs11RsaSignature(libraryPath, tokenSerial, tokenLabel, pin, ckaLabel, ckaId, hashAlgorithm);
The hash algorithm I use is:
HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
Update after using the pkcs11-tool:
The content of the virtual card is:
C:\Program Files\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module "C:\windows\System32\vcki.dll" --list-slots --list-objects --login --pin 1234
Available slots:
Slot 0 (0xd47db04d): Virtual Smart Card Reader
token label: Virtual SC-A0101010101
token manuf: Cryptware
token model: VirtualSmartCard
token flags: rng, login required, PIN initialized, token initialized, other flags=0x200
serial num :
Using slot 0 with a present token (0xd47db04d)
Certificate Object, type = X.509 cert
label: ibisit
ID: 4a656e73204b6175666d616e6e
Public Key Object; RSA 1024 bits
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: encrypt, verify
Private Key Object; RSA
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: decrypt, sign
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
Public Key Object; RSA 1024 bits
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: encrypt, verify
Private Key Object; RSA
label: ibisit
ID: 4a656e73204b6175666d616e6e
Usage: decrypt, sign
warning: PKCS11 function C_GetAttributeValue(ALWAYS_AUTHENTICATE) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)
The parameters I use are:
string libraryPath = #"C:\Windows\System32\vcki.dll";
string tokenSerial = null;
string tokenLabel = #"Virtual SC-A0101010101";
string pin = #"1234";
string ckaLabel = #"ibisit";
string ckaId = "4a656e73204b6175666d616e6e";
HashAlgorithm hashAlgorithm = HashAlgorithm.SHA256;
I am unfortunatly getting a Net.Pkcs11Interop.PDF.ObjectNotFoundException setting the ckaId. Changing the pin gives me a different exception so I am definetely accessing the right device here.
Update after switching to SoftHSM:
jariq stated that too many objects with the same id were there problem here. Unfortunately the Virtual Key Explorer would not let me delete any object so I switched to SoftHSM (which I was using at the beginning). I am getting "Certificate with label "ibis-it key" and id "A1B2" was not found". I suppose the problem here is that this virtual card only holds a RSA keypair not a certificate.
The output of the pkcs11-tool is:
C:\Program Files (x86)\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module "C:\SoftHSM\lib\libsofthsm.dll" --list-slots --list-objects --login --pin smart
Available slots:
Slot 0 (0x0): SoftHSM
token label: SoftHSM
token manuf: SoftHSM
token model: SoftHSM
token flags: rng, login required, PIN initialized, token initialized, other flags=0x40
serial num : 1
Using slot 0 with a present token (0x0)
Public Key Object; RSA 2048 bits
label: ibis-it key
ID: a1b2
Usage: verify
Private Key Object; RSA
label: ibis-it key
ID: a1b2
Usage: sign
The Problem is that SoftHSM only imports PKCS#8 (RSA) key pairs, so there will never be a certificate here. I suppose you have been using it with RSA key pairs but not with certificates.

Parameters passed to the constructor of Net.Pkcs11Interop.PDF.Pkcs11RsaSignature class identify following things:
which PKCS#11 library should be used (libraryPath)
which token/smartcard stores the private key (tokenSerial and/or tokenLabel)
which private key should be used for signing (ckaLabel and/or ckaId)
which hash algorithm should be used during signature creation (hashAlgorithm)
If you know which PKCS#11 library should be used to access the smartcard then you can determine correct values for the rest of the parameters i.e. by running pkcs11-tool utility which is bundled with OpenSC middleware. Please find below the exact command and the output generated for my testing card (important parts are highlighted with bold text):
C:\Program Files (x86)\OpenSC Project\OpenSC\tools>pkcs11-tool.exe --module cardos11.dll --list-slots --list-objects --login --pin 11111111
Available slots:
Slot 0 (0x1): SCM Microsystems Inc. SCR33x USB Smart Card Reader 0
token label : Pkcs11Interop
token manufacturer : www.atos.net/cardos
token model : CardOS V4.3B
token flags : rng, login required, PIN initialized, token initialized, other flags=0x800
hardware version : 102.63
firmware version : 200.8
serial num : 7BFF2737350B262C
Using slot 0 with a present token (0x1)
Private Key Object; RSA
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Usage: sign
Certificate Object, type = X.509 cert
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Based on this output these are the correct values of individual parameters for this card:
libraryPath="cardos11.dll"
tokenSerial="7BFF2737350B262C" and/or tokenLabel="Pkcs11Interop"
ckaLabel="John Doe" and/or ckaId="ec5e50a889b888d600c6e13cb0fdf0c1"
Hope this helps.
Update for ObjectNotFoundException:
You are getting ObjectNotFoundException because there are two private keys with the exactly same label and ID stored in your token and therefore Pkcs11RsaSignature class cannot be sure which one should be used for signature creation. Just delete or rename one of them and it should be working.
Update for SoftHSM:
You can import PKCS#8 private key to SoftHSM with softhsm.exe tool:
C:\SoftHSM\bin>softhsm.exe --import doe.key --slot 0 --label "John Doe" --pin 11111111 --id "ec5e50a889b888d600c6e13cb0fdf0c1"
The key pair has been imported to the token in slot 0.
You can import DER encoded X.509 certificate to SoftHSM with pkcs11-tool.exe tool:
C:\SoftHSM\bin>"c:\Program Files (x86)\OpenSC Project\OpenSC\tools\pkcs11-tool.exe" --module libsofthsm.dll --login --pin 11111111 --write-object doe.der --type cert --label "John Doe" --id "ec5e50a889b888d600c6e13cb0fdf0c1"
Using slot 0 with a present token (0x0)
Created certificate:
Certificate Object, type = X.509 cert
label: John Doe
ID: ec5e50a889b888d600c6e13cb0fdf0c1
Just make sure you will import the certificate with the same ID as the ID of private key.

Related

How can I encrypt data using a public key from ECC X509 certificate in .net framework on windows?

I am using:
Windows 10 (Version 1709, OS Build 17025.1000)
.net framework 4.7
VS 2017 (version: 15.3.5)
Here is what I did:
Got a self signed ECC certificate using OpenSSL and steps outlined in the script at https://gist.github.com/sidshetye/4759690 with modifications:
a) Used NIST/P-256 curve over a 256 bit prime field
b) Used SHA-256
Load the certificate from file (generated in previous step) into X509Certificate2 object
Imported the PFX file into windows trust store (for testing). This is successful.
Inspection of the imported certificate shows Public Key field as 'ECC (256 Bits)' and Public key parameters as 'ECDSA_P256'.
Next tried to figure out how to encrypt with this certificate.
I am stuck at the last step because all the examples that use X509Certificate2 object predominantly use only RSA and I am using ECC certificate. For RSA certificate, there is a GetRSAPublicKey extention method on X509Certificate2 and RSA class has Encrypt method. However there is no such method for ECC certificates.
Next, I stumbled on this post (Load a Certificate Using X509Certificate2 with ECC Public Key) and tried following (even though it appeared bizarre as to why ECC cert public key is being coerced into RSA type):
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key
I got following exception: The certificate key algorithm is not supported.
Next I stumbled on this post (Importing ECC-based certificate from the Windows Certificate Store into CngKey) which basically tried to create CNGKey type and instantiate ECDsaCng with it. However even if I can do it with ECDiffieHellmanCng, there is no Encrypt method on it.
So I am not really sure how can I proceed further to use ECC X509 certificate's public key to encrypt data.
###Background
Asymmetric algorithms have three different purposes (that I know of)
Encryption
RSA is the only "standard" algorithm that can do this directly.
Signature
RSA
DSA
ECDSA
ElGamal Signature
Key Agreement
Diffie-Hellman (DH)
ECDH
ElGamal encryption (the asymmetric startup phase)
MQV
ECMQV
Because RSA encryption is space limited, and was hard for computers in the '90s, RSA encryption's primary use was in "Key Transfer", which is to say that the "encrypted message" was just the symmetric encryption key for DES/3DES (AES not yet having been invented) - https://www.rfc-editor.org/rfc/rfc2313#section-8.
Key agreement (or transfer) schemes always have to be combined with a protocol/scheme to result in an encryption operation. Such schemes include
TLS (nee SSL)
CMS or S/MIME encrypted-data
IES (Integrated Encryption Scheme)
ECIES (Elliptic Curve Integrated Encryption Scheme)
ElGamal encryption (holistically)
PGP encryption
So what you probably want is ECIES.
ECIES.Net
Currently (.NET Framework 4.7.1, .NET Core 2.0) there's no support to get an ECDiffieHellman object from a certificate in .NET.
Game over, right? Well, probably not. Unless a certificate carrying an ECDH key explicitly uses the id-ecDH algorithm identifier (vs the more standard id-ecc one) it can be opened as ECDSA. Then, you can coerce that object into being ECDH:
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}
(a similar thing can be done for a private key, if the key is exportable, otherwise complex things are required, but you shouldn't need it)
Let's go ahead and carve off the recipient public object:
ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;
So now we turn to http://www.secg.org/sec1-v2.pdf section 5.1 (Elliptic Curve Integrated Encryption Scheme)
###Setup
Choose ANSI-X9.63-KDF with SHA-2-256 as the hash function.
Choose HMAC–SHA-256–256.
Choose AES–256 in CBC mode.
Choose Elliptic Curve Diffie-Hellman Primitive.
You already chose secp256r1.
Hard-coded. Done.
Point compression's annoying, choose not to use it.
I'm omitting SharedInfo. That probably makes me a bad person.
Not using XOR, N/A.
###Encrypt
Make an ephemeral key on the right curve.
ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
We decided no.
ECParameters ephemPublicParams = ephem.ExportParameters(false);
int pointLen = ephemPublicParams.Q.X.Length;
byte[] rBar = new byte[pointLen * 2 + 1];
rBar[0] = 0x04;
Buffer.BlockCopy(ephemPublicParams.Q.X, 0, rBar, 1, pointLen);
Buffer.BlockCopy(ephemPublicParams.Q.Y, 0, rBar, 1 + pointLen, pointLen);
Can't directly do this, moving on.
Can't directly do this, moving on.
Since we're in control here, we'll just do 3, 4, 5, and 6 as one thing.
KDF time.
// This is why we picked AES 256, HMAC-SHA-2-256(-256) and SHA-2-256,
// the KDF is dead simple.
byte[] ek = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 1 });
byte[] mk = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 2 });
Encrypt stuff.
byte[] em;
// ECIES uses AES with the all zero IV. Since the key is never reused,
// there's not risk in that.
using (Aes aes = Aes.Create())
using (ICryptoTransform encryptor = aes.CreateEncryptor(ek, new byte[16]))
{
if (!encryptor.CanTransformMultipleBlocks)
{
throw new InvalidOperationException();
}
em = encryptor.TransformFinalBlock(message, 0, message.Length);
}
MAC it
byte[] d;
using (HMAC hmac = new HMACSHA256(mk))
{
d = hmac.ComputeHash(em);
}
Finish
// Either
return Tuple.Create(rBar, em, d);
// Or
return rBar.Concat(em).Concat(d).ToArray();
###Decrypt
Left as an exercise to the reader.
For getting ECDiffieHellman private key from certificate, use the following method:
Install NuGet package Security.Cryptography (CLR Security). (The package is under MIT license.)
Use the following extension method to get the CngKey instance:
CngKey cngKey = certificate.GetCngPrivateKey();
(Note: The extension method certificate.GetECDsaPrivateKey(), natively supported in .NET, returns an ECDsaCng instance; there is no extension method to return ECDiffieHellmanCng.)
The cngKey instance can be used to create either an ECDsaCng or an ECDiffieHellmanCng instance:
var sa = new ECDsaCng(cngKey);
var sa = new ECDiffieHellmanCng(cngKey);

Certificate's private key is exported as a different value?

I have created a self-signed-certificate (pfx) for testing purposes.
I'm able to export ist public key via :
X509Certificate2 cer = new X509Certificate2();
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificateCollection = store.Certificates.Find(...);
var cert = certificateCollection[0] ;
Console.WriteLine(Convert.ToBase64String(cert.Export( X509ContentType.Cert ), Base64FormattingOptions.InsertLineBreaks));
Result :
MIIDFTCCAf2gAw...........eFUpBB9C0/UNRmD7EAg==
This is consistent with openssl command :
$ openssl pkcs12 -in domain.name.pfx -clcerts -nokeys -out domain.name.crt
Result :
-----BEGIN CERTIFICATE-----
MIIDFTCCAf2gAw........9C0/UNRmD7EAg==
-----END CERTIFICATE-----
However , when I export the private key via C# :
Convert.ToBase64String(cert.Export( X509ContentType.Pfx ), Base64FormattingOptions.InsertLineBreaks)
I get :
MIIDFTCCAf2gAw............OVeFUpBB9C0/UNRmD7EAg==
While with openssl command :
$ openssl pkcs12 -in domain.name.pfx -nocerts -nodes -out domain.name.key
I get :
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w.........6HNjF2h7uuFdvbH2VAVg=
-----END PRIVATE KEY-----
Question:
Why do I get different results in the private keys ? And how can I fix my C# code to yield the same result as openssl's result ?
(NB - I'm not talking about -----begin/end keys----- boundries , but the actual value.
Additional info , the PFX file
The X509ContentType.Cert representation is the same because the certificate is a static structure. If you export it in a loop you'll always get the same answer.
The X509ContentType.Pfx representation is different because there are random salts in the PFX structure. One for each certificate, one for each private key, and one final one. If you export the same public+private pair as a PFX in a loop it will be different every time (288 bits of random data).
The end of the base64 data is different because it contains the last salt and the MAC over the rest of the data (including the encrypted cert salt and the encrypted key salt). It also contains an integer work factor, which Windows chooses as 2000 and OpenSSL chooses as 2048; further differentiating the OpenSSL and Windows exports.
So what you are seeing is by design. If you need a stable export you'll have to do something else. If you were just concerned that they're different, it's almost certainly fine.
Why do I get different results in the private keys ?
You are trying to do two separate things. With openssl you are using pfx file that contains private key and certificate(s). In c# you are using some representation of certificate with private key and you are doing export to a pfx file!
And how can I fix my C# code to yield the same result as openssl's result ?
C# has no friendly function that could export only private key (in openssl-like way) from X509Certificate2. As #darksquirell42 mentioned in comments you could use X509Certificate2.PrivateKey property, then cast it to RSACryptoServiceProvider and do ToXmlString(true). But this will give you private key parameters in a xml structure. Then you would have to take each parameter and construct an ASN.1 structure PKCS#1 or (IMHO better) PKCS#8. But C# has by default no friendly methods to work with ASN.1 objects.
OR you could use some crypto library to do it for you like BouncyCastle. This method from BouncyCastle could be helpful.

The requested operation is not supported in CngKey.Create

I'm trying to generate a self-signed certificate on the fly (programmatically) in a C# assembly (targeting .NET 4.0), to serve as a root CA to generate other certificates. The certificate doesn't need to be persisted in the Windows certificate store, I'll export it as a file.
Reading through this question (and in particular, #dthorpe's answer), I decided to give a try to CLR Security.
The CLR Security library put an extension method on CngKey class to generate a self-signed certificate, but I couldn't succeed in creating an instance of CngKey with:
var key = CngKey.Create(CngAlgorithm.Sha1); //same with Sha256, Sha512 and MD5
//or
var key = CngKey.Create(CngAlgorithm.Sha1, null, new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowExport,
KeyUsage = CngKeyUsages.AllUsages,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
});
Any of these lines raises the exception:
System.Security.Cryptography.CryptographicException was unhandled
HResult=-2146893783
Message=The requested operation is not supported.
Source=System.Core
StackTrace:
at System.Security.Cryptography.NCryptNative.CreatePersistedKey(SafeNCryptProviderHandle provider, String algorithm, String name, CngKeyCreationOptions options)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm, String keyName, CngKeyCreationParameters creationParameters)
at System.Security.Cryptography.CngKey.Create(CngAlgorithm algorithm)
at Tests.Program.Main(String[] args) at Program.cs:line 51
Searching through SO and the internet, I've checked the following:
I'm running a Windows 7 box (so it supports RPC as per MSDN)
Tried on a Windows Server 2012 box, same error
The process is running as admin (so it have access to all cert storages, anyway)
The services CNG Key Isolation and Remote Procedure Call (RPC) are running
Any help would be appreciated.
Small off-topic: during google search for this question found a site with HRESULT descriptions and handy search tool on SO and MSDN (I simply googled for your HRESULT code -2146893783)
I found a topic on MSDN which contains code failing with similar HRESULT, and the author provides a link to MSDN article about CNG:
NCRYPT_ALGORITHM_GROUP_PROPERTY
L"Algorithm Group"
A null-terminated Unicode string that contains the name of the object's algorithm group. This property only applies to keys. The following identifiers are returned by the Microsoft key storage provider:
NCRYPT_RSA_ALGORITHM_GROUP
"RSA", The RSA algorithm group.
NCRYPT_DH_ALGORITHM_GROUP
"DH", The Diffie-Hellman algorithm group.
NCRYPT_DSA_ALGORITHM_GROUP
"DSA", The DSA algorithm group.
NCRYPT_ECDSA_ALGORITHM_GROUP
"ECDSA", The elliptic curve DSA algorithm group.
NCRYPT_ECDH_ALGORITHM_GROUP
"ECDH", The elliptic curve Diffie-Hellman algorithm group.
Also I found an article on MSDN about CNG Key Storage Providers, which contains similar list of the algorithms:
Diffie-Hellman (DH)
Secret agreement and key exchange, 512 to 4096 in 64-bit increments
Digital Signature Algorithm (DSA)
Signatures, 512 to 1024 in 64-bit increments
Elliptic Curve Diffie-Hellman (ECDH)
Secret agreement and key exchange, P256, P384, P521
Elliptic Curve Digital Signature Algorithm (ECDSA)
Signatures, P256, P384, P521
RSA
Asymmetric encryption and signing, 512 to 16384 in 64-bit increments
So, as you've said that you've tried only Sha1, Sha256, Sha512 and MD5, maybe you simply use another algorithm from list available? You can find there ones mentioned above:
RSA
ECDsa
P256
P384
P521
ECDiffieHellman
P256
P384
P521
Here other developers successfully created one of them and was able to export it:
var cngKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256, null,
new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport });

.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. The same procedure has been tested on a Windows 8.1 computer with the same result.
Using the standard PowerShell PKI module, I'm getting an X509Certificate2 object using Get-Item:
$my_cert = Get-Item Cert:\CurrentUser\My\ADAA82188A17THUMBPRINTXXXXXXXXXXX
The output of $my_cert | fl * is as follows:
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. 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.
I've read similar questions here, such as Empty PrivateKey in x509certificate2, but none seem to resolve my issue. 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. 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:
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:
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? How do I gain access to it?
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).
.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). If you have access to that method (I'm not sure what version of the framework PowerShell uses) then it would solve your problem.
Two things to be aware of, though:
GetRSAPrivateKey returns a unique Disposable object each time. 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).
The sign/verify//encrypt/decrypt methods have moved down to the RSA base class (albiet with slightly different, more forward-looking, signatures).
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. You can check this by running the following command:
certutil -user -store my "<CertSerialNumber>"
2) the private key is missing.
HasPrivateKey property doesn't necessary reflect the actual picture and may True for non-existent keys or False for existing keys. Run the certutil command above to make sure if the key is presented.
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>"
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.
Anyhow, to get a PrivateKey object to return the key info, You need to do the following:
1) Import your P12 file into the Windows Keystore by double-clicking it.
2) Select "Import" when prompted into "Current User"
3) Make sure you select "Make Key Exportable",
if this option is not available then your certificate has no private
key
.
4) Select "Automatically place into store"
5) Write the following code to retrieve your certificate:
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.
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.
I hope this helps.

DSA and Public Key Exchange

I am trying to implement a licensing solution with DSA Algorithms for my application. Now here is what I have done:
Generated a hardware key, taken its hash.
Generated public and private keys. And encrypted my hash function with private key.
I forward this encrypted value back to client along with the public key.
At client's system, I use the DSASignatureDeformatter's VerifySignature function to validate my encrypted key, and my hardware key. If equal I validate the client.
Now my problem is that how to send the public key over the network. I tried to store and forward various DSAParameters values e.g., J, G, P in a file but since the sizes of keys change, that is not viable. Please see if anyone can guide.
Updated:
When I try to do this at the client's machine
DSAParameters KeyInfo;
using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
{
// Import the key information.
KeyInfo = DSA.ExportParameters(false);
}
The key size it generates for its various members is different from the public key parameters I have sent it back from server.
Okay. A bit late. But maybe other ones will have the same question.
You should just export your key like this:
string publicKey = DSA.ToXmlString(false);
so you can import it like this:
using (DSACryptoServiceProvider dsa = new DSACryptoServiceProvider())
{
dsa.FromXmlString(publicKey);
return dsa.VerifySignature()
}

Categories

Resources