Search code examples
c++opensslpemderasn.1

OpenSsl cannot read DER formatted certificate


Update

I have based my solution on this and this answers.

Background

I am trying to read a DER formatted certificate file and attempt to verify it.

My cert is in DER format. I have confirmed this by:

Using openssl command line:

  • openssl x509 -text -noout -inform DER -in Cert.cer: displays certificate

  • openssl x509 -text -noout -in Cert.cer: displays unable to load certificate

  • openssl x509 -inform der -in Cert.cer -out Cert.pem: converts DER to PEM

I am using the below code to read:

static std::vector<char> ReadAllBytes(char const* filename)
{
    std::cout << "in ReadAllBytes(" << filename << ")" << std::endl;
    std::ifstream stream(filename, std::ios::in | std::ios::binary);
    std::vector<char> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());

    std::cout << "out ReadAllBytes" << std::endl;

    return contents;
}

int main(int argc, char **argv)
{
    OpenSSL_add_all_algorithms();

    auto readBytes = ReadAllBytes("Cert.cer");
    std::cout << "after ReadAllBytes, read size:" << readBytes.size() << std::endl;
    BIO *bio_mem = BIO_new(BIO_s_mem());
    BIO_puts(bio_mem, readBytes.data());
    X509 * x509 = d2i_X509_bio(bio_mem, NULL);

    // PEM format
    //X509 *x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);

    if(x509 == NULL){
        unsigned int errCode = ERR_get_error();

        printf("\nError: %s\n", ERR_error_string(errCode, NULL));
        printf("\nLib: %s\n", ERR_lib_error_string(errCode));
        printf("\nFunc: %s\n", ERR_func_error_string(errCode));
        printf("\nReason: %s\n", ERR_reason_error_string(errCode));
    }

    BIO_free(bio_mem);
    X509_free(x509);
}

The output:

in ReadAllBytes(Cert.cer)
out ReadAllBytes
after ReadAllBytes, read size:1033

Error: error:0D06B08E:lib(13):func(107):reason(142)

Lib: (null)

Func: (null)

Reason: (null)

Updated output after calling ERR_load_crypto_strings();:

Error: error:0D06B08E:asn1 encoding routines:ASN1_D2I_READ_BIO:not enough data

Lib: asn1 encoding routines

Func: ASN1_D2I_READ_BIO

Reason: not enough data

Issue

d2i_X509_bio(bio_mem, NULL) returns NULL.

I have successfully read the PEM formatted cert after converting with: X509 *x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);

Questions

  • Is there something wrong in my code that I have missed?

  • How can I read a DER formatted x509 certificate file with openssl?


Solution

  • It looks like your problem is that you passed a data blob as a string.

    BIO_puts (put string) copies up to the first zero-valued byte. Odds are this is somewhere in the middle of your certificate, which is why you're getting "not enough data" (a DER length value ends up being bigger than the length of the BIO data). (If your certificate had no zeros then it'll read too far and copy too much; be really careful calling functions that take pointers but not length).

    BIO_write, on the other hand, writes the specified amount of data.

    So instead of BIO_puts(bio_mem, readBytes.data()) you want BIO_write(bio_mem, readBytes.data(), readBytes.size()).

    Technically, you should write to BIO_write in a loop, checking the return value (how many bytes it accepted for write), but the BIO_MEM always either critically fails or succeeds in one call.

    (It turns out that BIO_MEM isn't a stream (a data segment with a position) but a pipe (a data segment with a read position and a write position), so it doesn't need to be rewound after writing to it.)