I am trying to use the Windows Crypto API to generat an RSA keypair, and then export the public key in either DER or PEM. I have my code working and can successfully export the private key, but I haven't figured out how to do the same with public key. My end goal is to feed the public key to openssl to verify a signature.
I've seen this question: Microsoft CryptoAPI: how to convert PUBLICKEYBLOB to DER/PEM?
But I haven't found a solution. How can I save the public key to a valid PEM format using only native Windows Crypto API functions? What am I missing? Thanks!
Here is the relevant code snippets and my order of API calls for how I am exporting the private key as DER/PEM:
HCRYPTPROV hProv = NULL;
CryptAcquireContext(&hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0)
HCRYPTKEY hPrivateKey = NULL;
CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hPrivateKey);
CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
BYTE* pPrivKeyBlob = (BYTE *)malloc(size);
CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBlob, &size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pPrivKeyBlob, 0, NULL, NULL, &size);
BYTE* pPrivDer = (BYTE *)malloc(size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pPrivKeyBlob, 0, NULL, pPrivDer, &size);
This works, and pPrivDer contains the private key in DER format. I can format that to PEM no problem:
DWORD pemSize = 0;
CryptBinaryToStringA(pPrivDer, size, CRYPT_STRING_BASE64HEADER, NULL, &pemSize);
LPSTR pPrivPEM = (LPSTR)malloc(pemSize);
CryptBinaryToStringA(pPrivDer, size, CRYPT_STRING_BASE64HEADER, pPrivPEM, &pemSize);
printf("%s", pPrivPEM);
This works fine and the key is in the correct format (changing the header allows openssl to read/parse it)
However, I can not for the life of me figure out how to save the public key in DER/PEM. This is what I'm trying:
//export public key
CryptExportKey(hPrivateKey, 0, PUBLICKEYBLOB, 0, 0, &size);
BYTE* pPubKeyBlob = (BYTE *)malloc(size);
CryptExportKey(hPrivateKey, 0, PUBLICKEYBLOB, 0, pPubKeyBlob, &size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, mypubkey, 0, NULL, NULL, &size);
BYTE* pPubDer = (BYTE *)malloc(size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, mypubkey, 0, NULL, pPubDer, &size);
DWORD pPubPEMSize = 0;
CryptBinaryToStringA(pPubDer, size, CRYPT_STRING_BASE64, NULL, &pPubPEMSize);
LPSTR pPubPEM = (LPSTR)malloc(pPubPEMSize);
CryptBinaryToStringA(pPubDer, size, CRYPT_STRING_BASE64, pPubPEM, &pPubPEMSize);
printf("-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----\n\n", pPubPEM);
This causes no errors, but the exported DER and printed PEM for the public key is not recognized by openssl and seems to be in the wrong format.