0

I am trying to create a CNG (Windows Cryptography API Next Generation) key handle by calling NCryptOpenKey() and using the certificate's thumbprint as the key name parameter:

LPCWSTR keyName = L"\0xe0\0xf5\0xdf\0x72\0x7f\0x81\0x92\0xfa\0xae\0x8a\0x4b\0xf1\0xd5\0x53\0xc1\0xbe\0x40\0x18\0x90\0xdc";
NCryptOpenKey( hProvider, &hKey, keyName, 0, 0 );

Using this keyName the key cannot be found, but I can see the certificate with that thumbprint when I look it up in certmgr.msc ("‎e0 f5 df 72 7f 81 92 fa ae 8a 4b f1 d5 53 c1 be 40 18 90 dc"). What could possibly be the problem here?

Edit: What I essentially want to do (as I found out by the help of the people on stack overflow ;)) is to create a NCRYPT_KEY_HANDLE from a certificate stored in the Windows certificate store.

kitomer
  • 33
  • 1
  • 7
  • 1
    Maybe you should use the ASCII hex representation instead of the binary hex representation. – President James K. Polk Aug 22 '12 at 17:17
  • Do you mean like this: `LPCWSTR keyName = L"e0f5df727f8192faae8a4bf1d553c1be401890dc";`? I just tried it and the key still cannot be found. I wonder if anyone is actually using the CNG API to do what we are trying to do, that is the following: the user installs a certificate (holds private key) and the program tries to use this certificate to decrypt some data (without ever seeing or retrieving the private key). I do not think that is such an unusual approach - is it? – kitomer Aug 23 '12 at 05:45
  • I solved the problem with the help of the people finally by using the function `CryptAcquireCertificatePrivateKey()` with the parameter `CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG`. This lets me obtain a `NCRYPT_KEY_HANDLE` which I can then use to encrypt/decrypt data. It seems that when a certificate with an attached private key is installed (in Windows 7) then the attached private key is installed in the CNG key storage. The solution also leads to the conclusion that the CNG API can NOT be used to find a certain certificate, just all installed private keys. – kitomer Aug 30 '12 at 15:02

3 Answers3

1

The thumbprint and the key name usually has no relationship.

To get the name of the key that is associated with a certificate use CertGetCertificateContextProperty with CERT_KEY_PROV_INFO_PROP_ID.

Rasmus Faber
  • 48,631
  • 24
  • 141
  • 189
  • That helped a bit! The problem now is that `CertGetCertificateContextProperty()` with `CERT_KEY_PROV_INFO_PROP_ID` now returns with a failure code - meaning this property could not be retrieved. If I retrieve the friendly name of the certificate (`CERT_FRIENDLY_NAME_PROP_ID`) I get the expected name (the one I set in certmgr.msc which tells me this is the very certificate I am looking for). Why would the `CERT_KEY_PROV_INFO_PROP_ID` property not be accessable? – kitomer Aug 23 '12 at 12:29
0

Certificates don't contain the private keys. CERT_CONTEXT can have a CERT_KEY_PROV_INFO_PROP_ID property which say where to find a related private key. It is used by system function CryptAcquireCertificatePrivateKey and other high-level cryptography functions. This property is the only information about private key location in CNG.

Also, you can open every stored key in loop and compare private key BLOB with certificate's one.

