From 1ff344dbd944f91a5600809dc79438bacddfa275 Mon Sep 17 00:00:00 2001 From: Lennart Grahl Date: Wed, 2 Nov 2016 17:37:25 +0100 Subject: [PATCH] Setting Certificate/Private Key Improvements (#25) Add tls_set_certificate_pem() and tls_set_certificate_der() functions for setting certificate and private key on a TLS context in respective formats. Private key may optionally be passed as a separate argument, or part of the certificate data. --- include/re_tls.h | 10 +++ src/tls/openssl/tls.c | 151 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 140 insertions(+), 21 deletions(-) diff --git a/include/re_tls.h b/include/re_tls.h index c1feda0..70c3201 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -24,11 +24,21 @@ enum tls_fingerprint { TLS_FINGERPRINT_SHA256, }; +enum tls_keytype { + TLS_KEYTYPE_RSA, + TLS_KEYTYPE_EC, +}; + int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, const char *pwd); int tls_add_ca(struct tls *tls, const char *capath); int tls_set_selfsigned(struct tls *tls, const char *cn); +int tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert, + const char *key, size_t len_key); +int tls_set_certificate_der(struct tls *tls, enum tls_keytype key_type, + const uint8_t *cert, size_t len_cert, const uint8_t *key, + size_t len_key); int tls_set_certificate(struct tls *tls, const char *cert, size_t len); void tls_set_verify_client(struct tls *tls); int tls_set_srtp(struct tls *tls, const char *suites); diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index 31ae997..0daff08 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -72,6 +74,19 @@ static int password_cb(char *buf, int size, int rwflag, void *userdata) } +static int keytype2int(enum tls_keytype type) +{ + switch (type) { + case TLS_KEYTYPE_EC: + return EVP_PKEY_EC; + case TLS_KEYTYPE_RSA: + return EVP_PKEY_RSA; + default: + return EVP_PKEY_NONE; + } +} + + /** * Allocate a new TLS context * @@ -337,55 +352,63 @@ int tls_set_selfsigned(struct tls *tls, const char *cn) /** * Set the certificate and private key on a TLS context * - * @param tls TLS Context - * @param pem Certificate and private key in PEM format - * @param len Length of PEM string + * @param tls TLS Context + * @param cert Certificate in PEM format + * @param len_cert Length of certificate PEM string + * @param key Private key in PEM format, will be read from cert if NULL + * @param len_key Length of private key PEM string * * @return 0 if success, otherwise errorcode */ -int tls_set_certificate(struct tls *tls, const char *pem, size_t len) +int tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert, + const char *key, size_t len_key) { BIO *bio = NULL, *kbio = NULL; - X509 *cert = NULL; - RSA *rsa = NULL; + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; int r, err = ENOMEM; - if (!tls || !pem || !len) + if (!tls || !cert || !len_cert || (key && !len_key)) return EINVAL; - bio = BIO_new_mem_buf((char *)pem, (int)len); - kbio = BIO_new_mem_buf((char *)pem, (int)len); + if (!key) { + key = cert; + len_key = len_cert; + } + + bio = BIO_new_mem_buf((char *)cert, (int)len_cert); + kbio = BIO_new_mem_buf((char *)key, (int)len_key); if (!bio || !kbio) goto out; - cert = PEM_read_bio_X509(bio, NULL, 0, NULL); - rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL); - if (!cert || !rsa) + x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); + pkey = PEM_read_bio_PrivateKey(kbio, NULL, 0, NULL); + if (!x509 || !pkey) goto out; - r = SSL_CTX_use_certificate(tls->ctx, cert); + r = SSL_CTX_use_certificate(tls->ctx, x509); if (r != 1) goto out; - r = SSL_CTX_use_RSAPrivateKey(tls->ctx, rsa); + r = SSL_CTX_use_PrivateKey(tls->ctx, pkey); if (r != 1) { - DEBUG_WARNING("set_certificate: use_RSAPrivateKey failed\n"); + DEBUG_WARNING("set_certificate_pem: use_PrivateKey failed\n"); goto out; } if (tls->cert) X509_free(tls->cert); - tls->cert = cert; - cert = NULL; + tls->cert = x509; + x509 = NULL; err = 0; out: - if (cert) - X509_free(cert); - if (rsa) - RSA_free(rsa); + if (x509) + X509_free(x509); + if (pkey) + EVP_PKEY_free(pkey); if (bio) BIO_free(bio); if (kbio) @@ -396,6 +419,92 @@ int tls_set_certificate(struct tls *tls, const char *pem, size_t len) return err; } +/** + * Set the certificate and private key on a TLS context + * + * @param tls TLS Context + * @param keytype Private key type + * @param cert Certificate in DER format + * @param len_cert Length of certificate DER bytes + * @param key Private key in DER format, will be read from cert if NULL + * @param len_key Length of private key DER bytes + * + * @return 0 if success, otherwise errorcode + */ +int tls_set_certificate_der(struct tls *tls, enum tls_keytype keytype, + const uint8_t *cert, size_t len_cert, const uint8_t *key, + size_t len_key) +{ + const uint8_t *buf_cert; + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + int r, type, err = ENOMEM; + + if (!tls || !cert || !len_cert || (key && !len_key)) + return EINVAL; + + type = keytype2int(keytype); + if (type == EVP_PKEY_NONE) + return EINVAL; + + buf_cert = cert; + + x509 = d2i_X509(NULL, &buf_cert, len_cert); + if (!x509) + goto out; + + if (!key) { + key = buf_cert; + len_key = len_cert - (buf_cert - cert); + } + + pkey = d2i_PrivateKey(type, NULL, &key, len_key); + if (!pkey) + goto out; + + r = SSL_CTX_use_certificate(tls->ctx, x509); + if (r != 1) + goto out; + + r = SSL_CTX_use_PrivateKey(tls->ctx, pkey); + if (r != 1) { + DEBUG_WARNING("set_certificate_der: use_PrivateKey failed\n"); + goto out; + } + + if (tls->cert) + X509_free(tls->cert); + + tls->cert = x509; + x509 = NULL; + + err = 0; + + out: + if (x509) + X509_free(x509); + if (pkey) + EVP_PKEY_free(pkey); + if (err) + ERR_clear_error(); + + return err; +} + +/** + * Set the certificate and private key on a TLS context + * + * @param tls TLS Context + * @param pem Certificate and private key in PEM format + * @param len Length of PEM string + * + * @return 0 if success, otherwise errorcode + */ +int tls_set_certificate(struct tls *tls, const char *pem, size_t len) +{ + return tls_set_certificate_pem(tls, pem, len, NULL, 0); +} + static int verify_handler(int ok, X509_STORE_CTX *ctx) {