0

I tried to get EC private key from cert which in certificate store by CNG API. First, I call CertGetCertificateContextProperty() to get private key handle with CERT_KEY_CONTEXT_PROP_ID property, but it always return false. I'm sure that the cert has private key.

Code:

    wchar_t                 wMY_CERT_NAME[100];
    HCERTSTORE              hCertStore = NULL;
    PCCERT_CONTEXT          pSignerCert = NULL;
    NCRYPT_KEY_HANDLE       hKey = NULL;
    const int buffsize = 4999;
    DWORD len = buffsize;
    char buff[buffsize];

    // Open the certificate store.
    if (!(hCertStore = CertOpenStore(
        CERT_STORE_PROV_SYSTEM,
        0,
        NULL,
        CERT_SYSTEM_STORE_CURRENT_USER,
        CERT_STORE_NAME)))
    {
        MyHandleError(const_cast<LPTSTR>("The MY store could not be opened."));
    }

    swprintf(wMY_CERT_NAME, 100, L"%hs", MY_CERT_NAME);

    if (pSignerCert = CertFindCertificateInStore(
        hCertStore,
        MY_ENCODING_TYPE,
        0,
        CERT_FIND_SUBJECT_STR,
        wMY_CERT_NAME,
        NULL))
    {
        //continue
    }

    if (CertGetCertificateContextProperty(
        pSignerCert,
        CERT_KEY_CONTEXT_PROP_ID,
        buff,
        &len))
    {
        auto ckc = (CERT_KEY_CONTEXT *)buff;
        hKey = ckc->hNCryptKey;
    }
    else {
        wprintf(L"**** GetCertContextProperty failed.\n");
    }

Then, I tried to do the same step but read from a .pfx file like this page. It return true and get private key successfully. Why cert in certificate store doesn't have CERT_KEY_CONTEXT_PROP_ID property?

Assam
  • 179
  • 1
  • 13

2 Answers2

3

Certificates are signed public keys, they don't contain the private key. Most of the confusion stems from casual mis-use and conflation of terminology and concepts by people who don't understand cryptography properly.

For example, .cert .cer .crt can contain keys and/or certificates. However, strictly a certificate is a signed public key.

Woodstock
  • 22,184
  • 15
  • 80
  • 118
  • Hi Woodstock, I checked the cert in certificate store. There is a message "You have a private key that corresponds to this certificate". Doesn't mean have private key? I used these certs to sign, decrypt files and work fine. The cert in store is imported from .pfx file. – Assam Apr 08 '20 at 07:44
  • 1
    Certs can’t be used to sign by definition. A corresponding key lives outside the cert. Technically, it's a misnomer to say the data is "signed with the certificate", but it's close enough. Digital signatures are created using a private key. A certificate contains a corresponding public key that can be used to verify that the signature is valid. – Woodstock Apr 08 '20 at 07:45
  • Got it. So..Do you know how to export EC private key? I exported RSA private key successfully, but failed in EC private key. The detail description is [here](https://stackoverflow.com/questions/61073106/how-to-export-ec-private-key-as-pkcs1-or-pkcs8-format-from-certificate-store-b) – Assam Apr 08 '20 at 07:56
  • The way you've generated the key in that example forbids export (from a quick glance), you need to recreate the key allowing export via wrapping. An ECC private key is just a 256bit number, it's not a special data structure, similarly RSA private key is simply a number to be used as an exponent. There isn't a special reason RSA will export but ECC won't/ – Woodstock Apr 08 '20 at 08:15
  • hmm...I don't understand why that example forbids export. Could you share more info? and could you please give me some tips about recreating the key to allow export? – Assam Apr 08 '20 at 08:27
2

In my experience, if the certificate UI says you have a private key, then you should be able to open the key and use it. It doesn't count as export. Have you tried CryptAcquireCertificatePrivateKey?

As Woodstock writes, the private key is not a part of the certificate, and isn't even stored together with the certificate. Private keys are stored elsewhere. However, the public key is a part of the certificate, and the code can scan the local key library looking for a private key that has the same algorithm as the cert, and matches the public key in the cert.

One wrinkle in that is the "strong key protection" flag. If it's set, any attempt to get to the private key would pop up a prompt. That's why CryptAcquireCertificatePrivateKey has a parent window handle parameter, and also a "silent" flag. To the best of my knowledge, there's no way to remove it short of recreating the key.

Also, private keys are securable objects - they have access control lists (ACLs). Another reason why private key access may fail is an access denial. The ACL is editable from the certificate UI.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • I'm passing the retrieved raw byte[] of the located `CertFindCertificateInStore` certificate to C#, but that byte[] doesn't seem to include the private key when it's used in C#'s `X509Certificate2(byte[] rawData)` constructor. How can I go about extracting and merging the private key into the `PCCERT_CONTEXT->pbCertEncoded` byte[]. OR is there a different way to get a byte[] which contains he certificate and private key – Reahreic Jul 18 '23 at 17:15