3

I have added a certificate to the system store with something like this:

PCCERT_CONTEXT pCertContext;
HCERTSTORE hCertStore;
CRYPT_KEY_PROV_INFO provInfo;

if (pCertContext = CertCreateCertificateContext(MY_ENCODING_TYPE, certDER, certSize)) {
    provInfo.pwszContainerName = idCert;
    provInfo.pwszProvName = provName;
    provInfo.dwProvType = provType;
    provInfo.dwFlags = 0;
    provInfo.cProvParam = 0;
    provInfo.rgProvParam = NULL;
    provInfo.dwKeySpec = AT_SIGNATURE;

    if (!CertSetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfo)) Error(TEXT("CertSetCertificateContextProperty"));

    if (!(hCertStore = CertOpenSystemStore(NULL, L"MY"))) Error(TEXT("CertOpenSystemStore"));
    if (!CertAddCertificateContextToStore(hCertStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL)) Error(TEXT("CertAddCertificateContextToStore"));
    CertFreeCertificateContext(pCertContext);
} else Error(TEXT("CertCreateCertificateContext"));

Now I'm making a Cryptographyc Service Provider and need to get the public key from this certificate to implement the CPExportKey() function.

Is this possible? If it is, how can I do it?

Also, if someone could point me at a kind of guide, or howto, of CSP drivers implementation it would be great! I'm having a bad time searching documentation for these things.

  • Also see [Get RSA public key from CRYPT_BIT_BLOB](https://stackoverflow.com/q/23864214/608639). (The WinHTTP part does not matter for the question and answer). – jww Sep 17 '19 at 20:58

1 Answers1

3

So I found the solution below. With all non-essential for understanding code omitted.

Iterate through the certificates in the store and through its properties to find my certificate, then use the CryptDecodeObjectEx() function to convert the key to the RSA_CSP_PUBLICKEYBLOB format.

The key is kept at this location pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData and its size at pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData.

HCERTSTORE hCertStore = NULL;
PCCERT_CONTEXT pCertContext = NULL;
PBYTE pbPKEY = NULL;
DWORD iPKEYSize;

hCertStore = CertOpenSystemStore(NULL, L"MY");

while(pCertContext = CertEnumCertificatesInStore(
    hCertStore,
    pCertContext))
{
    DWORD dwPropId = 0;
    while(dwPropId = CertEnumCertificateContextProperties(
        pCertContext, // The context whose properties are to be listed.
        dwPropId))    // Number of the last property found.  
    {
        // ...
        // here I compare the properties to see if it is the certificate that I want.
        // ...
        CryptDecodeObjectEx((PKCS_7_ASN_ENCODING | X509_ASN_ENCODING), 
            RSA_CSP_PUBLICKEYBLOB, 
            pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData, 
            pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData, 
            CRYPT_ENCODE_ALLOC_FLAG, 
            NULL, 
            &pbPKEY, 
            &iPKEYSize);

            // pbData and pcbDataLen are output parameters of the function
            *pcbDataLen = iPKEYSize;
            memcpy(pbData, pbPKEY, *pcbDataLen);
            LocalFree((HANDLE)pbPKEY);
        }
    }
}
  • Thanks for posting this. I adapted the code you posted, but when I dump the memory at `pbPKEY` it does not look anything like the Public Key shown for the certificate when displaying it in CertMgr.msc. Am I missing something? – Neil Weicher Mar 16 '19 at 14:34
  • @NeilWeicher What is the format in which CertMgr.msc display the Public Key? `RSA_CSP_PUBLICKEYBLOB` is a binary blob format. – Fábio Roberto Teodoro Mar 16 '19 at 15:11
  • The PK in CertMgr is 140 bytes and begins like this: `30 81 89 02 81 81 00 E2 84 F5 B5 F2 28 15 AE FE`. The blob is 150 bytes and begins like this: `06 02 00 00 00 A4 00 00 52 53 41 31 00 04 00 00`. The bytes in the PK do not match any sequence in the blob. I assume i have to convert the blob in some way to get the "real" PK? – Neil Weicher Mar 16 '19 at 15:23
  • Sorry, I meant that the blob is 148 bytes. – Neil Weicher Mar 16 '19 at 15:31
  • @NeilWeicher The PK in CertMgr looks like it is in an ASN encoding. To make the `pbKey` it is reversed, a header added to it, etc. I think that if you dump the `pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData` it should be equal to the value CertMgr shows. I'm curious now. – Fábio Roberto Teodoro Mar 16 '19 at 15:37
  • Yes, I had thought of the reversed bytes too, but that still didn't match in any way. – Neil Weicher Mar 16 '19 at 15:39