1
votes

I'm trying to output the original base64 form of an imported RSA public key using Windows CryptoAPI functions. Using CryptBinaryToString with the CRYPT_STRING_BASE64HEADER flag, the output header reads "BEGIN CERTIFICATE" instead of expected "BEGIN PUBLIC KEY".

(EDIT: I didn't highlight this part of my problem) Furthermore, the resulting public key appears to be different than the original.

Will this become problematic if I were to export the output and reimport it? If so, what am I doing wrong?

Here's how the public key was imported.

Public key is stored in file pubkey.pem in the following PEM format:

-----BEGIN PUBLIC KEY-----
[REDACTED]
-----END PUBLIC KEY-----

File is read into a buffer with CreateFile/ReadFile. PEM converted to binary using CryptStringToBinaryA. Binary is decoded to X509_PUBLIC_KEY_INFO using CryptDecodeObjectEx. PubKeyInfo struct is decoded to RSA_CSP_PUBLICKEYBLOB (same function as above).

This part works just fine (can import the key and encrypt data with CryptImportKey, CryptEncrypt, etc).

Here's the code that I've put together to try and bring the raw blob back to base64 PEM format. I've removed most error-checking to save headaches.

pbTmp and cbTmp are the temp buffer to hold output and size respectively. pBinaryKey is the raw public key blob (imported from earlier) pBuffer is the output buffer (assumed to be the correct size) ulDataLen is the output buffer size

CryptEncodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pBinaryKey, 0, NULL, NULL, &cbTmp)
pbTmp = malloc(cbTmp);
CryptEncodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pBinaryKey, 0, NULL, pbTmp, &cbTmp)
CryptBinaryToStringA(pbTmp, cbTmp, CRYPT_STRING_BASE64HEADER, NULL, &ulDataLen)
CryptBinaryToStringA(pbTmp, cbTmp, CRYPT_STRING_BASE64HEADER, pBuffer, &ulDataLen)

The resulting buffer ends up with this:

-----BEGIN CERTIFICATE-----
[REDACTED; DIFFERENT FROM ORIGINAL PUBLIC KEY]
-----END CERTIFICATE-----
1
It looks like that API only supports headers for CERTIFICATE, CERTIFICATE REQUEST and X509 CRL. Whether it matters or not depends on the program that's going to be interpret the data. You can always use the CRYPT_STRING_BASE64 flag to encode without headers and then add the correct header yourself.Jonathan Potter

1 Answers

1
votes

According to the document:

CRYPT_STRING_BASE64HEADER(0x00000000):Base64, with certificate beginning and ending headers.

The expected header is "BEGIN CERTIFICATE". And there is no other flag parameter support for "BEGIN PUBLIC KEY". As @Jonathan Potter says, you can always use the CRYPT_STRING_BASE64 flag to encode without headers and then add the correct header yourself.

And, here is another answer about getting PUBILC from CERTIFICATE with openssl.