43#include <openssl/bn.h>
44#include <openssl/evp.h>
45#include <openssl/pem.h>
46#include <openssl/rsa.h>
47#include <openssl/x509.h>
48#include <openssl/x509v3.h>
51#define N_X509_KEY_BITS 2048
53#define N_X509_SERIAL_BITS 64
55#define N_X509_SAN_BUF 300
61 BIO_get_mem_ptr(bio, &mem);
62 if (!mem || !mem->data || mem->length == 0)
72 BIO* bio = BIO_new(BIO_s_mem());
76 if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL) == 1)
84 BIO* bio = BIO_new(BIO_s_mem());
88 if (PEM_write_bio_X509(bio, x) == 1)
96 BIGNUM* bn = BN_new();
101 ASN1_INTEGER* serial = X509_get_serialNumber(x);
102 if (serial && BN_to_ASN1_INTEGER(bn, serial) != NULL)
110static int add_ext(X509* issuer, X509* subject,
int nid,
const char* value) {
114 X509V3_set_ctx_nodb(&ctx);
115 X509V3_set_ctx(&ctx, issuer, subject, NULL, NULL, 0);
116 ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
118 if (X509_add_ext(subject, ext, -1) == 1)
120 X509_EXTENSION_free(ext);
126static int set_cn(X509_NAME* name,
const char* cn) {
127 if (X509_NAME_add_entry_by_txt(name,
"CN", MBSTRING_ASC, (
const unsigned char*)cn, -1, -1, 0) == 1)
134 unsigned char buf[16];
135 if (inet_pton(AF_INET, host, buf) == 1)
137 if (inet_pton(AF_INET6, host, buf) == 1)
145 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
146 EVP_PKEY* pkey = NULL;
149 if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0)
151 EVP_PKEY_CTX_free(ctx);
163 n_log(
LOG_ERR,
"n_x509: RSA key generation failed (%d bits)", bits);
172 EVP_PKEY* pkey = NULL;
190 if (X509_set_version(x, 2) != 1)
194 X509_gmtime_adj(X509_getm_notBefore(x), 0);
195 X509_gmtime_adj(X509_getm_notAfter(x), (
long)days * 86400L);
196 if (X509_set_pubkey(x, pkey) != 1)
198 name = X509_get_subject_name(x);
199 if (
set_cn(name, cn) != 0)
201 if (X509_set_issuer_name(x, name) != 1)
203 if (
add_ext(x, x, NID_basic_constraints,
"critical,CA:TRUE") != 0)
205 if (
add_ext(x, x, NID_key_usage,
"critical,keyCertSign,cRLSign") != 0)
207 if (
add_ext(x, x, NID_subject_key_identifier,
"hash") != 0)
209 if (X509_sign(x, pkey, EVP_sha256()) == 0) {
230 EVP_PKEY* leaf_key = NULL;
231 EVP_PKEY* ca_key = NULL;
234 BIO* bio_cert = NULL;
247 bio_cert = BIO_new_mem_buf(ca_cert_pem->
data, (
int)ca_cert_pem->
written);
248 bio_key = BIO_new_mem_buf(ca_key_pem->
data, (
int)ca_key_pem->
written);
249 if (!bio_cert || !bio_key)
251 ca = PEM_read_bio_X509(bio_cert, NULL, NULL, NULL);
252 ca_key = PEM_read_bio_PrivateKey(bio_key, NULL, NULL, NULL);
253 if (!ca || !ca_key) {
254 n_log(
LOG_ERR,
"n_x509: could not parse CA certificate/key");
264 if (X509_set_version(x, 2) != 1)
268 X509_gmtime_adj(X509_getm_notBefore(x), 0);
269 X509_gmtime_adj(X509_getm_notAfter(x), (
long)days * 86400L);
270 if (X509_set_pubkey(x, leaf_key) != 1)
272 name = X509_get_subject_name(x);
273 if (
set_cn(name, host) != 0)
275 if (X509_set_issuer_name(x, X509_get_subject_name(ca)) != 1)
277 if (
add_ext(ca, x, NID_basic_constraints,
"critical,CA:FALSE") != 0)
279 if (
add_ext(ca, x, NID_key_usage,
"critical,digitalSignature,keyEncipherment") != 0)
281 if (
add_ext(ca, x, NID_ext_key_usage,
"serverAuth") != 0)
283 snprintf(san,
sizeof(san),
"%s:%s",
host_is_ip(host) ?
"IP" :
"DNS", host);
284 if (
add_ext(ca, x, NID_subject_alt_name, san) != 0)
286 if (X509_sign(x, ca_key, EVP_sha256()) == 0) {
304 EVP_PKEY_free(leaf_key);
306 EVP_PKEY_free(ca_key);
#define __n_assert(__ptr, __ret)
macro to assert things
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
int char_to_nstr_ex(const char *from, NSTRBYTE nboct, N_STR **to)
Convert a char into a N_STR, extended version.
A box including a string and his lenght.
int n_x509_generate_ca(const char *cn, int days, N_STR **ca_cert_pem, N_STR **ca_key_pem)
Generate a self-signed certificate authority (CA) keypair and cert.
int n_x509_keypair_pem(int bits, N_STR **key_pem)
Generate an RSA private key and return it as a PEM string.
int n_x509_mint_host_cert(const char *host, const N_STR *ca_cert_pem, const N_STR *ca_key_pem, int days, N_STR **leaf_cert_pem, N_STR **leaf_key_pem)
Mint a per-host leaf certificate signed by the given CA.
Common headers and low-level functions & define.
static int add_ext(X509 *issuer, X509 *subject, int nid, const char *value)
static int pkey_to_pem(EVP_PKEY *pkey, N_STR **out)
static EVP_PKEY * gen_rsa(int bits)
static int x509_to_pem(X509 *x, N_STR **out)
#define N_X509_SERIAL_BITS
random serial number size in bits
static int set_cn(X509_NAME *name, const char *cn)
static int set_random_serial(X509 *x)
#define N_X509_KEY_BITS
RSA modulus size in bits used for the CA and leaf keys.
static int host_is_ip(const char *host)
static int bio_to_nstr(BIO *bio, N_STR **out)
#define N_X509_SAN_BUF
buffer size for a subjectAltName value string
X.509 helpers: self-signed CA generation and per-host leaf minting.