9
9
import * as path from 'path' ;
10
10
import * as getPort from 'get-port' ;
11
11
import * as http from 'http' ;
12
- import { execSync } from 'child_process' ;
12
+ import { exec , execSync } from 'child_process' ;
13
13
import * as tmp from 'tmp' ;
14
14
import * as glob from 'glob' ;
15
15
import * as Configstore from 'configstore' ;
@@ -32,12 +32,16 @@ if (isWindows && process.env.LOCALAPPDATA) {
32
32
let userHome = ( isLinux && uid === 0 ) ? path . resolve ( '/usr/local/share' ) : require ( 'os' ) . homedir ( ) ;
33
33
configDir = path . join ( userHome , '.config' , 'devcert' ) ;
34
34
}
35
- mkdirp . sync ( configDir ) ;
36
35
const configPath : ( ...pathSegments : string [ ] ) => string = path . join . bind ( path , configDir ) ;
37
36
38
- const opensslConfPath = path . join ( __dirname , '..' , 'openssl.conf' ) ;
37
+ const opensslConfTemplate = path . join ( __dirname , '..' , 'openssl.conf' ) ;
38
+ const opensslConfPath = configPath ( 'openssl.conf' ) ;
39
39
const rootKeyPath = configPath ( 'devcert-ca-root.key' ) ;
40
40
const rootCertPath = configPath ( 'devcert-ca-root.crt' ) ;
41
+ const caCertsDir = configPath ( 'certs' ) ;
42
+
43
+ mkdirp . sync ( configDir ) ;
44
+ mkdirp . sync ( caCertsDir ) ;
41
45
42
46
export interface Options {
43
47
installCertutil ?: boolean ;
@@ -57,14 +61,14 @@ export default async function devcert(appName: string, options: Options = {}) {
57
61
let appKeyPath = configPath ( `${ appName } .key` ) ;
58
62
let appCertPath = configPath ( `${ appName } .crt` ) ;
59
63
60
- if ( ! existsSync ( rootKeyPath ) ) {
64
+ if ( ! existsSync ( rootCertPath ) ) {
61
65
debug ( 'devcert root CA not installed yet, must be first run; installing root CA ...' ) ;
62
66
await installCertificateAuthority ( options . installCertutil ) ;
63
67
}
64
68
65
- if ( ! existsSync ( configPath ( `${ appName } .key ` ) ) ) {
69
+ if ( ! existsSync ( configPath ( `${ appName } .crt ` ) ) ) {
66
70
debug ( `first request for ${ appName } cert, generating and caching ...` ) ;
67
- generateKey ( appName ) ;
71
+ generateKey ( configPath ( ` ${ appName } .key` ) ) ;
68
72
generateSignedCertificate ( appName , appKeyPath ) ;
69
73
}
70
74
@@ -81,18 +85,21 @@ export default async function devcert(appName: string, options: Options = {}) {
81
85
// Install the once-per-machine trusted root CA. We'll use this CA to sign per-app certs, allowing
82
86
// us to minimize the need for elevated permissions while still allowing for per-app certificates.
83
87
async function installCertificateAuthority ( installCertutil : boolean ) : Promise < void > {
88
+ debug ( `generating openssl configuration` ) ;
89
+ writeFileSync ( opensslConfPath , readFileSync ( opensslConfTemplate , 'utf-8' ) . replace ( / D I R / g, configDir ) ) ;
84
90
debug ( `generating root certificate authority key` ) ;
85
- generateKey ( 'devcert-ca-root' ) ;
91
+ generateKey ( rootKeyPath ) ;
86
92
debug ( `generating root certificate authority certificate` ) ;
87
93
execSync ( `openssl req -config ${ opensslConfPath } -key ${ rootKeyPath } -out ${ rootCertPath } -new -subj '/CN=devcert' -x509 -days 7000 -extensions v3_ca` ) ;
88
94
debug ( `adding root certificate authority to trust stores` )
89
95
await addCertificateToTrustStores ( installCertutil ) ;
96
+ writeFileSync ( configPath ( 'index.txt' ) , '' ) ;
97
+ writeFileSync ( configPath ( 'serial' ) , '01' ) ;
90
98
}
91
99
92
100
// Generate a cryptographic key, used to sign certificates or certificate signing requests.
93
- function generateKey ( name : string ) : void {
94
- debug ( `generateKey: ${ name } ` ) ;
95
- let filename = configPath ( `${ name } .key` ) ;
101
+ function generateKey ( filename : string ) : void {
102
+ debug ( `generateKey: ${ filename } ` ) ;
96
103
execSync ( `openssl genrsa -out ${ filename } 2048` ) ;
97
104
chmodSync ( filename , 400 ) ;
98
105
}
@@ -104,7 +111,7 @@ function generateSignedCertificate(name: string, keyPath: string): void {
104
111
execSync ( `openssl req -config ${ opensslConfPath } -subj '/CN=${ name } ' -key ${ keyPath } -out ${ csrFile } -new` ) ;
105
112
debug ( `generating certificate for ${ name } from signing request; signing with devcert root CA` ) ;
106
113
let certPath = configPath ( `${ name } .crt` ) ;
107
- execSync ( `openssl ca -config ${ opensslConfPath } -in ${ csrFile } -out ${ certPath } -keyfile ${ rootKeyPath } -cert ${ rootCertPath } -notext -md sha256 -days 7000 -extensions server_cert` )
114
+ execSync ( `openssl ca -config ${ opensslConfPath } -in ${ csrFile } -out ${ certPath } -outdir ${ caCertsDir } - keyfile ${ rootKeyPath } -cert ${ rootCertPath } -notext -md sha256 -days 7000 -extensions server_cert` )
108
115
}
109
116
110
117
// Add the devcert root CA certificate to the trust stores for this machine. Adds to OS level trust
@@ -185,12 +192,11 @@ async function openCertificateInFirefox(firefoxPath: string): Promise<void> {
185
192
res . end ( ) ;
186
193
} ) . listen ( port ) ;
187
194
debug ( 'certificate is hosted, starting firefox at hosted URL' ) ;
188
- execSync ( `${ firefoxPath } http://localhost:${ port } ` ) ;
189
195
await new Promise ( ( resolve ) => {
190
- console . log ( 'Unable to automatically install SSL certificate - please follow the prompts in Firefox to trust the root certificate' ) ;
196
+ console . log ( 'Unable to automatically install SSL certificate - please follow the prompts at http://localhost:${ port } in Firefox to trust the root certificate' ) ;
191
197
console . log ( 'See https://github.com/davewasmer/devcert#how-it-works for more details' ) ;
192
- console . log ( 'Press any key once you finish the Firefox prompts' ) ;
193
- debug ( 'waiting for user to finish firefox certificate import' ) ;
198
+ console . log ( '-- Press <Enter> once you finish the Firefox prompts -- ' ) ;
199
+ exec ( ` ${ firefoxPath } http://localhost: ${ port } ` ) ;
194
200
process . stdin . resume ( ) ;
195
201
process . stdin . on ( 'data' , resolve ) ;
196
202
} ) ;
0 commit comments