UNPKG

3.14 kBPlain TextView Raw
1import {
2 readFileSync,
3 readdirSync,
4 writeFileSync,
5 unlinkSync,
6 chmodSync,
7 existsSync
8} from 'fs';
9import * as path from 'path';
10import { exec, execSync, ExecSyncOptions } from 'child_process';
11import * as tmp from 'tmp';
12import * as Configstore from 'configstore';
13import * as mkdirp from 'mkdirp';
14import * as createDebug from 'debug';
15import { sync as commandExists } from 'command-exists';
16
17import {
18 isMac,
19 isLinux,
20 isWindows,
21 configDir,
22 configPath,
23 opensslConfTemplate,
24 opensslConfPath,
25 rootKeyPath,
26 rootCertPath,
27 caCertsDir
28} from './constants';
29import installCertificateAuthority from './root-authority';
30import { openssl, generateKey } from './utils';
31
32const debug = createDebug('devcert');
33
34/**
35 * Request an SSL certificate for the given app name signed by the devcert root certificate
36 * authority. If devcert has previously generated a certificate for that app name on this machine,
37 * it will reuse that certificate.
38 *
39 * If this is the first time devcert is being run on this machine, it will generate and attempt to
40 * install a root certificate authority.
41 *
42 * Returns a promise that resolves with { keyPath, certPath, key, cert }, where `key` and `cert` are
43 * Buffers with the contents of `keyPath` and `certPath`, respectively.
44 */
45export default async function devcert(appName: string, options: { installCertutil?: boolean } = {}) {
46 debug(`development cert requested for ${ appName }`);
47
48 if (!isMac && !isLinux && !isWindows) {
49 throw new Error(`devcert: "${ process.platform }" platform not supported`);
50 }
51
52 if (!commandExists('openssl')) {
53 throw new Error('Unable to find openssl - make sure it is installed and available in your PATH');
54 }
55
56 let appKeyPath = configPath(`${ appName }.key`);
57 let appCertPath = configPath(`${ appName }.crt`);
58
59 if (!existsSync(rootCertPath)) {
60 debug('devcert root CA not installed yet, must be first run; installing root CA ...');
61 await installCertificateAuthority(options.installCertutil);
62 }
63
64 if (!existsSync(configPath(`${ appName }.crt`))) {
65 debug(`first request for ${ appName } cert, generating and caching ...`);
66 generateKey(configPath(`${ appName }.key`));
67 generateSignedCertificate(appName, appKeyPath);
68 }
69
70 debug(`returning app cert`);
71 return {
72 keyPath: appKeyPath,
73 certPath: appCertPath,
74 key: readFileSync(appKeyPath),
75 cert: readFileSync(appCertPath)
76 };
77
78}
79
80// Generate an app certificate signed by the devcert root CA
81function generateSignedCertificate(name: string, keyPath: string): void {
82 debug(`generating certificate signing request for ${ name }`);
83 let csrFile = configPath(`${ name }.csr`)
84 openssl(`req -config ${ opensslConfPath } -subj "/CN=${ name }" -key ${ keyPath } -out ${ csrFile } -new`);
85 debug(`generating certificate for ${ name } from signing request; signing with devcert root CA`);
86 let certPath = configPath(`${ name }.crt`);
87 openssl(`ca -config ${ opensslConfPath } -in ${ csrFile } -out ${ certPath } -outdir ${ caCertsDir } -keyfile ${ rootKeyPath } -cert ${ rootCertPath } -notext -md sha256 -days 7000 -batch -extensions server_cert`)
88}