1 | import path from 'path';
|
2 | import {
|
3 | unlinkSync as rm,
|
4 | readFileSync as readFile,
|
5 | writeFileSync as writeFile,
|
6 | existsSync as exists
|
7 | } from 'fs';
|
8 | import { sync as rimraf } from 'rimraf';
|
9 | import createDebug from 'debug';
|
10 |
|
11 | import {
|
12 | rootCAKeyPath,
|
13 | rootCACertPath,
|
14 | caSelfSignConfig,
|
15 | opensslSerialFilePath,
|
16 | opensslDatabaseFilePath,
|
17 | isWindows,
|
18 | isLinux,
|
19 | caVersionFile
|
20 | } from './constants';
|
21 | import currentPlatform from './platforms';
|
22 | import { openssl, mktmp } from './utils';
|
23 | import { generateKey } from './certificates';
|
24 | import { Options } from './index';
|
25 |
|
26 | const debug = createDebug('devcert:certificate-authority');
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | export default async function installCertificateAuthority(options: Options = {}): Promise<void> {
|
33 | debug(`Checking if older devcert install is present`);
|
34 | scrubOldInsecureVersions();
|
35 |
|
36 | debug(`Generating a root certificate authority`);
|
37 | let rootKeyPath = mktmp();
|
38 | let rootCertPath = mktmp();
|
39 |
|
40 | debug(`Generating the OpenSSL configuration needed to setup the certificate authority`);
|
41 | seedConfigFiles();
|
42 |
|
43 | debug(`Generating a private key`);
|
44 | generateKey(rootKeyPath);
|
45 |
|
46 | debug(`Generating a CA certificate`);
|
47 | openssl(`req -new -x509 -config "${ caSelfSignConfig }" -key "${ rootKeyPath }" -out "${ rootCertPath }"`);
|
48 |
|
49 | debug('Saving certificate authority credentials');
|
50 | await saveCertificateAuthorityCredentials(rootKeyPath, rootCertPath);
|
51 |
|
52 | debug(`Adding the root certificate authority to trust stores`);
|
53 | await currentPlatform.addToTrustStores(rootCertPath, options);
|
54 | }
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | function scrubOldInsecureVersions() {
|
63 |
|
64 | let configDir: string;
|
65 | if (isWindows && process.env.LOCALAPPDATA) {
|
66 | configDir = path.join(process.env.LOCALAPPDATA, 'devcert', 'config');
|
67 | } else {
|
68 | let uid = process.getuid && process.getuid();
|
69 | let userHome = (isLinux && uid === 0) ? path.resolve('/usr/local/share') : require('os').homedir();
|
70 | configDir = path.join(userHome, '.config', 'devcert');
|
71 | }
|
72 |
|
73 |
|
74 | debug(`Checking ${ configDir } for legacy files ...`);
|
75 | [
|
76 | path.join(configDir, 'openssl.conf'),
|
77 | path.join(configDir, 'devcert-ca-root.key'),
|
78 | path.join(configDir, 'devcert-ca-root.crt'),
|
79 | path.join(configDir, 'devcert-ca-version'),
|
80 | path.join(configDir, 'certs')
|
81 | ].forEach((filepath) => {
|
82 | if (exists(filepath)) {
|
83 | debug(`Removing legacy file: ${ filepath }`)
|
84 | rimraf(filepath);
|
85 | }
|
86 | });
|
87 | }
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | function seedConfigFiles() {
|
94 |
|
95 | writeFile(caVersionFile, '2');
|
96 |
|
97 | writeFile(opensslDatabaseFilePath, '');
|
98 | writeFile(opensslSerialFilePath, '01');
|
99 | }
|
100 |
|
101 | export async function withCertificateAuthorityCredentials(cb: ({ caKeyPath, caCertPath }: { caKeyPath: string, caCertPath: string }) => Promise<void> | void) {
|
102 | debug(`Retrieving devcert's certificate authority credentials`);
|
103 | let tmpCAKeyPath = mktmp();
|
104 | let tmpCACertPath = mktmp();
|
105 | let caKey = await currentPlatform.readProtectedFile(rootCAKeyPath);
|
106 | let caCert = await currentPlatform.readProtectedFile(rootCACertPath);
|
107 | writeFile(tmpCAKeyPath, caKey);
|
108 | writeFile(tmpCACertPath, caCert);
|
109 | await cb({ caKeyPath: tmpCAKeyPath, caCertPath: tmpCACertPath });
|
110 | rm(tmpCAKeyPath);
|
111 | rm(tmpCACertPath);
|
112 | }
|
113 |
|
114 | async function saveCertificateAuthorityCredentials(keypath: string, certpath: string) {
|
115 | debug(`Saving devcert's certificate authority credentials`);
|
116 | let key = readFile(keypath, 'utf-8');
|
117 | let cert = readFile(certpath, 'utf-8');
|
118 | await currentPlatform.writeProtectedFile(rootCAKeyPath, key);
|
119 | await currentPlatform.writeProtectedFile(rootCACertPath, cert);
|
120 | }
|