0

My goal is to use CryptoAPI to take some bytes of a signed and encoded certificate made in CryptoAPI and convert them to B64. Problem is, the certificate header is being tacked on but the output is not B64!

My output:

-----BEGIN CERTIFICATE-----MIIDfjCCAmagAwIBAgIBAjANBgkqhkiG9w0BAQsFADBoMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xEDAOBgNVBAoMB0FVVE9TT0wxDzANBgNVBAsMBkNNREVQVDEUMBIGA1UEAwwLbmFtZWRwaXBlQ0EwHhcNMTgwMTI2MTk0NDQ2WhcNMTkwMTI2MTk0NDQ2WjBlMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xDDAKBgNVBAoMA0RpczEmMCQGA1UEAwwdU1ZSLkNNMDA0MDlEOTg4RTk1LjE1MTY5OTQ5OTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD28NOZkuJbUsU0COQFrJgMgzGaj/afj6lYKZyTr5oXtenSmTAXN0WJE7Wd7DCfKqPpbnui4mb62Tgn3NCyMyEHY5jzFUWaUIchlmVzkamQzkmE3g99Fhj3EZP9zAEQE7qs7p5aKcgzIHuMRDB16Ap7/GcFLTUBXcval17yOyU+J9csiywTRA54IK8nvtXzYVvgDSKXOh02VEU9RIjE4C069PKGg04lwZNHm8KvuPJn70PXQQnDaoSkbyvh46lKGhJUsNzNsV0Dpk1owrV9jCCUhpOKdA61Ye8P1oFgLkyu8VV4FjJL7/t7AwaV7AgV7fVtOQ1fZ1HU4VheDD+Q23snAgMBAAGjNjA0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIFoDANBgNVHQoEBjAEAwIGQDANBgkqhkiG9w0BAQsFAAOCAQEAShaqwVJvEjg9n4Tw24hBvl5pXpRGlUjgOQHDsk9sf8WWlDQWZzdRbaw9Y2QBEPuBbxbnF/voiUwYIhWrIYZziZDmx4mHuM1tt7Dyo79pAaJ6KrYkEzOoLP9VCuC1qHsux9cwYb1WiJmJtIZi22aFvAXDCQ3cDr6ej+PbgrXOL+oaS3b95F2ds6zWhDjyFwBLldkAXB4P/GiiOnS3X85DVxWLzY+y88hoKqBYJq5vaAIdLHlqA6jZJYuR2VUjVixggLclAeGUYO9ljLcyS9aer0DFJvdCMKJyUfcN6t6s/tDsKO7nrJrPNIVxbXfg8KzVnWG3Px9KTF9u9bt2G3kJNg==-----END CERTIFICATE-----

If I paste the above text here and try to decode it, it fails...its not B64: https://www.base64decode.org/

If I go to https://www.base64encode.org/ and paste in the above, the tool converts it to B64 which I can submit via the in-house API call I need to make and OpenSSL is happy with it. I need to use the last two function calls in the code sample to do this, but its not working. No errors are being thrown.

Code:

std::string sCertificate;
HCRYPTPROV hCaProv = NULL;
PCRYPT_KEY_PROV_INFO pKeyInfo_CA = NULL;
CERT_INFO serverCertInfo;
CRYPT_ALGORITHM_IDENTIFIER signAlgo;
DWORD cbEncodedCert_SERVER = 0;
LPBYTE pbEncodedCert_SERVER = NULL;
DWORD dwB64CertificateLength;
LPSTR lpstrServerCertificate = NULL;

//...
//do a million things to process a CSR and make a certificate ready to be signed

//use CryptSignAndEncodeCertificate to figure out how much space to allocate for the certificate
if (!CryptSignAndEncodeCertificate(
    hCaProv,
    pKeyInfo_CA->dwKeySpec,
    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    X509_CERT_TO_BE_SIGNED,
    (void*)&serverCertInfo,
    &signAlgo,
    NULL,
    NULL,
    &cbEncodedCert_SERVER
))
{
    LocalFree(pbEnhancedUsage);
    LocalFree(pbUsage);
    LocalFree(pbEncodedBasicConstraints);
    CertFreeCertificateContext(pCACertContext);
    CryptReleaseContext(hCaProv, 0);
    CertCloseStore(hStoreRoot, 0);
    throw std::runtime_error("Unable to sign and encode certificate");
}

//allocate space for the certificate
pbEncodedCert_SERVER = (LPBYTE)LocalAlloc(0, cbEncodedCert_SERVER);

