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.
This commit is contained in:
Lennart Grahl 2016-11-02 17:37:25 +01:00 committed by Richard Aas
parent a627951aeb
commit 1ff344dbd9
2 changed files with 140 additions and 21 deletions

View file

@ -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);

View file

@ -8,6 +8,8 @@
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
@ -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)
{