Reputation: 43
Hello and thanks in advance.
I am new to mbedtls. The following would be pretty simple using openssl, however I do not know where to start here.
I need to capture the certificate chain from a server like https://example.com as a string similar to the string example below. The formatting should be the easy part, I am just at a loss of what exactly to use. I am assuming the x509 module.
"-----BEGIN CERTIFICATE-----\n"
"MIIGrTCCBZWgAwIBAgIQBFkU5B02DI8ZdB9c2V/1CzANBgkqhkiG9w0BAQsFADBc\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMRswGQYDVQQDExJUaGF3dGUgUlNBIENBIDIwMTgwHhcN\n"
"MTgwNjAxMDAwMDAwWhcNMjAwNTMxMTIwMDAwWjCBnDELMAkGA1UEBhMCVVMxEDAO\n"
"BgNVBAgTB0Zsb3JpZGExGjAYBgNVBAcTEUFsdGFtb250ZSBTcHJpbmdzMSkwJwYD\n"
"VQQKEyBCcmlkZ2VwYXkgTmV0d29yayBTb2x1dGlvbnMsIExMQzELMAkGA1UECxMC\n"
"SVQxJzAlBgNVBAMTHnBnYy5icmlkZ2VwYXluZXRzZWN1cmV0ZXN0LmNvbTCCASIw\n"
"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOgeLoYsgYB2EVkox4YKBaeiUg93\n"
"05S+BV1mj52hY4SA4a+Uv2bGPA34uryUyTzBBzs9V40ugmybgpRiEe7ImaVBRRvK\n"
"VEftTC2qi5o9y/HySIgHlSyruVFUGGweCz6v32A/4WRXrYMubTcxDPb8eZSz1QKm\n"
"pTSjHoAeCftlclwAHSvATz78whhEhbpQudYGBsRjqZVdeEcClGP3ukQuDAdZwjqh\n"
"OAUKH2THEXtvtJYWyyWZSGJm4/FMZnhNRqQKFaf5Pz4rxvM3bNOqzTqj4BmA9def\n"
"5tcDwblTUBpZ37M0rNJfSebnSc/XrR9Urc1vkJugFAykYptwNoYWhQJ+x2sCAwEA\n"
"AaOCAygwggMkMB8GA1UdIwQYMBaAFKPIXmVU5TB4wQXqBwpqWcy5/t5aMB0GA1Ud\n"
"DgQWBBQGXyzHHolmD/seY+LpqVf3ozFmITApBgNVHREEIjAggh5wZ2MuYnJpZGdl\n"
"cGF5bmV0c2VjdXJldGVzdC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG\n"
"CCsGAQUFBwMBBggrBgEFBQcDAjA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY2Rw\n"
"LnRoYXd0ZS5jb20vVGhhd3RlUlNBQ0EyMDE4LmNybDBMBgNVHSAERTBDMDcGCWCG\n"
"SAGG/WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v\n"
"Q1BTMAgGBmeBDAECAjBvBggrBgEFBQcBAQRjMGEwJAYIKwYBBQUHMAGGGGh0dHA6\n"
"Ly9zdGF0dXMudGhhd3RlLmNvbTA5BggrBgEFBQcwAoYtaHR0cDovL2NhY2VydHMu\n"
"dGhhd3RlLmNvbS9UaGF3dGVSU0FDQTIwMTguY3J0MAkGA1UdEwQCMAAwggGABgor\n"
"BgEEAdZ5AgQCBIIBcASCAWwBagB3AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3\n"
"zQ7IDdwQAAABY7v2qJEAAAQDAEgwRgIhAM5Tsrb1lqzE4D4LtQqmWO9lCH7XzyEb\n"
"2qvqldqmhxoEAiEA2tBlvXiMI0WysQqhF7RUipBF4YMdsyjMVZ9tLv9uyTsAdwCH\n"
"db/nWXz4jEOZX73zbv9WjUdWNv9KtWDBtOr/XqCDDwAAAWO79qk2AAAEAwBIMEYC\n"
"IQDB8ZyF+GuzBzihgkJgUxVcZ4YjntDmyVURrqLp8aycwQIhAPPOCZG8ZwIfY2up\n"
"Y4DjH2QhlRIjE0rsEPUhMEi+EtbeAHYAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUuga\n"
"kJZkNo4e0YUAAAFju/apgQAABAMARzBFAiEA8Z6N8cb1D6XPsu9fcPIvfjuLDyYJ\n"
"KlpY3GRvVxq+jsMCIE+zdbBSEcc4SkIWTx/vvj8THcaMVX/OKWrqCKVuVHWbMA0G\n"
"CSqGSIb3DQEBCwUAA4IBAQBmQt8QfGKW8/c+o6fZvBAWtwgPnitKgiWvBwIvMlYr\n"
"6teFYkRR0qe+vQBWcHF/ax5VyDFHH/MjZLqCzoR0VJKBz1uNTXDYYgfwrwy9EFPt\n"
"s9bFiZerIZwBHO55HmpWpvmrT6V178gJOFTGppUbwxuwHWan8075Q2MfVZpuP/kw\n"
"0BYJxeFC09tdgz5CiWRJMsAVvYbqr2Dkdrc1IAERQ782qTMbwujCvojpKmIt5w16\n"
"UfUY02ICqQ3XgXU/iwMSb3XpnEvP6BIliMgdyW8wW493dEpbZs1igWSct8U1f5bH\n"
"YVluRgq/O02MQZgmu6tDXFVd9X9NY/TBwtjfiQ35Vmn1\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIEiTCCA3GgAwIBAgIQAlqK7xlvfg1sIQSyGuZwKzANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
"QTAeFw0xNzExMDYxMjIzNTJaFw0yNzExMDYxMjIzNTJaMFwxCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xGzAZBgNVBAMTElRoYXd0ZSBSU0EgQ0EgMjAxODCCASIwDQYJKoZIhvcNAQEB\n"
"BQADggEPADCCAQoCggEBAMoIXuVTipccHkMvtoqnVumLhEOorJ16VYJ6FEuGty+P\n"
"Up8cyrEgW2+6It2mnC142ukGCE6+E6bry7s+uQUMPkrh8DIfE071BsVHc4k+gKOL\n"
"8QEkm6OZZpJraK0NLbTNcqL0+ThaZaa0jFPBCBqE+P0u8xF1btxqMSmsDYfMk2B4\n"
"3yW6JlmRxoNSNabKnLgoGs7XHO4Uv3ZcZas4HnnpfMxJIyaiUlBm0Flh/6D+mkwM\n"
"n/nojt4Ji7gVwaQITCacewbb/Yp0W1h+zWOkkS9F8Ho8lAuKfLIFqWeTn2jllWNg\n"
"2FiVX+BV75OnETt85pLYZkTgq72nj82khXhBJFTn2AMCAwEAAaOCAUAwggE8MB0G\n"
"A1UdDgQWBBSjyF5lVOUweMEF6gcKalnMuf7eWjAfBgNVHSMEGDAWgBQD3lA1VtFM\n"
"u2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH\n"
"AwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm\n"
"MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDsw\n"
"OTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFs\n"
"Um9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0\n"
"cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzANBgkqhkiG9w0BAQsFAAOCAQEARE2F\n"
"5d0cgozhZNWokCLfdhhl6mXSOyU3SoPamYcWfLH1CzMwD8a1+pFvwHIQfvlwXFH8\n"
"MrjB3C+jVobNbVWRrgqS3Jsa0ltRH/Ffs6ZTgP4WJYm1SNpUbgR7LWUD2F+PTvKB\n"
"M/gf9eSyqP4OiJslYaa38NU1aVAxZI15o+4xX4RZMqKXIIBTG2V+oPBjQ1oPmHGA\n"
"C/yWt2eThvb8/re7OpSpUdJyfGf97XeM4PiJAl6+4HQXhjwN7ZPZKrQv9Ay33Mgm\n"
"YLVQA+x9HONZXx9vvy8pl9bu+NVYWKGxzGxBK0CBozmVUCeXQPJKPTZleYuNM18p\n"
"U1P8Xh1CDguM+ZEoew==\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
"-----END CERTIFICATE-----";
Upvotes: 2
Views: 2165
Reputation: 4620
The short version: The mbedtls library represents certificates with the mbedtls_x509_crt
struct (reference). This struct has a next
member. Certificate chains are represented as a linked list of mbedtls_x509_crt
entries, where next
points to the next entry in the chain or is nullptr
for the last entry in the chain. For an active SSL session, you can query the first entry in the certificate chain of the peer with mbedtls_ssl_get_peer_cert
(reference). You can then iterate over the certificate chain and print each certificate with mbedtls_pem_write_buffer
(reference).
For reference, I will first give an example on how to print the certificate chain with openssl (mostly to make sure we both agree on what the "easy" solution would be):
STACK_OF(X509) *certs = SSL_get_peer_cert_chain(ssl);
for (int i = 0; i < sk_X509_num(certs); i++) {
PEM_write_X509(stdout, sk_X509_value(certs, i));
}
The equivalent code with mbedtls would be:
const mbedtls_x509_crt *chain;
chain = mbedtls_ssl_get_peer_cert(&ssl);
while (chain != nullptr) {
std::cout << cert_to_pem(chain) << std::endl;
chain = chain->next;
}
(where cert_to_pem
is a custom helper function for serializing the certificate, see the full code below for how to implement this.)
Here is a full working example (based on the SSL client example given here):
#include "mbedtls/certs.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/entropy.h"
#include "mbedtls/error.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/pem.h"
#include "mbedtls/platform.h"
#include "mbedtls/ssl.h"
#include <array>
#include <iostream>
#include <vector>
/**
* Exception for printing mbedtls error codes.
*/
struct MbedtlsError : public std::runtime_error {
MbedtlsError(int err)
: std::runtime_error{[=]() {
constexpr size_t buffer_size{1024};
std::string buffer{};
buffer.resize(buffer_size);
mbedtls_strerror(err, buffer.data(), buffer.size());
return buffer;
}()} {}
};
/**
* Helper function for serializing certificates into PEM.
*
* @param crt the certificate to be encoded as PEM
* @returns the pem encoding of the certificate
*/
std::string cert_to_pem(const mbedtls_x509_crt *crt) {
constexpr auto pem_begin_crt{"-----BEGIN CERTIFICATE-----\n"};
constexpr auto pem_end_crt{"-----END CERTIFICATE-----"};
constexpr size_t buffer_size{4096};
std::string buffer{};
size_t written{};
buffer.resize(buffer_size);
if (int ret = mbedtls_pem_write_buffer(
pem_begin_crt, pem_end_crt, crt->raw.p, crt->raw.len,
(unsigned char *)buffer.data(), buffer.size(), &written);
ret != 0) {
throw MbedtlsError{ret};
}
buffer.resize(written);
return buffer;
}
/**
* RAII helper for the mbedtls CTR_DRBG random-number generator.
*/
struct Random {
Random(const std::vector<uint8_t> &seed) {
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
if (int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
&entropy, seed.data(), seed.size());
ret != 0) {
throw MbedtlsError{ret};
}
}
~Random() {
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
}
mbedtls_entropy_context entropy{};
mbedtls_ctr_drbg_context ctr_drbg{};
};
/**
* RAII helper for the mbedtls socket connection.
*/
struct Connection {
Connection(const std::string &hostname, uint16_t port) : hostname_{hostname} {
mbedtls_net_init(&fd_);
if (int ret = mbedtls_net_connect(&fd_, hostname_.c_str(),
std::to_string(port).c_str(),
MBEDTLS_NET_PROTO_TCP);
ret != 0) {
throw MbedtlsError{ret};
}
}
~Connection() { mbedtls_net_free(&fd_); }
const std::string hostname_{};
mbedtls_net_context fd_{};
};
/**
* RAII helper for the mbedtls ssl layer.
*/
struct Ssl {
Ssl(Random &random, Connection &connection) {
mbedtls_ssl_config_init(&conf_);
mbedtls_ssl_init(&ssl_);
if (int ret = mbedtls_ssl_config_defaults(&conf_, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
ret != 0) {
throw MbedtlsError{ret};
}
// XXX Disable certificate verification for this example.
mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &random.ctr_drbg);
if (int ret = mbedtls_ssl_setup(&ssl_, &conf_); ret != 0) {
throw MbedtlsError{ret};
}
if (int ret = mbedtls_ssl_set_hostname(&ssl_, connection.hostname_.c_str());
ret != 0) {
throw MbedtlsError{ret};
}
mbedtls_ssl_set_bio(&ssl_, &connection.fd_, mbedtls_net_send,
mbedtls_net_recv, NULL);
if (int ret = mbedtls_ssl_handshake(&ssl_); ret != 0) {
throw MbedtlsError{ret};
}
}
~Ssl() {
mbedtls_ssl_close_notify(&ssl_);
mbedtls_ssl_free(&ssl_);
mbedtls_ssl_config_free(&conf_);
}
mbedtls_ssl_config conf_{};
mbedtls_ssl_context ssl_{};
};
int main(void) {
// TODO Initialize seed from /dev/random or another secure source.
std::vector<uint8_t> seed{};
// Initialize SSL connection.
Random random{seed};
Connection connection{"example.com", 443};
Ssl ssl{random, connection};
// Dump certificate chain.
const mbedtls_x509_crt *chain;
chain = mbedtls_ssl_get_peer_cert(&ssl.ssl_);
while (chain != nullptr) {
std::cout << cert_to_pem(chain) << std::endl;
chain = chain->next;
}
return EXIT_SUCCESS;
}
Usage:
$ g++ -std=c++17 -Wall -Wextra -pedantic -lmbedcrypto -lmbedx509 -lmbedtls test.cpp
$ ./a.out
-----BEGIN CERTIFICATE-----
MIIHQDCCBiigAwIBAgIQD9B43Ujxor1NDyupa2A4/jANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
...
Upvotes: 3