@dev-swarup/http-mitm-proxy 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/ca.js ADDED
@@ -0,0 +1,268 @@
1
+ 'use strict';
2
+
3
+ var FS = require('fs');
4
+ var path = require('path');
5
+ var Forge = require('node-forge');
6
+ var pki = Forge.pki;
7
+ const { mkdirp } = require('mkdirp');
8
+ var async = require('async');
9
+
10
+ var CAattrs = [
11
+ {
12
+ name: 'commonName',
13
+ value: 'NodeMITMProxyCA',
14
+ },
15
+ {
16
+ name: 'countryName',
17
+ value: 'Internet',
18
+ },
19
+ {
20
+ shortName: 'ST',
21
+ value: 'Internet',
22
+ },
23
+ {
24
+ name: 'localityName',
25
+ value: 'Internet',
26
+ },
27
+ {
28
+ name: 'organizationName',
29
+ value: 'Node MITM Proxy CA',
30
+ },
31
+ {
32
+ shortName: 'OU',
33
+ value: 'CA',
34
+ },
35
+ ];
36
+
37
+ var CAextensions = [
38
+ {
39
+ name: 'basicConstraints',
40
+ cA: true,
41
+ },
42
+ {
43
+ name: 'keyUsage',
44
+ keyCertSign: true,
45
+ digitalSignature: true,
46
+ nonRepudiation: true,
47
+ keyEncipherment: true,
48
+ dataEncipherment: true,
49
+ },
50
+ {
51
+ name: 'extKeyUsage',
52
+ serverAuth: true,
53
+ clientAuth: true,
54
+ codeSigning: true,
55
+ emailProtection: true,
56
+ timeStamping: true,
57
+ },
58
+ {
59
+ name: 'nsCertType',
60
+ client: true,
61
+ server: true,
62
+ email: true,
63
+ objsign: true,
64
+ sslCA: true,
65
+ emailCA: true,
66
+ objCA: true,
67
+ },
68
+ {
69
+ name: 'subjectKeyIdentifier',
70
+ },
71
+ ];
72
+
73
+ var ServerAttrs = [
74
+ {
75
+ name: 'countryName',
76
+ value: 'Internet',
77
+ },
78
+ {
79
+ shortName: 'ST',
80
+ value: 'Internet',
81
+ },
82
+ {
83
+ name: 'localityName',
84
+ value: 'Internet',
85
+ },
86
+ {
87
+ name: 'organizationName',
88
+ value: 'Node MITM Proxy CA',
89
+ },
90
+ {
91
+ shortName: 'OU',
92
+ value: 'Node MITM Proxy Server Certificate',
93
+ },
94
+ ];
95
+
96
+ var ServerExtensions = [
97
+ {
98
+ name: 'basicConstraints',
99
+ cA: false,
100
+ },
101
+ {
102
+ name: 'keyUsage',
103
+ keyCertSign: false,
104
+ digitalSignature: true,
105
+ nonRepudiation: false,
106
+ keyEncipherment: true,
107
+ dataEncipherment: true,
108
+ },
109
+ {
110
+ name: 'extKeyUsage',
111
+ serverAuth: true,
112
+ clientAuth: true,
113
+ codeSigning: false,
114
+ emailProtection: false,
115
+ timeStamping: false,
116
+ },
117
+ {
118
+ name: 'nsCertType',
119
+ client: true,
120
+ server: true,
121
+ email: false,
122
+ objsign: false,
123
+ sslCA: false,
124
+ emailCA: false,
125
+ objCA: false,
126
+ },
127
+ {
128
+ name: 'subjectKeyIdentifier',
129
+ },
130
+ ];
131
+
132
+ var CA = function () {};
133
+
134
+ CA.create = function (caFolder, callback) {
135
+ var ca = new CA();
136
+ ca.baseCAFolder = caFolder;
137
+ ca.certsFolder = path.join(ca.baseCAFolder, 'certs');
138
+ ca.keysFolder = path.join(ca.baseCAFolder, 'keys');
139
+ mkdirp(ca.baseCAFolder)
140
+ .then(() =>
141
+ mkdirp(ca.certsFolder).then(() =>
142
+ mkdirp(ca.keysFolder).then(() => {
143
+ if (FS.existsSync(path.join(ca.certsFolder, 'ca.pem'))) {
144
+ ca.loadCA((err) => (err ? callback(err) : callback(null, ca)));
145
+ } else {
146
+ ca.generateCA((err) => (err ? callback(err) : callback(null, ca)));
147
+ }
148
+ })
149
+ )
150
+ )
151
+ .catch((err) => (err ? callback(err) : callback(null, ca)));
152
+ };
153
+
154
+ CA.prototype.randomSerialNumber = function () {
155
+ // generate random 16 bytes hex string
156
+ var sn = '';
157
+ for (var i = 0; i < 4; i++) {
158
+ sn += ('00000000' + Math.floor(Math.random() * Math.pow(256, 4)).toString(16)).slice(-8);
159
+ }
160
+ return sn;
161
+ };
162
+
163
+ CA.prototype.generateCA = function (callback) {
164
+ var self = this;
165
+ pki.rsa.generateKeyPair({ bits: 2048 }, function (err, keys) {
166
+ if (err) {
167
+ return callback(err);
168
+ }
169
+ var cert = pki.createCertificate();
170
+ cert.publicKey = keys.publicKey;
171
+ cert.serialNumber = self.randomSerialNumber();
172
+ cert.validity.notBefore = new Date();
173
+ cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
174
+ cert.validity.notAfter = new Date();
175
+ cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 2);
176
+ cert.setSubject(CAattrs);
177
+ cert.setIssuer(CAattrs);
178
+ cert.setExtensions(CAextensions);
179
+ cert.sign(keys.privateKey, Forge.md.sha256.create());
180
+ self.CAcert = cert;
181
+ self.CAkeys = keys;
182
+ async.parallel(
183
+ [
184
+ FS.writeFile.bind(null, path.join(self.certsFolder, 'ca.pem'), pki.certificateToPem(cert)),
185
+ FS.writeFile.bind(null, path.join(self.keysFolder, 'ca.private.key'), pki.privateKeyToPem(keys.privateKey)),
186
+ FS.writeFile.bind(null, path.join(self.keysFolder, 'ca.public.key'), pki.publicKeyToPem(keys.publicKey)),
187
+ ],
188
+ callback
189
+ );
190
+ });
191
+ };
192
+
193
+ CA.prototype.loadCA = function (callback) {
194
+ var self = this;
195
+ async.auto(
196
+ {
197
+ certPEM: function (callback) {
198
+ FS.readFile(path.join(self.certsFolder, 'ca.pem'), 'utf-8', callback);
199
+ },
200
+ keyPrivatePEM: function (callback) {
201
+ FS.readFile(path.join(self.keysFolder, 'ca.private.key'), 'utf-8', callback);
202
+ },
203
+ keyPublicPEM: function (callback) {
204
+ FS.readFile(path.join(self.keysFolder, 'ca.public.key'), 'utf-8', callback);
205
+ },
206
+ },
207
+ function (err, results) {
208
+ if (err) {
209
+ return callback(err);
210
+ }
211
+ self.CAcert = pki.certificateFromPem(results.certPEM);
212
+ self.CAkeys = {
213
+ privateKey: pki.privateKeyFromPem(results.keyPrivatePEM),
214
+ publicKey: pki.publicKeyFromPem(results.keyPublicPEM),
215
+ };
216
+ return callback();
217
+ }
218
+ );
219
+ };
220
+
221
+ CA.prototype.generateServerCertificateKeys = function (hosts, cb) {
222
+ var self = this;
223
+ if (typeof hosts === 'string') hosts = [hosts];
224
+ var mainHost = hosts[0];
225
+ var keysServer = pki.rsa.generateKeyPair(2048);
226
+ var certServer = pki.createCertificate();
227
+ certServer.publicKey = keysServer.publicKey;
228
+ certServer.serialNumber = this.randomSerialNumber();
229
+ certServer.validity.notBefore = new Date();
230
+ certServer.validity.notBefore.setDate(certServer.validity.notBefore.getDate() - 1);
231
+ certServer.validity.notAfter = new Date();
232
+ certServer.validity.notAfter.setFullYear(certServer.validity.notBefore.getFullYear() + 2);
233
+ var attrsServer = ServerAttrs.slice(0);
234
+ attrsServer.unshift({
235
+ name: 'commonName',
236
+ value: mainHost,
237
+ });
238
+ certServer.setSubject(attrsServer);
239
+ certServer.setIssuer(this.CAcert.issuer.attributes);
240
+ certServer.setExtensions(
241
+ ServerExtensions.concat([
242
+ {
243
+ name: 'subjectAltName',
244
+ altNames: hosts.map(function (host) {
245
+ if (host.match(/^[\d\.]+$/)) {
246
+ return { type: 7, ip: host };
247
+ }
248
+ return { type: 2, value: host };
249
+ }),
250
+ },
251
+ ])
252
+ );
253
+ certServer.sign(this.CAkeys.privateKey, Forge.md.sha256.create());
254
+ var certPem = pki.certificateToPem(certServer);
255
+ var keyPrivatePem = pki.privateKeyToPem(keysServer.privateKey);
256
+
257
+ cb(certPem, keyPrivatePem);
258
+ };
259
+
260
+ CA.prototype.hostToFilename = function (hostname, ext) {
261
+ // Replace wildcards and colons, not portably supported in filenames
262
+ return hostname.replace(/\*/g, '_').replace(/\:/g, ';') + ext;
263
+ };
264
+
265
+ CA.prototype.getCACertPath = function () {
266
+ return this.certsFolder + '/ca.pem';
267
+ };
268
+ module.exports = CA;
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var zlib = require('zlib');
4
+
5
+ module.exports = {
6
+ onResponse: function(ctx, callback) {
7
+ if (ctx.serverToProxyResponse.headers['content-encoding']
8
+ && ctx.serverToProxyResponse.headers['content-encoding'].toLowerCase() == 'gzip') {
9
+ delete ctx.serverToProxyResponse.headers['content-encoding'];
10
+ ctx.addResponseFilter(zlib.createGunzip());
11
+ }
12
+ return callback();
13
+ },
14
+ onRequest: function(ctx, callback) {
15
+ ctx.proxyToServerRequestOptions.headers['accept-encoding'] = 'gzip';
16
+ return callback();
17
+ }
18
+ };
19
+
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * group1: subdomain
5
+ * group2: domain.ext
6
+ * exclude short domains (length < 4) to avoid catching double extensions (ex: net.au, co.uk, ...)
7
+ */
8
+ const HOSTNAME_REGEX = /^(.+)(\.[^\.]{4,}(\.[^\.]{1,3})*\.[^\.]+)$/;
9
+
10
+ module.exports = {
11
+ onCertificateRequired: function (hostname, callback) {
12
+ var rootHost = hostname;
13
+ if (HOSTNAME_REGEX.test(hostname)) {
14
+ rootHost = hostname.replace(/^[^\.]+\./, '');
15
+ }
16
+ // Replace wildcards and colons, not portably supported in filenames
17
+ const hostCertFilename = rootHost.replace(/\*/g, '_').replace(/\:/g, ';') + ext;
18
+ return callback(null, {
19
+ keyFile: this.sslCaDir + '/keys/_.' + hostCertFilename + '.key',
20
+ certFile: this.sslCaDir + '/certs/_.' + hostCertFilename + '.pem',
21
+ hosts: ['*.' + rootHost, rootHost],
22
+ });
23
+ },
24
+ };