mockttp
Advanced tools
Comparing version 3.13.0 to 3.14.0
@@ -52,2 +52,5 @@ /// <reference types="node" /> | ||
bits?: number; | ||
nameConstraints?: { | ||
permitted?: string[]; | ||
}; | ||
}): Promise<{ | ||
@@ -54,0 +57,0 @@ key: string; |
@@ -8,3 +8,3 @@ "use strict"; | ||
const forge = require("node-forge"); | ||
const { pki, md, util: { encode64 } } = forge; | ||
const { asn1, pki, md, util } = forge; | ||
; | ||
@@ -50,7 +50,19 @@ /** | ||
]); | ||
cert.setExtensions([ | ||
const extensions = [ | ||
{ name: 'basicConstraints', cA: true, critical: true }, | ||
{ name: 'keyUsage', keyCertSign: true, digitalSignature: true, nonRepudiation: true, cRLSign: true, critical: true }, | ||
{ name: 'subjectKeyIdentifier' } | ||
]); | ||
{ name: 'subjectKeyIdentifier' }, | ||
]; | ||
const permittedDomains = options.nameConstraints?.permitted || []; | ||
if (permittedDomains.length > 0) { | ||
extensions.push({ | ||
critical: true, | ||
id: '2.5.29.30', | ||
name: 'nameConstraints', | ||
value: generateNameConstraints({ | ||
permitted: permittedDomains, | ||
}), | ||
}); | ||
} | ||
cert.setExtensions(extensions); | ||
// Self-issued too | ||
@@ -66,5 +78,21 @@ cert.setIssuer(cert.subject.attributes); | ||
exports.generateCACertificate = generateCACertificate; | ||
/** | ||
* Generate name constraints in conformance with | ||
* [RFC 5280 § 4.2.1.10](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10) | ||
*/ | ||
function generateNameConstraints(input) { | ||
const domainsToSequence = (ips) => ips.map((domain) => { | ||
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | ||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, util.encodeUtf8(domain)), | ||
]); | ||
}); | ||
const permittedAndExcluded = []; | ||
if (input.permitted && input.permitted.length > 0) { | ||
permittedAndExcluded.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, domainsToSequence(input.permitted))); | ||
} | ||
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, permittedAndExcluded); | ||
} | ||
function generateSPKIFingerprint(certPem) { | ||
let cert = pki.certificateFromPem(certPem.toString('utf8')); | ||
return encode64(pki.getPublicKeyFingerprint(cert.publicKey, { | ||
return util.encode64(pki.getPublicKeyFingerprint(cert.publicKey, { | ||
type: 'SubjectPublicKeyInfo', | ||
@@ -71,0 +99,0 @@ md: md.sha256.create(), |
{ | ||
"name": "mockttp", | ||
"version": "3.13.0", | ||
"version": "3.14.0", | ||
"description": "Mock HTTP server for testing HTTP clients and stubbing webservices", | ||
@@ -5,0 +5,0 @@ "exports": { |
@@ -6,3 +6,3 @@ import * as _ from 'lodash'; | ||
const { pki, md, util: { encode64 } } = forge; | ||
const { asn1, pki, md, util } = forge; | ||
@@ -67,3 +67,6 @@ export type CAOptions = (CertDataOptions | CertPathOptions); | ||
countryName?: string, | ||
bits?: number | ||
bits?: number, | ||
nameConstraints?: { | ||
permitted?: string[] | ||
} | ||
} = {}) { | ||
@@ -103,7 +106,19 @@ options = _.defaults({}, options, { | ||
cert.setExtensions([ | ||
const extensions: any[] = [ | ||
{ name: 'basicConstraints', cA: true, critical: true }, | ||
{ name: 'keyUsage', keyCertSign: true, digitalSignature: true, nonRepudiation: true, cRLSign: true, critical: true }, | ||
{ name: 'subjectKeyIdentifier' } | ||
]); | ||
{ name: 'subjectKeyIdentifier' }, | ||
]; | ||
const permittedDomains = options.nameConstraints?.permitted || []; | ||
if(permittedDomains.length > 0) { | ||
extensions.push({ | ||
critical: true, | ||
id: '2.5.29.30', | ||
name: 'nameConstraints', | ||
value: generateNameConstraints({ | ||
permitted: permittedDomains, | ||
}), | ||
}) | ||
} | ||
cert.setExtensions(extensions); | ||
@@ -122,5 +137,53 @@ // Self-issued too | ||
type GenerateNameConstraintsInput = { | ||
/** | ||
* Array of permitted domains | ||
*/ | ||
permitted?: string[]; | ||
}; | ||
/** | ||
* Generate name constraints in conformance with | ||
* [RFC 5280 § 4.2.1.10](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10) | ||
*/ | ||
function generateNameConstraints( | ||
input: GenerateNameConstraintsInput | ||
): forge.asn1.Asn1 { | ||
const domainsToSequence = (ips: string[]) => | ||
ips.map((domain) => { | ||
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ | ||
asn1.create( | ||
asn1.Class.CONTEXT_SPECIFIC, | ||
2, | ||
false, | ||
util.encodeUtf8(domain) | ||
), | ||
]); | ||
}); | ||
const permittedAndExcluded: forge.asn1.Asn1[] = []; | ||
if (input.permitted && input.permitted.length > 0) { | ||
permittedAndExcluded.push( | ||
asn1.create( | ||
asn1.Class.CONTEXT_SPECIFIC, | ||
0, | ||
true, | ||
domainsToSequence(input.permitted) | ||
) | ||
); | ||
} | ||
return asn1.create( | ||
asn1.Class.UNIVERSAL, | ||
asn1.Type.SEQUENCE, | ||
true, | ||
permittedAndExcluded | ||
); | ||
} | ||
export function generateSPKIFingerprint(certPem: PEM) { | ||
let cert = pki.certificateFromPem(certPem.toString('utf8')); | ||
return encode64( | ||
return util.encode64( | ||
pki.getPublicKeyFingerprint(cert.publicKey, { | ||
@@ -127,0 +190,0 @@ type: 'SubjectPublicKeyInfo', |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1541433
26319