mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2025-02-07 00:19:44 +00:00
This also includes a script to use the functionality from the command line Signed-off-by: Felix Fietkau <nbd@nbd.name>
253 lines
5.4 KiB
Plaintext
Executable File
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);
|