Search code examples
ccertificatex509mbedtls

mbedtls cannot parse valid x509 certificate


I have the following certificate:

-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIVAJ3wzBnLSnQvYi31rNVQRAXDUO/zMA0GCSqGSIb3DQEB
CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t
IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0yMDA3MjgxMTMz
MTJaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh
dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHc2tmezGoekLjkQlb
+YOBKFyPswYR+GLq/JRVbFX2k4OrHF5js4GTfbHm1oQ733KbcnIugdejtQnRhtnr
1HRk3pqedVhRKGRo2DFDYyuX3K1UR6xna1poJF+6WNy6vXGxIQYKi7SNS5LtzkRT
1FCziOLBaxfcCRNgR1NBHjlcFsUWyL4evMok6h/wU7HA3/dfKEisyLdh3sMy7Yox
Im/ldvyX+9pH7Hj0TrGGTd5f8GtX8npNuSKdkntuag95r+vAaAPp6bQVyPWm8T/G
SUN8N7Nvc9DOcJ8ZhvB/Ubq+Fa/eoUnr3SgXInufLHhrfxJW7dyrBTlw/1kdXgYw
YiKnAgMBAAGjYDBeMB8GA1UdIwQYMBaAFP4UzdqnzQ4l89+D7UhXC5MKWnOJMB0G
A1UdDgQWBBSn95OHFqTn3DrE3anpNq5RoOsT+DAMBgNVHRMBAf8EAjAAMA4GA1Ud
DwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEA2Hvrxy2N0xt3I/w/7JIyoTH4
ixUKMaD1QXe+g6LrsQSCVVsaq0L468OpyydVzQLQONXvDDRv3rqIEel1hPAJNG0y
dp3g+WC1dPl7E44btM+59gBf1369lFwV6FbJMwCltVBUJ4hFAjt3QTkWRHq6DlFQ
wa896aSr5UUiVNAJjf/hLVjERlVG4wDjPN7YifQssRqlNcYDgok3UhVsBfKIGnct
WFbisX+0ONMyNnE1Qq6bX5g4sLN7VlwFhADiz1Xp2rUtLECR1NSPutYibWyvJJ8d
htYYV1a0FSkg7JKyvOIJ8IYKEPsKE+UYo1Z8DwkmHHcap+h0OMWAnKQgRXn6QQ==
-----END CERTIFICATE-----

I fed this into several certificate reading sites, and they were all able to parse and display its contents.

I tried using mbedtls to parse this certificate using the following code:

mbedtls_x509_crt certificate;
mbedtls_x509_crt_init(&certificate);
        
char certificate_string[] = "-----BEGIN CERTIFICATE--..."
            
int result_code = mbedtls_x509_crt_parse(&certificate, (unsigned char*)certificate_string, strlen(certificate_string));
        
if(result_code != 0) {
    char err_str[256];
    mbedtls_strerror(result_code, err_str, 256);
    printf("Could not read the certificate. Error: %s\n", err_str);
    return -1;
}

I then check the result_code for 0, and print the error message if it is not. I get the following error message every time I try to parse this certificate: "Could not read the certificate. Error: X509 - The CRT/CRL/CSR format is invalid, e.g. different type expected"

I tried looking at the mbedtls_x509_crt_parse code to see what causes this message, and I then modified the code to use the following pieces of mbedtls_x509_crt_parse instead:

mbedtls_pem_context pem;
size_t use_len;
mbedtls_pem_init(&pem);

// If we get there, we know the string is null-terminated 
int ret = mbedtls_pem_read_buffer(&pem, 
                                "-----BEGIN CERTIFICATE-----", 
                                "-----END CERTIFICATE-----",
                                (unsigned char *)certificate_string, 
                                NULL, 
                                0, 
                                &use_len);

if(ret != 0) {
    printf("we could not pem read the string\n");
    return -1;
}
else {
    printf("We pem read the certificate\n");
}

ret = mbedtls_x509_crt_parse_der(&certificate, pem.buf, pem.buflen);

if(ret != 0) {
    printf("crt parse der has failed\n");
}
else {
    printf("The issuer is: %s\n", certificate.issuer.val.p);
    return 0;
}

When I run the program, I get the following output:

491231235959Z010�Uzon Web Services O=Amazon.com Inc. L=Seattle ST=Washington C=US0 *�H�� AWS IoT Certificate0�"0

I kept searching for answers as to what may be wrong, and I found a post saying that mbedtls is configured by default to use RSA 1024, so if your key is 2048 (and it is in mine) then mbedtls will have an error with parsing. I modified the configuration file to use 2048 and I rebuilt the library, but I still get errors.

Any ideas? I feel like I am really close, because mbedtls_x509_crt_parse executes almost the whole way through. I am pretty sure I am using the library correctly based on code samples I have seen.

Thanks!


Solution

  • Initially, the PEM format certificate string was parsed with the following code:

    mbedtls_x509_crt certificate;
    mbedtls_x509_crt_init(&certificate);
            
    char certificate_string[] = "-----BEGIN CERTIFICATE--..."; // actually much longer
                
    int result_code = mbedtls_x509_crt_parse(&certificate, (unsigned char*)certificate_string, strlen(certificate_string));
    

    That resulted in a parsing error because for PEM format input, the final argument of the call to mbedtls_x509_crt certificate should be the length of the input including the null terminator. Changing the final argument to 1 + strlen(certificate_string) fixes the issue.


    After successfully parsing, the issuer string was printed using:

    printf("The issuer is: %s\n", certificate.issuer.val.p);
    

    That produced some junk output that looked as if the initial part of the issuer string had been overwritten, but was actually due to the lack of a null terminator in the issuer string. The bytes of data after the issuer string included ASCII CR characters causing the terminal cursor position to move to the start of the line and print over the initial part of the output. (The CR characters can be seen by piping the output through | od -c for example, wherewith they are displayed as \r.)

    Piping the output through | od -c produces:

    0000000   T   h   e       i   s   s   u   e   r       i   s   :       A
    0000020   m   a   z   o   n       W   e   b       S   e   r   v   i   c
    0000040   e   s       O   =   A   m   a   z   o   n   .   c   o   m
    0000060   I   n   c   .       L   =   S   e   a   t   t   l   e       S
    0000100   T   =   W   a   s   h   i   n   g   t   o   n       C   =   U
    0000120   S   0 036 027  \r   2   0   0   7   2   8   1   1   3   3   1
    0000140   2   Z 027  \r   4   9   1   2   3   1   2   3   5   9   5   9
    0000160   Z   0 036   1 034   0 032 006 003   U 004 003  \f 023   A   W
    0000200   S       I   o   T       C   e   r   t   i   f   i   c   a   t
    0000220   e   0 202 001   "   0  \r 006  \t   * 206   H 206 367  \r 001
    0000240 001 001 005  \n
    0000244
    

    That shows unprintable bytes as 3-digit octal codes or as C backslash escape codes, depending on the byte value.

    To print the issuer string without the junk, change the printf call to the following:

    printf("The issuer is: %.*s\n", (int)certificate.issuer.val.len, certificate.issuer.val.p);