Search code examples
c++cryptographycng

How can I get a NCRYPT_KEY_HANDLE for the private key of a PCCERT_CONTEXT?


How can I get a NCRYPT_KEY_HANDLE for encryption/decryption with CNG from the private key of a PCCERT_CONTEXT from the windows certificate store?

The CryptEncrypt function has been superceded by the NCryptEncrypt and BCryptEncrypt functions, but there is no immediately apparent way of obtaining a handle for either of these functions from a PCCERT_CONTEXT from the Windows certificate store.

Is it even possible to use the CNG functions for encryption/decryption using (private keys of) certificates without resolving to brute force approaches like exporting the certificate?


Solution

  • I'm answering this myself since I found answers nowhere else:

    The following code will get you what you want:

    const HCERTSTORE store(CertOpenStore(CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, (const void*)L"MY"));
    const PCCERT_CONTEXT certContext(CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR_W, subjectName, nullptr));
    if (certContext)
    {
      HCRYPTPROV_OR_NCRYPT_KEY_HANDLE keyHandle;
      DWORD keySpec;
      BOOL callerFreesKeyHandle;
    
      // Get NCrypt key handle from certificate.
      // Might fail for instance if certificate private key is not accessible to current user.
      if (CryptAcquireCertificatePrivateKey(certContext, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, nullptr, &keyHandle, &keySpec, &callerFreesKeyHandle))
      {
        check(keySpec == CERT_NCRYPT_KEY_SPEC); //< Should always have this value when giving CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.
        UNIQUE_NCRYPT_KEY_HANDLE keyHandleKeeper;
        if (callerFreesKeyHandle)
        {
          keyHandleKeeper.reset(keyHandle);
        }
        ...