forked from watson/https-pem
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgenerate.js
More file actions
74 lines (59 loc) · 2.35 KB
/
Copy pathgenerate.js
File metadata and controls
74 lines (59 loc) · 2.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
'use strict'
const { X509Certificate } = require('node:crypto')
const selfsigned = require('selfsigned')
// `selfsigned` derives the certificate serial number from 9 random bytes and
// runs them through its own `toPositiveHex()`, which clears the sign bit but
// does not re-minimise the resulting DER INTEGER. Roughly 1 in 65536 draws end
// up with two redundant leading zero bytes, and node-forge's encoder strips
// only one of them (see the "should all leading bytes be stripped vs just one?"
// TODO in its `asn1.js`), so the serial goes out as a positive INTEGER with
// illegal padding.
//
// node-forge's own parser accepts that encoding, so the
// `verifyCertificateChain()` check `selfsigned` runs before returning passes
// and the pair looks fine. OpenSSL rejects it, so the certificate only blows
// up later, as ERR_OSSL_ASN1_ILLEGAL_PADDING from the middle of a TLS
// handshake. That made every consumer building a server from a freshly
// generated pair intermittently fail (nodejs/undici#5245).
//
// Each serial number is drawn independently, so generating again is enough to
// get past it: three attempts bring the odds down to about 1 in 2.8e14. All of
// this can go away once `selfsigned` emits minimally encoded serial numbers.
const ATTEMPTS = 3
// Returns the error OpenSSL refused the certificate with, or `null` if it
// loads. node-forge parsing it successfully says nothing about OpenSSL.
function loadError (cert) {
try {
new X509Certificate(cert) // eslint-disable-line no-new
return null
} catch (err) {
return err
}
}
function unusable (cause) {
return new Error(
`could not generate a certificate OpenSSL can load in ${ATTEMPTS} attempts`,
{ cause }
)
}
function generateSync (attrs, opts) {
let lastError
for (let i = 0; i < ATTEMPTS; i++) {
const pems = selfsigned.generate(attrs, opts)
const err = loadError(pems.cert)
if (err === null) return pems
lastError = err
}
throw unusable(lastError)
}
function generate (attrs, opts, done) {
let remaining = ATTEMPTS
selfsigned.generate(attrs, opts, function onPems (err, pems) {
if (err) return done(err)
const loadErr = loadError(pems.cert)
if (loadErr === null) return done(null, pems)
if (--remaining > 0) return selfsigned.generate(attrs, opts, onPems)
done(unusable(loadErr))
})
}
module.exports = { generate, generateSync }