review feedback
This commit is contained in:
parent
23aca5e891
commit
1afc25aa70
|
|
@ -31,7 +31,7 @@ function loadDummyServerCertsIfNeeded() {
|
||||||
if (dummyServerTlsOptions)
|
if (dummyServerTlsOptions)
|
||||||
return;
|
return;
|
||||||
// TODO: do we want to have it unique per browser context, launch or global?
|
// TODO: do we want to have it unique per browser context, launch or global?
|
||||||
const { cert, key } = generateSelfSignedCertificate('localhost');
|
const { cert, key } = generateSelfSignedCertificate();
|
||||||
dummyServerTlsOptions = { key, cert };
|
dummyServerTlsOptions = { key, cert };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
export function createGuid(): string {
|
export function createGuid(): string {
|
||||||
|
|
@ -26,46 +27,28 @@ export function calculateSha1(buffer: Buffer | string): string {
|
||||||
return hash.digest('hex');
|
return hash.digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
const encodeBase128 = (value: number) => {
|
// Variable-length quantity encoding aka. base-128 encoding
|
||||||
const bytes = new Uint8Array(calculateBase128BytesNeeded(value));
|
function encodeBase128 (value: number): Buffer {
|
||||||
const lastPos = bytes.byteLength - 1;
|
const bytes = [];
|
||||||
let pos = lastPos;
|
|
||||||
do {
|
do {
|
||||||
let byte = value & 0x7f; // Take the last 7 bits
|
let byte = value & 0x7f;
|
||||||
value >>>= 7; // Shift right, unsigned
|
value >>>= 7;
|
||||||
if (pos !== lastPos) {
|
if (bytes.length > 0) byte |= 0x80;
|
||||||
byte |= 0x80; // Set the continuation bit on all but the first byte
|
bytes.push(byte);
|
||||||
}
|
|
||||||
bytes[pos--] = byte; // Insert the byte at the start of the array
|
|
||||||
} while (value > 0);
|
} while (value > 0);
|
||||||
return bytes;
|
return Buffer.from(bytes.reverse());
|
||||||
};
|
|
||||||
|
|
||||||
const calculateBase128BytesNeeded = (num: number) => {
|
|
||||||
// Start at 6 and not 0 to account for overflow and to ensure that the
|
|
||||||
// division below always gives a value equal to or greater than 1.
|
|
||||||
// For example, consider the following 'real' bits needed:
|
|
||||||
// 0: 6 (initial value) + 1 (real) => 7 / 7 = 1
|
|
||||||
// 7: 6 (initial value) + 7 (real) => 13 / 7 = 1
|
|
||||||
// 8: 6 (initial value) + 8 (real) => 14 / 7 = 2
|
|
||||||
let bitsNeeded = 6;
|
|
||||||
|
|
||||||
do {
|
|
||||||
bitsNeeded++;
|
|
||||||
num >>>= 1;
|
|
||||||
} while (num > 0);
|
|
||||||
|
|
||||||
return (bitsNeeded / 7) >>> 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ASN1/DER Speficiation: https://www.itu.int/rec/T-REC-X.680-X.693-202102-I/en
|
||||||
class DER {
|
class DER {
|
||||||
static encodeSequence(data: Buffer[]): Buffer {
|
static encodeSequence(data: Buffer[]): Buffer {
|
||||||
return this._encode(0x30, Buffer.concat(data));
|
return this._encode(0x30, Buffer.concat(data));
|
||||||
}
|
}
|
||||||
static encodeInteger(data: number): Buffer {
|
static encodeInteger(data: number): Buffer {
|
||||||
|
assert(data >= 0 && data <= 0xff);
|
||||||
return this._encode(0x02, Buffer.from([data]));
|
return this._encode(0x02, Buffer.from([data]));
|
||||||
}
|
}
|
||||||
static encodeObject(oid: string): Buffer {
|
static encodeObjectIdentifier(oid: string): Buffer {
|
||||||
const parts = oid.split('.').map((v) => Number(v));
|
const parts = oid.split('.').map((v) => Number(v));
|
||||||
// Encode the second part, which could be large, using base-128 encoding if necessary
|
// Encode the second part, which could be large, using base-128 encoding if necessary
|
||||||
const output = [encodeBase128(40 * parts[0] + parts[1])];
|
const output = [encodeBase128(40 * parts[0] + parts[1])];
|
||||||
|
|
@ -80,9 +63,10 @@ class DER {
|
||||||
return Buffer.from([0x05, 0x00]);
|
return Buffer.from([0x05, 0x00]);
|
||||||
}
|
}
|
||||||
static encodeSet(data: Buffer[]): Buffer {
|
static encodeSet(data: Buffer[]): Buffer {
|
||||||
|
// We expect the data to be already sorted.
|
||||||
return this._encode(0x31, Buffer.concat(data));
|
return this._encode(0x31, Buffer.concat(data));
|
||||||
}
|
}
|
||||||
static encodeForContext(tag: number, data: Buffer): Buffer {
|
static encodeImplicitContextDependent(tag: number, data: Buffer): Buffer {
|
||||||
return this._encode(0xa0 + tag, data);
|
return this._encode(0xa0 + tag, data);
|
||||||
}
|
}
|
||||||
static encodePrintableString(data: string): Buffer {
|
static encodePrintableString(data: string): Buffer {
|
||||||
|
|
@ -130,48 +114,50 @@ class DER {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateSelfSignedCertificate(commonName: string) {
|
// X.509 Specification: https://datatracker.ietf.org/doc/html/rfc2459#section-4.1
|
||||||
|
export function generateSelfSignedCertificate() {
|
||||||
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
|
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
|
||||||
const publicKeyDer = publicKey.export({ type: 'pkcs1', format: 'der' });
|
const publicKeyDer = publicKey.export({ type: 'pkcs1', format: 'der' });
|
||||||
|
|
||||||
|
// List of fields / structure: https://datatracker.ietf.org/doc/html/rfc2459#section-4.1
|
||||||
const tbsCertificate = DER.encodeSequence([
|
const tbsCertificate = DER.encodeSequence([
|
||||||
DER.encodeForContext(0, DER.encodeInteger(1)), // version
|
DER.encodeImplicitContextDependent(0, DER.encodeInteger(1)), // version
|
||||||
DER.encodeInteger(1), // serialNumber
|
DER.encodeInteger(1), // serialNumber
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('1.2.840.113549.1.1.11'),
|
DER.encodeObjectIdentifier('1.2.840.113549.1.1.11'), // sha256WithRSAEncryption PKCS #1
|
||||||
DER.encodeNull()
|
DER.encodeNull()
|
||||||
]), // signature
|
]), // signature
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeSet([
|
DER.encodeSet([
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('2.5.4.3'),
|
DER.encodeObjectIdentifier('2.5.4.3'), // commonName X.520 DN component
|
||||||
DER.encodePrintableString(commonName)
|
DER.encodePrintableString('localhost')
|
||||||
]),
|
]),
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('2.5.4.10'),
|
DER.encodeObjectIdentifier('2.5.4.10'), // organizationName X.520 DN component
|
||||||
DER.encodePrintableString('Client Certificate Demo')
|
DER.encodePrintableString('Client Certificate Demo')
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]), // issuer
|
]), // issuer
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeDate(new Date()),
|
DER.encodeDate(new Date()), // notBefore
|
||||||
DER.encodeDate(new Date()),
|
DER.encodeDate(new Date()), // notAfter
|
||||||
]), // validity
|
]), // validity
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeSet([
|
DER.encodeSet([
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('2.5.4.3'),
|
DER.encodeObjectIdentifier('2.5.4.3'), // commonName X.520 DN component
|
||||||
DER.encodePrintableString(commonName)
|
DER.encodePrintableString('localhost')
|
||||||
]),
|
]),
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('2.5.4.10'),
|
DER.encodeObjectIdentifier('2.5.4.10'), // organizationName X.520 DN component
|
||||||
DER.encodePrintableString('Client Certificate Demo')
|
DER.encodePrintableString('Client Certificate Demo')
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]), // subject
|
]), // subject
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('1.2.840.113549.1.1.1'),
|
DER.encodeObjectIdentifier('1.2.840.113549.1.1.1'), // rsaEncryption PKCS #1
|
||||||
DER.encodeNull()
|
DER.encodeNull()
|
||||||
]),
|
]),
|
||||||
DER.encodeBitString(publicKeyDer)
|
DER.encodeBitString(publicKeyDer)
|
||||||
|
|
@ -183,7 +169,7 @@ export function generateSelfSignedCertificate(commonName: string) {
|
||||||
const certificate = DER.encodeSequence([
|
const certificate = DER.encodeSequence([
|
||||||
tbsCertificate,
|
tbsCertificate,
|
||||||
DER.encodeSequence([
|
DER.encodeSequence([
|
||||||
DER.encodeObject('1.2.840.113549.1.1.11'),
|
DER.encodeObjectIdentifier('1.2.840.113549.1.1.11'), // sha256WithRSAEncryption PKCS #1
|
||||||
DER.encodeNull()
|
DER.encodeNull()
|
||||||
]),
|
]),
|
||||||
DER.encodeBitString(signature)
|
DER.encodeBitString(signature)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue