| // 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; |
| } |