| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- #!/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);
|