forked from Openwrt/openwrt
6aad5ab099
Store the private key with read and write permission for the user only and not with read permissions for everyone. This converts the write_file() function from fopen() to open() because open allows to specify the permission mask of the newly created file. It also adds and fixes some existing error handling. OpenSSL does this in the same way already. With this change it looks like this: root@OpenWrt:/# ls -al /etc/uhttpd.* -rw-r--r-- 1 root root 749 Nov 6 23:14 /etc/uhttpd.crt -rw------- 1 root root 121 Nov 6 23:14 /etc/uhttpd.key Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
386 lines
10 KiB
C
386 lines
10 KiB
C
// Copyright 2020 Paul Spooren <mail@aparcar.org>
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <wolfssl/options.h>
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#include <wolfssl/wolfcrypt/asn_public.h>
|
|
#include <wolfssl/wolfcrypt/ecc.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/rsa.h>
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
|
|
#define HEAP_HINT NULL
|
|
#define FOURK_SZ 4096
|
|
#define WOLFSSL_MIN_RSA_BITS 2048
|
|
|
|
enum {
|
|
EC_KEY_TYPE = 0,
|
|
RSA_KEY_TYPE = 1,
|
|
};
|
|
|
|
int write_file(byte *buf, int bufSz, char *path, bool cert) {
|
|
mode_t mode = S_IRUSR | S_IWUSR;
|
|
ssize_t written;
|
|
int err;
|
|
int fd;
|
|
|
|
if (cert)
|
|
mode |= S_IRGRP | S_IROTH;
|
|
|
|
if (path) {
|
|
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
|
if (fd < 0) {
|
|
perror("Error opening file");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
fd = STDERR_FILENO;
|
|
}
|
|
written = write(fd, buf, bufSz);
|
|
if (written != bufSz) {
|
|
perror("Error write file");
|
|
exit(1);
|
|
}
|
|
err = fsync(fd);
|
|
if (err < 0) {
|
|
perror("Error fsync file");
|
|
exit(1);
|
|
}
|
|
if (path) {
|
|
close(fd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_key(ecc_key *ecKey, RsaKey *rsaKey, int type, int keySz, char *fName,
|
|
bool write_pem) {
|
|
int ret;
|
|
byte der[FOURK_SZ] = {};
|
|
byte pem[FOURK_SZ] = {};
|
|
int derSz, pemSz;
|
|
if (type == EC_KEY_TYPE) {
|
|
ret = wc_EccKeyToDer(ecKey, der, sizeof(der));
|
|
} else {
|
|
ret = wc_RsaKeyToDer(rsaKey, der, sizeof(der));
|
|
}
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "Key To DER failed: %d\n", ret);
|
|
}
|
|
derSz = ret;
|
|
|
|
if (write_pem) {
|
|
if (type == EC_KEY_TYPE) {
|
|
ret = wc_DerToPem(der, derSz, pem, sizeof(pem), ECC_PRIVATEKEY_TYPE);
|
|
} else {
|
|
ret = wc_DerToPem(der, derSz, pem, sizeof(pem), PRIVATEKEY_TYPE);
|
|
}
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "DER to PEM failed: %d\n", ret);
|
|
}
|
|
pemSz = ret;
|
|
ret = write_file(pem, pemSz, fName, false);
|
|
} else {
|
|
ret = write_file(der, derSz, fName, false);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int gen_key(WC_RNG *rng, ecc_key *ecKey, RsaKey *rsaKey, int type, int keySz,
|
|
long exp, int curve) {
|
|
int ret;
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
ret = wc_ecc_init(ecKey);
|
|
(void)rsaKey;
|
|
} else {
|
|
ret = wc_InitRsaKey(rsaKey, NULL);
|
|
(void)ecKey;
|
|
}
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Key initialization failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
fprintf(stderr, "Generating EC private key\n");
|
|
ret = wc_ecc_make_key_ex(rng, 32, ecKey, curve);
|
|
} else {
|
|
fprintf(stderr, "Generating RSA private key, %i bit long modulus\n", keySz);
|
|
ret = wc_MakeRsaKey(rsaKey, keySz, WC_RSA_EXPONENT, rng);
|
|
}
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Key generation failed: %d\n", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int selfsigned(WC_RNG *rng, char **arg) {
|
|
ecc_key ecKey;
|
|
RsaKey rsaKey;
|
|
int ret;
|
|
char *subject = "";
|
|
int keySz = WOLFSSL_MIN_RSA_BITS;
|
|
int type = EC_KEY_TYPE;
|
|
int exp = WC_RSA_EXPONENT;
|
|
int curve = ECC_SECP256R1;
|
|
unsigned int days = 3653; // 10 years
|
|
char *keypath = NULL, *certpath = NULL;
|
|
char fstr[20], tstr[20];
|
|
bool pem = true;
|
|
Cert newCert;
|
|
#ifdef __USE_TIME_BITS64
|
|
time_t to, from = time(NULL);
|
|
#else
|
|
unsigned long to, from = time(NULL);
|
|
#endif
|
|
byte derBuf[FOURK_SZ] = {};
|
|
byte pemBuf[FOURK_SZ] = {};
|
|
int pemSz = -1;
|
|
int derSz = -1;
|
|
char *key, *val, *tmp;
|
|
|
|
ret = wc_InitCert(&newCert);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Init Cert failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
newCert.isCA = 0;
|
|
|
|
while (*arg && **arg == '-') {
|
|
if (!strcmp(*arg, "-der")) {
|
|
pem = false;
|
|
} else if (!strcmp(*arg, "-newkey") && arg[1]) {
|
|
if (!strncmp(arg[1], "rsa:", 4)) {
|
|
type = RSA_KEY_TYPE;
|
|
keySz = atoi(arg[1] + 4);
|
|
} else if (!strcmp(arg[1], "ec")) {
|
|
type = EC_KEY_TYPE;
|
|
} else {
|
|
fprintf(stderr, "error: invalid algorithm\n");
|
|
return 1;
|
|
}
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-days") && arg[1]) {
|
|
days = (unsigned int)atoi(arg[1]);
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-pkeyopt") && arg[1]) {
|
|
if (strncmp(arg[1], "ec_paramgen_curve:", 18)) {
|
|
fprintf(stderr, "error: invalid pkey option: %s\n", arg[1]);
|
|
return 1;
|
|
}
|
|
if (!strcmp(arg[1] + 18, "P-256")) {
|
|
curve = ECC_SECP256R1;
|
|
} else if (!strcmp(arg[1] + 18, "P-384")) {
|
|
curve = ECC_SECP384R1;
|
|
} else if (!strcmp(arg[1] + 18, "P-521")) {
|
|
curve = ECC_SECP521R1;
|
|
} else {
|
|
fprintf(stderr, "error: invalid curve name: %s\n", arg[1] + 18);
|
|
return 1;
|
|
}
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-keyout") && arg[1]) {
|
|
keypath = arg[1];
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-out") && arg[1]) {
|
|
certpath = arg[1];
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-subj") && arg[1]) {
|
|
subject = strdupa(arg[1]);
|
|
key = arg[1];
|
|
do {
|
|
tmp = strchr(key, '/');
|
|
if (tmp)
|
|
*tmp = '\0';
|
|
|
|
val = strchr(key, '=');
|
|
if (val) {
|
|
*val = '\0';
|
|
++val;
|
|
|
|
if (!strcmp(key, "C"))
|
|
strncpy(newCert.subject.country, val, CTC_NAME_SIZE);
|
|
else if (!strcmp(key, "ST"))
|
|
strncpy(newCert.subject.state, val, CTC_NAME_SIZE);
|
|
else if (!strcmp(key, "L"))
|
|
strncpy(newCert.subject.locality, val, CTC_NAME_SIZE);
|
|
else if (!strcmp(key, "O"))
|
|
strncpy(newCert.subject.org, val, CTC_NAME_SIZE);
|
|
else if (!strcmp(key, "OU"))
|
|
strncpy(newCert.subject.unit, val, CTC_NAME_SIZE);
|
|
else if (!strcmp(key, "CN")) {
|
|
strncpy(newCert.subject.commonName, val, CTC_NAME_SIZE);
|
|
|
|
#ifdef WOLFSSL_ALT_NAMES
|
|
if(strlen(val) + 2 > 256) {
|
|
fprintf(stderr, "error: CN is too long: %s\n", val);
|
|
return 1;
|
|
}
|
|
|
|
newCert.altNames[0] = 0x30; //Sequence with one element
|
|
newCert.altNames[1] = strlen(val) + 2; // Length of entire sequence
|
|
newCert.altNames[2] = 0x82; //8 - String, 2 - DNS Name
|
|
newCert.altNames[3] = strlen(val); //DNS Name length
|
|
memcpy(newCert.altNames + 4, val, strlen(val)); //DNS Name
|
|
newCert.altNamesSz = strlen(val) + 4;
|
|
#endif
|
|
}
|
|
else if (!strcmp(key, "EMAIL"))
|
|
strncpy(newCert.subject.email, val, CTC_NAME_SIZE);
|
|
else
|
|
printf("warning: unknown attribute %s=%s\n", key, val);
|
|
}
|
|
} while (tmp && (key = ++tmp));
|
|
}
|
|
arg++;
|
|
}
|
|
newCert.daysValid = days;
|
|
|
|
newCert.keyUsage = KEYUSE_DIGITAL_SIG | KEYUSE_CONTENT_COMMIT | KEYUSE_KEY_ENCIPHER;
|
|
newCert.extKeyUsage = EXTKEYUSE_SERVER_AUTH;
|
|
|
|
gen_key(rng, &ecKey, &rsaKey, type, keySz, exp, curve);
|
|
write_key(&ecKey, &rsaKey, type, keySz, keypath, pem);
|
|
|
|
from = (from < 1000000000) ? 1000000000 : from;
|
|
strftime(fstr, sizeof(fstr), "%Y%m%d%H%M%S", gmtime(&from));
|
|
to = from + 60 * 60 * 24 * days;
|
|
if (to < from)
|
|
to = INT_MAX;
|
|
strftime(tstr, sizeof(tstr), "%Y%m%d%H%M%S", gmtime(&to));
|
|
|
|
fprintf(stderr,
|
|
"Generating selfsigned certificate with subject '%s'"
|
|
" and validity %s-%s\n",
|
|
subject, fstr, tstr);
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
newCert.sigType = CTC_SHA256wECDSA;
|
|
ret = wc_MakeCert(&newCert, derBuf, sizeof(derBuf), NULL, &ecKey, rng);
|
|
} else {
|
|
newCert.sigType = CTC_SHA256wRSA;
|
|
ret = wc_MakeCert(&newCert, derBuf, sizeof(derBuf), &rsaKey, NULL, rng);
|
|
}
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "Make Cert failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
ret = wc_SignCert(newCert.bodySz, newCert.sigType, derBuf, sizeof(derBuf),
|
|
NULL, &ecKey, rng);
|
|
} else {
|
|
ret = wc_SignCert(newCert.bodySz, newCert.sigType, derBuf, sizeof(derBuf),
|
|
&rsaKey, NULL, rng);
|
|
}
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "Sign Cert failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
derSz = ret;
|
|
|
|
ret = wc_DerToPem(derBuf, derSz, pemBuf, sizeof(pemBuf), CERT_TYPE);
|
|
if (ret <= 0) {
|
|
fprintf(stderr, "DER to PEM failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
pemSz = ret;
|
|
|
|
ret = write_file(pemBuf, pemSz, certpath, true);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Write Cert failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
wc_ecc_free(&ecKey);
|
|
} else {
|
|
wc_FreeRsaKey(&rsaKey);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dokey(WC_RNG *rng, int type, char **arg) {
|
|
ecc_key ecKey;
|
|
RsaKey rsaKey;
|
|
int ret;
|
|
int curve = ECC_SECP256R1;
|
|
int keySz = WOLFSSL_MIN_RSA_BITS;
|
|
int exp = WC_RSA_EXPONENT;
|
|
char *path = NULL;
|
|
bool pem = true;
|
|
|
|
while (*arg && **arg == '-') {
|
|
if (!strcmp(*arg, "-out") && arg[1]) {
|
|
path = arg[1];
|
|
arg++;
|
|
} else if (!strcmp(*arg, "-3")) {
|
|
exp = 3;
|
|
} else if (!strcmp(*arg, "-der")) {
|
|
pem = false;
|
|
}
|
|
arg++;
|
|
}
|
|
|
|
if (*arg && type == RSA_KEY_TYPE) {
|
|
keySz = atoi(*arg);
|
|
} else if (*arg) {
|
|
if (!strcmp(*arg, "P-256")) {
|
|
curve = ECC_SECP256R1;
|
|
} else if (!strcmp(*arg, "P-384")) {
|
|
curve = ECC_SECP384R1;
|
|
} else if (!strcmp(*arg, "P-521")) {
|
|
curve = ECC_SECP521R1;
|
|
} else {
|
|
fprintf(stderr, "Invalid Curve Name: %s\n", *arg);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
ret = gen_key(rng, &ecKey, &rsaKey, type, keySz, exp, curve);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = write_key(&ecKey, &rsaKey, type, keySz, path, pem);
|
|
|
|
if (type == EC_KEY_TYPE) {
|
|
wc_ecc_free(&ecKey);
|
|
} else {
|
|
wc_FreeRsaKey(&rsaKey);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int ret;
|
|
WC_RNG rng;
|
|
ret = wc_InitRng(&rng);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Init Rng failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (argv[1]) {
|
|
if (!strcmp(argv[1], "eckey"))
|
|
return dokey(&rng, EC_KEY_TYPE, argv + 2);
|
|
|
|
if (!strcmp(argv[1], "rsakey"))
|
|
return dokey(&rng, RSA_KEY_TYPE, argv + 2);
|
|
|
|
if (!strcmp(argv[1], "selfsigned"))
|
|
return selfsigned(&rng, argv + 2);
|
|
}
|
|
|
|
fprintf(stderr, "PX5G X.509 Certificate Generator Utilit using WolfSSL\n\n");
|
|
fprintf(stderr, "Usage: [eckey|rsakey|selfsigned]\n");
|
|
return 1;
|
|
}
|