Pavel Ognev
  • 962
  • 7
  • 15
  • How do I then non-programmatically install a certificate from a file (that has a private key inside) and have that key being located in the CNG key store? All my attempts so far failed: I see a certificate in the "Personal" certificate folder that clearly has a private key attached, but I do not see that key when I iterate over the keys stored using `NCryptEnumKeys()`. What could be the problem here? – kitomer Aug 28 '12 at 10:12
  • I know that there is no way on windows 7 and older to install certificate and private key by built-in utilities. Even standard *.pfx import only extracts certificate. – Pavel Ognev Aug 28 '12 at 12:31
  • Are there any non-builtin tools that would allow this? Is the programmatic way (using the NCrypt* functions) possible (as a last resort)? Thanks in advance! – kitomer Aug 28 '12 at 13:02
  • Of cause, and I did them. Function NCryptImportKey with parameter NCRYPT_PKCS8_PRIVATE_KEY_BLOB (meaning that private key is in PKCS#8 format) and key name in parameter list. Most problem is parsing *.pfx file - standard (PKCS#12) and corresponding RFC document are quite dirty. Then, I iteratively searched for key storage provider, supported algorithm of given key. But for most popular algorithms you can simply open "Microsoft Software Key Storage Provider" and be sure that it supports them. – Pavel Ognev Aug 28 '12 at 13:33
  • How would I create a key blob with OpenSSL that is suitable for import using the parameter `NCRYPT_PKCS8_PRIVATE_KEY_BLOB`? Thank you by the way, you are very helpful! – kitomer Aug 29 '12 at 10:14
  • openssl pkcs8 -topk8 ... converts private key from external formats to PKCS#8. But if you want to create a new _key_, why not to do that with CNG? – Pavel Ognev Aug 29 '12 at 11:35
  • ok that is the OpenSSL command I was using - but it gives me the error `8009310b` (not documented in msdn.microsoft.com/en-us/library/aa909166.aspx). What could be the reason? The key is coming from an external source, so we cannot create a new one. – kitomer Aug 29 '12 at 11:38
  • 8009310b means parsing error. Ensure, that openssl outputs a valid PKCS#8 BLOB (started with byte 0x30). By default, it writes a text file started wit line "-----BEGIN PRIVATE KEY-----". Did you forget "-outform DER" parameter? – Pavel Ognev Aug 29 '12 at 12:27
  • Thanks for the hint - it is certainly the correct file format now! The error disappeared. But now I get an "internal error" (`80090020` or `NTE_FAIL`). I think CNG is crap and I hate to ask: what could be wrong now? – kitomer Aug 29 '12 at 13:08
  • note: I did pass NULL as the parameter hImportKey of `NCryptImportKey`, so the function assumes that the key is not encrypted - could this be a problem? – kitomer Aug 29 '12 at 13:21
  • here is the code that should import the private key blob (loaded from file) and fails: `NCryptImportKey( provider, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, 0, &key, private_key_data, private_key_data_size, 0 );` – kitomer Aug 29 '12 at 13:33
  • I've just successfully imported PKCS#8 key, created by openssl. My call is: `NCryptImportKey( hProv, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &parList, &hKey, key.pbData, key.cbData, 0 );` parList contains only "NCRYPTBUFFER_PKCS_KEY_NAME" field (Name of key to store it as a named object), but in MS documentation this field is marked as "optional". I'm out of ideas what's going wrong. NTE_FAIL is _really_ internal error. I work with CNG for a long time, but never met it. – Pavel Ognev Aug 29 '12 at 13:54
0

If you want to try my example, here is a code:

DATA_BLOB key;
FILE *fil = _wfopen(L"D:\\222.p8", L"rb");
if(fil)
{
    fseek(fil, 0 ,2);
    key.cbData = ftell(fil);
    fseek(fil, 0, 0);
    key.pbData = new BYTE[key.cbData];

    fread(key.pbData, 1, key.cbData, fil);
    fclose(fil);
} else {
    MessageBox(L"Cannot open file", L"Error", MB_ICONERROR);
    return;
}

BCryptBuffer nameBuf;
nameBuf.BufferType = NCRYPTBUFFER_PKCS_KEY_NAME;
nameBuf.pvBuffer = L"killme";
nameBuf.cbBuffer = 14;

NCryptBufferDesc parList;
parList.ulVersion = NCRYPTBUFFER_VERSION;
parList.cBuffers = 1;
parList.pBuffers = &nameBuf;

// No error handlers!!!
NCRYPT_PROV_HANDLE hProv;
NCRYPT_KEY_HANDLE hKey;
NCryptOpenStorageProvider( &hProv, L"Microsoft Software Key Storage Provider", 0 );
NCryptImportKey( hProv, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &parList, &hKey, key.pbData, key.cbData, 0 );
NCryptFreeObject( hKey );
NCryptFreeObject( hProv );
return;

And this is my file (in HEX):

30 82 02 78 02 01 00 30 0D 06 09 2A 86 48 86 F7 
0D 01 01 01 05 00 04 82 02 62 30 82 02 5E 02 01 
00 02 81 81 00 BB 13 C9 C6 4A 18 1F 11 1D D3 CB 
E4 6A E2 F4 46 8C 1D 67 7F F2 6E 16 67 C6 81 B9 
D9 93 56 45 0B D5 EA 0F EB 7F F3 1D 83 07 19 3B 
A7 1C 84 82 23 88 44 65 13 79 78 EA E0 B7 AD A8 
4D B6 3C 9B D4 74 48 51 0D E8 60 59 19 D8 28 2B 
49 FE BC 8D BF 62 0D 44 70 70 76 2D 83 5B 2E DE 
9E 97 40 D3 BE 23 36 BB 02 3B F0 17 F2 EA 9A 97 
28 41 39 7F A1 98 6E CB A3 9F 5B B3 81 D8 66 73 
D5 19 63 35 E9 02 03 01 00 01 02 81 80 02 BA 5C 
CF 35 C0 B8 F5 EB 45 18 61 B7 51 4C 95 EE C5 CA 
FE E9 A3 C3 53 36 13 7E DD F6 5B B9 5C 07 D2 DC 
47 E7 30 BE 0B 18 17 BD 1A 9D BC 2C ED A4 62 0D 
9B 45 6D 31 A2 49 EB 65 5B 3A 14 BE 2C F4 DC C8 
9B 94 2C 5A EF 1E 43 00 3E 88 00 D3 F1 0F 6A E3 
14 C1 4D C8 7E 77 BD C8 41 92 8B 7D 2A B5 36 1A 
61 81 9C 38 F1 34 C4 C3 73 57 67 25 4C 93 59 9B 
C7 1B 10 39 F2 B2 2F 85 ED 69 B4 43 01 02 41 00 
EC D9 DD B0 C8 F3 BC 39 77 9F 15 20 D5 80 C7 E3 
F5 36 DB F0 BF AC 27 9E 82 A1 CB 64 F0 26 5A 31 
97 C5 75 B0 CB 42 E3 0D AC F1 69 AF 02 1C 0F F5 
38 7A 75 5C 87 41 16 D7 57 E5 A5 78 BB A4 6E 89 
02 41 00 CA 33 BF CA 34 F6 17 F8 F8 1F 0C FF EE 
62 18 09 22 BD 45 DC 2E E7 5D 6C 0F 66 0E 21 3D 
74 61 35 79 19 56 91 1B 90 93 12 AD BA FA 31 23 
4D D2 AE F8 0F 00 46 DE 6B F4 31 62 E3 88 5F 80 
35 B4 61 02 41 00 C7 78 AC C6 28 57 6D 5C 10 AC 
7F C4 C9 4A CE 0D E4 04 B1 B2 CE 1A 14 BB E0 54 
96 D1 89 97 23 3A C5 11 5D 8E E9 80 89 6C 89 0C 
3F EF 4E 1D 88 2B 03 C7 CE 73 80 CD 86 89 11 D3 
AC 4A 43 ED B5 D1 02 41 00 A7 E7 E3 0A 31 82 6D 
93 B3 CE 6D 08 15 56 E5 A8 A8 6D 4D 96 B2 68 33 
9E A9 06 D1 12 EF 2A 36 12 A6 55 D1 19 BC 2F 08 
C2 08 FB EC 08 63 CD 9A F6 EA 4B E2 A9 F6 C6 E4 
47 22 5B D9 01 9C C0 7B E1 02 41 00 E1 94 F0 BB 
B8 7E E6 CF 38 E0 8D 6B F7 BB E2 47 A2 B6 E4 EB 
A5 C7 15 62 76 C4 BA B8 DC 81 06 24 EB B1 46 9B 
EE 5D AC A5 A6 EB 93 2E B4 E7 F8 A3 CD FB 40 C1 
C9 F9 27 DF 31 8B BB EE 29 4F 63 6D 
Pavel Ognev
  • 962
  • 7
  • 15