1

I am trying to acquire a CNG private key handle to sign data per a certificate. But I am struggling to get a valid key handle, in the first place.

The certificate lookup via fingerprint is working. I am recieving the context of a self signed certificate providing a private key.

The call of CryptAcquireCertificatePrivateKey is reportedly successful, content of LKeyHandle is nonzero and LKeySpec describes a Ncrypt key.

Still, NCryptIsKeyHandle always returns False. Thus, my code raises a exception. Please help me to understand why LKeyHandle isn't considered valid.

FCertStore := CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
                            CERT_SYSTEM_STORE_CURRENT_USER or CERT_STORE_READONLY_FLAG,
                            PWideChar('My'));

SetLength(LFingerprintHex, 20);
LSize := HexToBin(PWideChar('d159975092f813d8a17f97fbb3152f600daecdbc'), @LFingerPrintHex[0], 20);
SetLength(LFingerPrintHex, LSize);

LFingerprint.cbData := Length(LFingerPrintHex);
LFingerprint.pbData := @LFingerPrintHex[0];

LCertContext := CertFindCertificateInStore(FCertStore, X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                                            0, CERT_FIND_SHA1_HASH,
                                            @LFingerprint, nil);

if CryptAcquireCertificatePrivateKey(LCertContext, $40000 {CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG},
                                    nil, LKeyHandle, @LKeySpec, @LFreeHandle) and
    (LKeySpec = $FFFF {CERT_NCRYPT_KEY_SPEC}) and
    not NCryptIsKeyHandle(LKeyHandle) then
begin
    raise Exception.Create('invalid handle'); // but why?
end;
René Hoffmann
  • 2,766
  • 2
  • 20
  • 43
  • Didn't you forget the `@` before `LKeyHandle`? – Olivier May 12 '21 at 15:38
  • According to the [MSDN](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecertificateprivatekey): `phCryptProvOrNCryptKey` is the address of an HCRYPTPROV_OR_NCRYPT_KEY_HANDLE variable. So as Olivier said, maybe you need to use @LKeyHandle. – Zeus May 13 '21 at 05:32
  • 1
    Had to change the constant to make that work: `const CERT_NCRYPT_KEY_SPEC = $FFFFFFFF;` – FredS May 13 '21 at 19:04
  • @SongZhu-MSFT @Olivier the corresponding param is declared as `var` in `JwaWinCrypt`. Thus it is already handled as _value by reference_. (Nevertheless, I tried your advice.) – René Hoffmann May 17 '21 at 07:07
  • @FredS the code continues to fail if the comparison of `LKeySpec` is commented. In what way is your comment helpful? Changing the comparison so that the whole exception condition is not met anymore is utterly obvious (and useless) to me. – René Hoffmann May 17 '21 at 08:36
  • 1
    I'm using the declaration of `CryptAcquireCertificatePrivateKey` from `jwaWindows` and tested against a Certificate in both W7 and W10 then successfully used `NCryptEncrypt/Decrypt` on the result. Each function return was checked and it only failed during the comparison of the constant you had. Also made this change `const CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG = $00040000;` – FredS May 17 '21 at 18:29

0 Answers0