0
0
mirror of https://git.openwrt.org/openwrt/openwrt.git synced 2025-02-07 00:19:44 +00:00
Felix Fietkau 2c78e9d7bd ucode-mod-pkgen: add ucode module for generating crypto keys/certificates
This also includes a script to use the functionality from the
command line

Signed-off-by: Felix Fietkau <nbd@nbd.name>
2025-01-17 12:12:23 +01:00

253 lines
5.4 KiB
Plaintext
Executable File

#!/usr/bin/env ucode
'use strict';
import { basename, readfile, writefile, stdin } from "fs";
let pk = require("pkgen");
let valid_from = "20240101000000";
let valid_to = "21001231235959";
let subject, password, password_stdin;
let keytype = "ec";
let keylen = 2048;
let keyexp = 65537;
let keycurve = "secp256r1";
let no_ca;
let legacy;
const usage_message = `Usage: ${basename(sourcepath())} [<options>] <command> [<arguments>]
Commands:
ca <ca.pem>: Create a new CA.
(creates ca.pem, ca.key, ca.serial)
cert <ca.pem> <cert.pem>: Create a new certificate/key using the CA
from ca.pem. (creates cert.pem and ca.key)
cert_p12 <ca.pem> <cert.p12>: Create a new PKCS#12 certificate/key
using the CA from ca.pem. (creates ca.p12)
selfsigned <cert.pem>: Create a self-signed certificate
(creates cert.pem)
Options:
-C <curve> Set EC curve type (default: ${keycurve})
Possible values: secp521r1, secp384r1, secp256r1,
secp256k1, secp224r1, secp224k1, secp192r1,
secp192k1
-E <exponent> Set RSA key exponent (default: ${keyexp})
-L <len> Set RSA key length (default: ${keylen})
-N Omit CA certificate for PKCS#12 files
-p <password> Set PKCS#12 password to <password>
-P Read PKCS#12 password from stdin
(default: random password, printed to stdout)
-s <name> Set subject for generated certificate to <name>.
-t rsa|ec Set key type to rsa or ec (default: ec)
-V <from> <to> Set validity for generated certificates.
(default: ${valid_from} ${valid_to})
-W Use weaker PKCS#12 encryption for
compatibility with Windows and Apple systems
`;
function perror(msg) {
let err = pk.errno() == -1 ? "Invalid arguments" : pk.error();
warn(`${msg}: ${err}\n`);
exit(1);
}
function usage() {
warn(usage_message);
exit(1);
}
function check_pem_path(pem_file) {
if (substr(pem_file, -4) != ".pem") {
warn(`Path with .pem extension expected\n`);
exit(1);
}
return pem_file;
}
function gen_key() {
let key = pk.generate_key({
type: keytype,
curve: keycurve,
size: keylen,
exponent: keyexp,
});
if (!key)
perror("Failed to generate CA key");
return key;
}
function gen_cert(key, args) {
let cert = pk.generate_cert({
subject_name: subject,
subject_key: key,
validity: [ valid_from, valid_to ],
...args
});
if (!cert)
perror("Failed to generate certificate");
cert = cert.pem();
if (!cert)
perror("Failed to complete certificate");
return cert;
}
function gen_client_cert(ca_file, ca_data, key) {
let ca_base = substr(ca_file, 0, -4);
let ca_info = pk.cert_info(ca_data);
if (!length(ca_info))
perror("Failed to load CA certificate");
let ca_key = pk.load_key(readfile(ca_base + ".key"));
if (!ca_key)
perror("Failed to load CA key");
let ca_serial = +readfile(ca_base + ".serial");
if (!ca_serial)
perror("Failed to load CA serial");
let cert = gen_cert(key, {
serial: ++ca_serial,
issuer_name: ca_info[0].subject,
issuer_key: ca_key,
});
writefile(ca_base + ".serial", "" + ca_serial);
return cert;
}
let cmds = {
ca: function(args) {
let ca_file = check_pem_path(shift(args));
let ca_base = substr(ca_file, 0, -4);
let key = gen_key();
let ca_cert = gen_cert(key, {
ca: true,
serial: 1,
issuer_name: subject,
issuer_key: key,
key_usage: [ "key_cert_sign" ],
});
writefile(ca_file, ca_cert);
writefile(ca_base + ".key", key.pem());
writefile(ca_base + ".serial", "1");
},
cert: function (args) {
let ca_file = check_pem_path(shift(args));
let crt_file = check_pem_path(shift(args));
let crt_base = substr(crt_file, 0, -4);
let key = gen_key();
let ca_data = readfile(ca_file);
let cert = gen_client_cert(ca_file, ca_data, key);
writefile(crt_base + ".key", key.pem());
writefile(crt_file, cert);
},
cert_p12: function (args) {
let ca_file = check_pem_path(shift(args));
let p12_file = shift(args);
if (!p12_file)
usage();
let key = gen_key();
let ca_data = readfile(ca_file);
let cert = gen_client_cert(ca_file, ca_data, key);
if (password_stdin)
password = rtrim(stdin.read("line"));
else if (!password)
print((password = hexenc(readfile("/dev/urandom", 8))) + "\n");
let p12 = pk.generate_pkcs12({
password, cert, key, legacy,
extra: no_ca ? null : [ ca_data ],
});
writefile(p12_file, p12);
},
selfsigned: function(args) {
let crt_file = check_pem_path(shift(args));
let crt_base = substr(crt_file, 0, -4);
let key = gen_key();
let cert = gen_cert(key, {
serial: 1,
issuer_name: subject,
issuer_key: key,
});
writefile(crt_base + ".key", key.pem());
writefile(crt_file, cert);
},
};
while (substr(ARGV[0], 0, 1) == "-") {
let opt = substr(shift(ARGV), 1);
switch (opt) {
case 'C':
keycurve = shift(ARGV);
break;
case 'L':
keylen = +shift(ARGV);
break;
case 'N':
no_ca = true;
break;
case 'p':
password = shift(ARGV);
if (password_stdin)
usage();
break;
case 'P':
password_stdin = true;
if (password)
usage();
break;
case 's':
subject = shift(ARGV);
break;
case 't':
keytype = shift(ARGV);
if (keytype != "rsa" && keytype != "ec") {
warn(`Unsupported key type ${keytype}\n`);
exit(1);
}
break;
case 'V':
valid_from = shift(ARGV);
valid_to = shift(ARGV);
break;
case 'W':
legacy = true;
break;
default:
usage();
break;
}
}
let cmd = shift(ARGV);
if (!cmd || !cmds[cmd])
usage();
if (subject == null) {
warn(`Missing -s option\n`);
exit(1);
}
cmds[cmd](ARGV);