Load an Pem Encoded X.509 Certificate into Windows Cryptoapi

Load an PEM encoded X.509 certificate into Windows CryptoAPI

KJKHyperion said in his answer:

I discovered the "magic" sequence of calls to import a RSA public key in PEM format. Here you go:

  1. decode the key into a binary blob with CryptStringToBinary; pass CRYPT_STRING_BASE64HEADER in dwFlags
  2. decode the binary key blob into a CERT_PUBLIC_KEY_INFO with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and X509_PUBLIC_KEY_INFO in lpszStructType
  3. decode the PublicKey blob from the CERT_PUBLIC_KEY_INFO into a RSA key blob with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and RSA_CSP_PUBLICKEYBLOB in lpszStructType
  4. import the RSA key blob with CryptImportKey

This sequence really helped me understand what's going on, but it didn't work for me as-is. The second call to CryptDecodeObjectEx gave me an error:
"ASN.1 bad tag value met".
After many attempts at understanding Microsoft documentation, I finally realized that the output of the fist decode cannot be decoded as ASN again, and that it is actually ready for import. With this understanding I found the answer in the following link:

http://www.ms-news.net/f2748/problem-importing-public-key-4052577.html

Following is my own program that imports a public key from a .pem file to a CryptApi context:

int main()
{
char pemPubKey[2048];
int readLen;
char derPubKey[2048];
size_t derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
int publicKeyInfoLen;
HANDLE hFile;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;

/*
* Read the public key cert from the file
*/
hFile = CreateFileA( "c:\\pub.pem", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
fprintf( stderr, "Failed to open file. error: %d\n", GetLastError() );
}

if ( !ReadFile( hFile, pemPubKey, 2048, &readLen, NULL ) )
{
fprintf( stderr, "Failed to read file. error: %d\n", GetLastError() );
}

/*
* Convert from PEM format to DER format - removes header and footer and decodes from base64
*/
if ( !CryptStringToBinaryA( pemPubKey, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen, NULL, NULL ) )
{
fprintf( stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError() );
}

/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if ( !CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen,
CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
{
fprintf( stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError() );
return -1;
}

/*
* Acquire context
*/
if( !CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
{
{
printf( "CryptAcquireContext failed - err=0x%x.\n", GetLastError() );
return -1;
}
}

/*
* Import the public key using the context
*/
if ( !CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey ) )
{
fprintf( stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError() );
return -1;
}
LocalFree( publicKeyInfo );

/*
* Now use hKey to encrypt whatever you need.
*/

return 0;
}

Import PEM encoded X.509 certificate into iOS KeyChain

The following calls were missing before creating the PKCS12:

OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();

These solved the problems with the missing cipher and also a subsequent problem of a missing digest.

rsa encryption in windows crypto and decryption in openssl

CryptEncrypt, for reasons that probably made sense to the author at the time, writes the bytes down backwards. Or, rather, it writes them in byte minor/little-endian order whereas almost every other cryptography library (including the Windows CNG BCryptEncrypt and NCryptEncrypt routines) writes them in byte major/big-endian order.

So you need to reverse data coming out of CryptEncrypt, and reverse it going in to CryptDecrypt.

For example, in .NET Core's RSACryptoServiceProvider.Encrypt it calls CapiHelper.EncryptKey which calls CryptEncrypt then Array.Reverse before returning.

The CryptEncrypt documentation has as the last sentence in the Remarks section

The ciphertext is returned in little-endian format.

Parsing and printing X.509 attributes from PEM

You want to use encoding.pem to decode the pem file, which will give you the DER blocks you can decode with the crypto/x509 package.

For example:

certPEMBlock, err := ioutil.ReadFile(certFile)
if err != nil {
log.Fatal(err)
}

var blocks [][]byte
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}

if certDERBlock.Type == "CERTIFICATE" {
blocks = append(blocks, certDERBlock.Bytes)
}
}

for _, block := range blocks {
cert, err := x509.ParseCertificate(block)
if err != nil {
log.Println(err)
continue
}

fmt.Println("Certificate:")
fmt.Printf("\tSubject: %+v\n", cert.Subject)
fmt.Printf("\tDNS Names: %+v\n", cert.DNSNames)
fmt.Printf("\tEmailAddresses: %+v\n", cert.EmailAddresses)
fmt.Printf("\tIPAddresses: %+v\n", cert.IPAddresses)
}


Related Topics



Leave a reply



Submit