//use CryptSignAndEncodeCertificate to sign and encode the new certificate
if (!CryptSignAndEncodeCertificate(
    hCaProv,
    pKeyInfo_CA->dwKeySpec,
    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
    X509_CERT_TO_BE_SIGNED,
    (void*)&serverCertInfo,
    &signAlgo,
    NULL,
    pbEncodedCert_SERVER,
    &cbEncodedCert_SERVER
))
{
    LocalFree(pbEnhancedUsage);
    LocalFree(pbUsage);
    LocalFree(pbEncodedBasicConstraints);
    CertFreeCertificateContext(pCACertContext);
    LocalFree(pbEncodedCert_SERVER);
    CryptReleaseContext(hCaProv, 0);
    CertCloseStore(hStoreRoot, 0);
    throw std::runtime_error("Unable to sign and encode certificate");
}

//free up a bunch of memory we don't need anymore
CertFreeCertificateContext(pCACertContext);
CryptReleaseContext(hCaProv, 0);
CertCloseStore(hStoreRoot, 0);
LocalFree(pbEnhancedUsage);
LocalFree(pbUsage);
LocalFree(pbEncodedBasicConstraints);

//convert to B64 (call #1 to get length)
if (!CryptBinaryToStringA(
    pbEncodedCert_SERVER,
    cbEncodedCert_SERVER,
    CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCRLF,
    NULL,
    &dwB64CertificateLength
))
{
    LocalFree(pbEncodedCert_SERVER);
    throw std::runtime_error("Could not get size of B64 string for server certificate");
}

//allocate space based on call #1
lpstrServerCertificate = (LPSTR)LocalAlloc(0, dwB64CertificateLength);

//convert to B64 (call #2 to encode)
if (!CryptBinaryToStringA(
    pbEncodedCert_SERVER,
    cbEncodedCert_SERVER,
    CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCRLF,
    lpstrServerCertificate,
    &dwB64CertificateLength
))
{
    LocalFree(pbEncodedCert_SERVER);
    LocalFree(lpstrServerCertificate);
    throw std::runtime_error("Could not make B64 string of server certificate");
}

//put the certificate text into a std::string to pass it out of the function and be done
sCertificate = lpstrServerCertificate;

//free resources
LocalFree(pbEncodedCert_SERVER);
LocalFree(lpstrServerCertificate);

Suggestions? I feel I might be doing a simple mistake here but I don't know what.

jww
  • 97,681
  • 90
  • 411
  • 885
Timothy John Laird
  • 1,101
  • 2
  • 13
  • 24

1 Answers1

0

I would think that CryptBinaryToString would actually convert the result to B64 on first pass...for some reason another pass was required. Seems silly to have to call the same function 4 times to do the job but apparently its necessary. I added this code and called it on sCertificate the code above and now I have something in B64 that works. Go figure. Hopefully another person gets something out of this.

bool UTIL::EncodeB64(
    std::string sToEncode, //data to encode in B64 [IN]
    std::string & sEncoded //encoded data [OUT]
    )
{
    sEncoded = "";
    LPSTR lpstrEncoded = NULL;
    DWORD dwB64Length;
    std::vector<BYTE> vcharBytes(sToEncode.begin(), sToEncode.end());

    //convert to B64 (call #1 to get length)
    if (!CryptBinaryToStringA(
        &vcharBytes[0],
        vcharBytes.size(),
        CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
        NULL,
        &dwB64Length
    ))
    {
        //crashed and burned
        return false;
    }

    //allocate space based on call #1
    lpstrEncoded = (LPSTR)LocalAlloc(0, dwB64Length);

    //convert to B64 (call #2 to encode)
    if (!CryptBinaryToStringA(
        &vcharBytes[0],
        vcharBytes.size(),
        CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
        lpstrEncoded,
        &dwB64Length
    ))
    {
        //crashed and burned
        LocalFree(lpstrEncoded);
        return false;
    }

    //set result that we are going to pass out
    sEncoded = lpstrEncoded;

    //free memory
    LocalFree(lpstrEncoded);

    //success
    return true;
}

Added to calling code:

std::string sB64Pass1Certificate = lpstrServerCertificate;

LocalFree(pbEncodedCert_SERVER);
LocalFree(lpstrServerCertificate);

if (!EncodeB64(
    sB64Pass1Certificate,
    sCertificate
))
{
    throw std::runtime_error("Could not make B64 string of server certificate (second pass)");
}
Timothy John Laird
  • 1,101
  • 2
  • 13
  • 24