2
0

pkgen 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/env ucode
  2. 'use strict';
  3. import { basename, readfile, writefile, stdin } from "fs";
  4. let pk = require("pkgen");
  5. let valid_from = "20240101000000";
  6. let valid_to = "21001231235959";
  7. let subject, password, password_stdin;
  8. let keytype = "ec";
  9. let keylen = 2048;
  10. let keyexp = 65537;
  11. let keycurve = "secp256r1";
  12. let no_ca;
  13. let legacy;
  14. const usage_message = `Usage: ${basename(sourcepath())} [<options>] <command> [<arguments>]
  15. Commands:
  16. ca <ca.pem>: Create a new CA.
  17. (creates ca.pem, ca.key, ca.serial)
  18. cert <ca.pem> <cert.pem>: Create a new certificate/key using the CA
  19. from ca.pem. (creates cert.pem and ca.key)
  20. cert_p12 <ca.pem> <cert.p12>: Create a new PKCS#12 certificate/key
  21. using the CA from ca.pem. (creates ca.p12)
  22. selfsigned <cert.pem>: Create a self-signed certificate
  23. (creates cert.pem)
  24. Options:
  25. -C <curve> Set EC curve type (default: ${keycurve})
  26. Possible values: secp521r1, secp384r1, secp256r1,
  27. secp256k1, secp224r1, secp224k1, secp192r1,
  28. secp192k1
  29. -E <exponent> Set RSA key exponent (default: ${keyexp})
  30. -L <len> Set RSA key length (default: ${keylen})
  31. -N Omit CA certificate for PKCS#12 files
  32. -p <password> Set PKCS#12 password to <password>
  33. -P Read PKCS#12 password from stdin
  34. (default: random password, printed to stdout)
  35. -s <name> Set subject for generated certificate to <name>.
  36. -t rsa|ec Set key type to rsa or ec (default: ec)
  37. -V <from> <to> Set validity for generated certificates.
  38. (default: ${valid_from} ${valid_to})
  39. -W Use weaker PKCS#12 encryption for
  40. compatibility with Windows and Apple systems
  41. `;
  42. function perror(msg) {
  43. let err = pk.errno() == -1 ? "Invalid arguments" : pk.error();
  44. warn(`${msg}: ${err}\n`);
  45. exit(1);
  46. }
  47. function usage() {
  48. warn(usage_message);
  49. exit(1);
  50. }
  51. function check_pem_path(pem_file) {
  52. if (substr(pem_file, -4) != ".pem") {
  53. warn(`Path with .pem extension expected\n`);
  54. exit(1);
  55. }
  56. return pem_file;
  57. }
  58. function gen_key() {
  59. let key = pk.generate_key({
  60. type: keytype,
  61. curve: keycurve,
  62. size: keylen,
  63. exponent: keyexp,
  64. });
  65. if (!key)
  66. perror("Failed to generate CA key");
  67. return key;
  68. }
  69. function gen_cert(key, args) {
  70. let cert = pk.generate_cert({
  71. subject_name: subject,
  72. subject_key: key,
  73. validity: [ valid_from, valid_to ],
  74. ...args
  75. });
  76. if (!cert)
  77. perror("Failed to generate certificate");
  78. cert = cert.pem();
  79. if (!cert)
  80. perror("Failed to complete certificate");
  81. return cert;
  82. }
  83. function gen_client_cert(ca_file, ca_data, key) {
  84. let ca_base = substr(ca_file, 0, -4);
  85. let ca_info = pk.cert_info(ca_data);
  86. if (!length(ca_info))
  87. perror("Failed to load CA certificate");
  88. let ca_key = pk.load_key(readfile(ca_base + ".key"));
  89. if (!ca_key)
  90. perror("Failed to load CA key");
  91. let ca_serial = +readfile(ca_base + ".serial");
  92. if (!ca_serial)
  93. perror("Failed to load CA serial");
  94. let cert = gen_cert(key, {
  95. serial: ++ca_serial,
  96. issuer_name: ca_info[0].subject,
  97. issuer_key: ca_key,
  98. });
  99. writefile(ca_base + ".serial", "" + ca_serial);
  100. return cert;
  101. }
  102. let cmds = {
  103. ca: function(args) {
  104. let ca_file = check_pem_path(shift(args));
  105. let ca_base = substr(ca_file, 0, -4);
  106. let key = gen_key();
  107. let ca_cert = gen_cert(key, {
  108. ca: true,
  109. serial: 1,
  110. issuer_name: subject,
  111. issuer_key: key,
  112. key_usage: [ "key_cert_sign" ],
  113. });
  114. writefile(ca_file, ca_cert);
  115. writefile(ca_base + ".key", key.pem());
  116. writefile(ca_base + ".serial", "1");
  117. },
  118. cert: function (args) {
  119. let ca_file = check_pem_path(shift(args));
  120. let crt_file = check_pem_path(shift(args));
  121. let crt_base = substr(crt_file, 0, -4);
  122. let key = gen_key();
  123. let ca_data = readfile(ca_file);
  124. let cert = gen_client_cert(ca_file, ca_data, key);
  125. writefile(crt_base + ".key", key.pem());
  126. writefile(crt_file, cert);
  127. },
  128. cert_p12: function (args) {
  129. let ca_file = check_pem_path(shift(args));
  130. let p12_file = shift(args);
  131. if (!p12_file)
  132. usage();
  133. let key = gen_key();
  134. let ca_data = readfile(ca_file);
  135. let cert = gen_client_cert(ca_file, ca_data, key);
  136. if (password_stdin)
  137. password = rtrim(stdin.read("line"));
  138. else if (!password)
  139. print((password = hexenc(readfile("/dev/urandom", 8))) + "\n");
  140. let p12 = pk.generate_pkcs12({
  141. password, cert, key, legacy,
  142. extra: no_ca ? null : [ ca_data ],
  143. });
  144. writefile(p12_file, p12);
  145. },
  146. selfsigned: function(args) {
  147. let crt_file = check_pem_path(shift(args));
  148. let crt_base = substr(crt_file, 0, -4);
  149. let key = gen_key();
  150. let cert = gen_cert(key, {
  151. serial: 1,
  152. issuer_name: subject,
  153. issuer_key: key,
  154. });
  155. writefile(crt_base + ".key", key.pem());
  156. writefile(crt_file, cert);
  157. },
  158. };
  159. while (substr(ARGV[0], 0, 1) == "-") {
  160. let opt = substr(shift(ARGV), 1);
  161. switch (opt) {
  162. case 'C':
  163. keycurve = shift(ARGV);
  164. break;
  165. case 'L':
  166. keylen = +shift(ARGV);
  167. break;
  168. case 'N':
  169. no_ca = true;
  170. break;
  171. case 'p':
  172. password = shift(ARGV);
  173. if (password_stdin)
  174. usage();
  175. break;
  176. case 'P':
  177. password_stdin = true;
  178. if (password)
  179. usage();
  180. break;
  181. case 's':
  182. subject = shift(ARGV);
  183. break;
  184. case 't':
  185. keytype = shift(ARGV);
  186. if (keytype != "rsa" && keytype != "ec") {
  187. warn(`Unsupported key type ${keytype}\n`);
  188. exit(1);
  189. }
  190. break;
  191. case 'V':
  192. valid_from = shift(ARGV);
  193. valid_to = shift(ARGV);
  194. break;
  195. case 'W':
  196. legacy = true;
  197. break;
  198. default:
  199. usage();
  200. break;
  201. }
  202. }
  203. let cmd = shift(ARGV);
  204. if (!cmd || !cmds[cmd])
  205. usage();
  206. if (subject == null) {
  207. warn(`Missing -s option\n`);
  208. exit(1);
  209. }
  210. cmds[cmd](ARGV);