@peculiar/x509
Advanced tools
Comparing version 1.3.3-beta.0 to 1.4.0
@@ -13,2 +13,3 @@ import { SubjectPublicKeyInfo } from "@peculiar/asn1-x509"; | ||
getThumbprint(algorithm: globalThis.AlgorithmIdentifier, crypto?: Crypto): Promise<ArrayBuffer>; | ||
getKeyIdentifier(crypto?: Crypto): Promise<ArrayBuffer>; | ||
} |
@@ -28,7 +28,7 @@ /*! | ||
import * as asn1X509 from '@peculiar/asn1-x509'; | ||
import { Extension as Extension$1, Name as Name$1, AttributeTypeAndValue, RelativeDistinguishedName, AlgorithmIdentifier, SubjectPublicKeyInfo, Certificate, BasicConstraints, id_ce_basicConstraints, ExtendedKeyUsage, id_ce_extKeyUsage, KeyUsage, id_ce_keyUsage, SubjectKeyIdentifier, id_ce_subjectKeyIdentifier, Attribute as Attribute$1, Extensions } from '@peculiar/asn1-x509'; | ||
import { Extension as Extension$1, AlgorithmIdentifier, SubjectPublicKeyInfo, Name as Name$1, AttributeTypeAndValue, RelativeDistinguishedName, Certificate, BasicConstraints, id_ce_basicConstraints, ExtendedKeyUsage, id_ce_extKeyUsage, KeyUsage, id_ce_keyUsage, SubjectKeyIdentifier, id_ce_subjectKeyIdentifier, Attribute as Attribute$1, Extensions } from '@peculiar/asn1-x509'; | ||
import { BufferSourceConverter, isEqual, Convert, combine } from 'pvtsutils'; | ||
import { container, injectable } from 'tsyringe'; | ||
import * as asn1Rsa from '@peculiar/asn1-rsa'; | ||
import { id_rsaEncryption, RSAPublicKey } from '@peculiar/asn1-rsa'; | ||
import { container, injectable } from 'tsyringe'; | ||
import * as asnPkcs9 from '@peculiar/asn1-pkcs9'; | ||
@@ -121,191 +121,2 @@ import { id_pkcs9_at_extensionRequest } from '@peculiar/asn1-pkcs9'; | ||
class NameIdentifier { | ||
constructor() { | ||
this.items = {}; | ||
} | ||
get(idOrName) { | ||
return this.items[idOrName] || null; | ||
} | ||
register(id, name) { | ||
this.items[id] = name; | ||
this.items[name] = id; | ||
} | ||
} | ||
const names = new NameIdentifier(); | ||
names.register("CN", "2.5.4.3"); | ||
names.register("L", "2.5.4.7"); | ||
names.register("ST", "2.5.4.8"); | ||
names.register("O", "2.5.4.10"); | ||
names.register("OU", "2.5.4.11"); | ||
names.register("C", "2.5.4.6"); | ||
names.register("DC", "0.9.2342.19200300.100.1.25"); | ||
names.register("E", "1.2.840.113549.1.9.1"); | ||
names.register("G", "2.5.4.42"); | ||
names.register("I", "2.5.4.43"); | ||
names.register("SN", "2.5.4.4"); | ||
names.register("T", "2.5.4.12"); | ||
function replaceUnknownCharacter(text, char) { | ||
return `\\${Convert.ToHex(Convert.FromUtf8String(char)).toUpperCase()}`; | ||
} | ||
function escape(data) { | ||
return data | ||
.replace(/([,+"\\<>;])/g, "\\$1") | ||
.replace(/^([ #])/, "\\$1") | ||
.replace(/([ ]$)/, "\\$1") | ||
.replace(/([\r\n\t])/, replaceUnknownCharacter); | ||
} | ||
class Name { | ||
constructor(data, extraNames = {}) { | ||
this.extraNames = new NameIdentifier(); | ||
this.asn = new Name$1(); | ||
for (const key in extraNames) { | ||
if (Object.prototype.hasOwnProperty.call(extraNames, key)) { | ||
const value = extraNames[key]; | ||
this.extraNames.register(key, value); | ||
} | ||
} | ||
if (typeof data === "string") { | ||
this.asn = this.fromString(data); | ||
} | ||
else if (data instanceof Name$1) { | ||
this.asn = data; | ||
} | ||
else if (BufferSourceConverter.isBufferSource(data)) { | ||
this.asn = AsnConvert.parse(data, Name$1); | ||
} | ||
else { | ||
this.asn = this.fromJSON(data); | ||
} | ||
} | ||
getName(idOrName) { | ||
return this.extraNames.get(idOrName) || names.get(idOrName); | ||
} | ||
toString() { | ||
return this.asn.map(rdn => rdn.map(o => { | ||
const type = this.getName(o.type) || o.type; | ||
const value = o.value.anyValue | ||
? `#${Convert.ToHex(o.value.anyValue)}` | ||
: escape(o.value.toString()); | ||
return `${type}=${value}`; | ||
}) | ||
.join("+")) | ||
.join(", "); | ||
} | ||
toJSON() { | ||
var _a; | ||
const json = []; | ||
for (const rdn of this.asn) { | ||
const jsonItem = {}; | ||
for (const attr of rdn) { | ||
const type = this.getName(attr.type) || attr.type; | ||
(_a = jsonItem[type]) !== null && _a !== void 0 ? _a : (jsonItem[type] = []); | ||
jsonItem[type].push(attr.value.anyValue ? `#${Convert.ToHex(attr.value.anyValue)}` : attr.value.toString()); | ||
} | ||
json.push(jsonItem); | ||
} | ||
return json; | ||
} | ||
fromString(data) { | ||
const asn = new Name$1(); | ||
const regex = /(\d\.[\d.]*\d|[A-Za-z]+)=((?:"")|(?:".*?[^\\]")|(?:[^,+].*?(?:[^\\][,+]))|(?:))([,+])?/g; | ||
let matches = null; | ||
let level = ","; | ||
while (matches = regex.exec(`${data},`)) { | ||
let [, type, value] = matches; | ||
const lastChar = value[value.length - 1]; | ||
if (lastChar === "," || lastChar === "+") { | ||
value = value.slice(0, value.length - 1); | ||
matches[3] = lastChar; | ||
} | ||
const next = matches[3]; | ||
if (!/[\d.]+/.test(type)) { | ||
type = this.getName(type) || ""; | ||
} | ||
if (!type) { | ||
throw new Error(`Cannot get OID for name type '${type}'`); | ||
} | ||
const attr = new AttributeTypeAndValue({ type }); | ||
if (value.charAt(0) === "#") { | ||
attr.value.anyValue = Convert.FromHex(value.slice(1)); | ||
} | ||
else { | ||
const quotedMatches = /"(.*?[^\\])?"/.exec(value); | ||
if (quotedMatches) { | ||
value = quotedMatches[1]; | ||
} | ||
value = value | ||
.replace(/\\0a/ig, "\n") | ||
.replace(/\\0d/ig, "\r") | ||
.replace(/\\0g/ig, "\t") | ||
.replace(/\\(.)/g, "$1"); | ||
if (type === this.getName("E") || type === this.getName("DC")) { | ||
attr.value.ia5String = value; | ||
} | ||
else { | ||
attr.value.printableString = value; | ||
} | ||
} | ||
if (level === "+") { | ||
asn[asn.length - 1].push(attr); | ||
} | ||
else { | ||
asn.push(new RelativeDistinguishedName([attr])); | ||
} | ||
level = next; | ||
} | ||
return asn; | ||
} | ||
fromJSON(data) { | ||
const asn = new Name$1(); | ||
for (const item of data) { | ||
const asnRdn = new RelativeDistinguishedName(); | ||
for (const type in item) { | ||
let typeId = type; | ||
if (!/[\d.]+/.test(type)) { | ||
typeId = this.getName(type) || ""; | ||
} | ||
if (!typeId) { | ||
throw new Error(`Cannot get OID for name type '${type}'`); | ||
} | ||
const values = item[type]; | ||
for (const value of values) { | ||
const asnAttr = new AttributeTypeAndValue({ type: typeId }); | ||
if (value[0] === "#") { | ||
asnAttr.value.anyValue = Convert.FromHex(value.slice(1)); | ||
} | ||
else { | ||
if (typeId === this.getName("E") || typeId === this.getName("DC")) { | ||
asnAttr.value.ia5String = value; | ||
} | ||
else { | ||
asnAttr.value.printableString = value; | ||
} | ||
} | ||
asnRdn.push(asnAttr); | ||
} | ||
} | ||
asn.push(asnRdn); | ||
} | ||
return asn; | ||
} | ||
toArrayBuffer() { | ||
return AsnConvert.serialize(this.asn); | ||
} | ||
} | ||
class ExtensionFactory { | ||
static register(id, type) { | ||
this.items.set(id, type); | ||
} | ||
static create(data) { | ||
const extension = new Extension(data); | ||
const Type = this.items.get(extension.type); | ||
if (Type) { | ||
return new Type(data); | ||
} | ||
return extension; | ||
} | ||
} | ||
ExtensionFactory.items = new Map(); | ||
const diAlgorithm = "crypto.algorithm"; | ||
@@ -516,4 +327,200 @@ class AlgorithmProvider { | ||
} | ||
async getKeyIdentifier(crypto) { | ||
if (!crypto) { | ||
crypto = cryptoProvider.get(); | ||
} | ||
const asn = AsnConvert.parse(this.rawData, SubjectPublicKeyInfo); | ||
return await crypto.subtle.digest("SHA-1", asn.subjectPublicKey); | ||
} | ||
} | ||
class NameIdentifier { | ||
constructor() { | ||
this.items = {}; | ||
} | ||
get(idOrName) { | ||
return this.items[idOrName] || null; | ||
} | ||
register(id, name) { | ||
this.items[id] = name; | ||
this.items[name] = id; | ||
} | ||
} | ||
const names = new NameIdentifier(); | ||
names.register("CN", "2.5.4.3"); | ||
names.register("L", "2.5.4.7"); | ||
names.register("ST", "2.5.4.8"); | ||
names.register("O", "2.5.4.10"); | ||
names.register("OU", "2.5.4.11"); | ||
names.register("C", "2.5.4.6"); | ||
names.register("DC", "0.9.2342.19200300.100.1.25"); | ||
names.register("E", "1.2.840.113549.1.9.1"); | ||
names.register("G", "2.5.4.42"); | ||
names.register("I", "2.5.4.43"); | ||
names.register("SN", "2.5.4.4"); | ||
names.register("T", "2.5.4.12"); | ||
function replaceUnknownCharacter(text, char) { | ||
return `\\${Convert.ToHex(Convert.FromUtf8String(char)).toUpperCase()}`; | ||
} | ||
function escape(data) { | ||
return data | ||
.replace(/([,+"\\<>;])/g, "\\$1") | ||
.replace(/^([ #])/, "\\$1") | ||
.replace(/([ ]$)/, "\\$1") | ||
.replace(/([\r\n\t])/, replaceUnknownCharacter); | ||
} | ||
class Name { | ||
constructor(data, extraNames = {}) { | ||
this.extraNames = new NameIdentifier(); | ||
this.asn = new Name$1(); | ||
for (const key in extraNames) { | ||
if (Object.prototype.hasOwnProperty.call(extraNames, key)) { | ||
const value = extraNames[key]; | ||
this.extraNames.register(key, value); | ||
} | ||
} | ||
if (typeof data === "string") { | ||
this.asn = this.fromString(data); | ||
} | ||
else if (data instanceof Name$1) { | ||
this.asn = data; | ||
} | ||
else if (BufferSourceConverter.isBufferSource(data)) { | ||
this.asn = AsnConvert.parse(data, Name$1); | ||
} | ||
else { | ||
this.asn = this.fromJSON(data); | ||
} | ||
} | ||
getName(idOrName) { | ||
return this.extraNames.get(idOrName) || names.get(idOrName); | ||
} | ||
toString() { | ||
return this.asn.map(rdn => rdn.map(o => { | ||
const type = this.getName(o.type) || o.type; | ||
const value = o.value.anyValue | ||
? `#${Convert.ToHex(o.value.anyValue)}` | ||
: escape(o.value.toString()); | ||
return `${type}=${value}`; | ||
}) | ||
.join("+")) | ||
.join(", "); | ||
} | ||
toJSON() { | ||
var _a; | ||
const json = []; | ||
for (const rdn of this.asn) { | ||
const jsonItem = {}; | ||
for (const attr of rdn) { | ||
const type = this.getName(attr.type) || attr.type; | ||
(_a = jsonItem[type]) !== null && _a !== void 0 ? _a : (jsonItem[type] = []); | ||
jsonItem[type].push(attr.value.anyValue ? `#${Convert.ToHex(attr.value.anyValue)}` : attr.value.toString()); | ||
} | ||
json.push(jsonItem); | ||
} | ||
return json; | ||
} | ||
fromString(data) { | ||
const asn = new Name$1(); | ||
const regex = /(\d\.[\d.]*\d|[A-Za-z]+)=((?:"")|(?:".*?[^\\]")|(?:[^,+].*?(?:[^\\][,+]))|(?:))([,+])?/g; | ||
let matches = null; | ||
let level = ","; | ||
while (matches = regex.exec(`${data},`)) { | ||
let [, type, value] = matches; | ||
const lastChar = value[value.length - 1]; | ||
if (lastChar === "," || lastChar === "+") { | ||
value = value.slice(0, value.length - 1); | ||
matches[3] = lastChar; | ||
} | ||
const next = matches[3]; | ||
if (!/[\d.]+/.test(type)) { | ||
type = this.getName(type) || ""; | ||
} | ||
if (!type) { | ||
throw new Error(`Cannot get OID for name type '${type}'`); | ||
} | ||
const attr = new AttributeTypeAndValue({ type }); | ||
if (value.charAt(0) === "#") { | ||
attr.value.anyValue = Convert.FromHex(value.slice(1)); | ||
} | ||
else { | ||
const quotedMatches = /"(.*?[^\\])?"/.exec(value); | ||
if (quotedMatches) { | ||
value = quotedMatches[1]; | ||
} | ||
value = value | ||
.replace(/\\0a/ig, "\n") | ||
.replace(/\\0d/ig, "\r") | ||
.replace(/\\0g/ig, "\t") | ||
.replace(/\\(.)/g, "$1"); | ||
if (type === this.getName("E") || type === this.getName("DC")) { | ||
attr.value.ia5String = value; | ||
} | ||
else { | ||
attr.value.printableString = value; | ||
} | ||
} | ||
if (level === "+") { | ||
asn[asn.length - 1].push(attr); | ||
} | ||
else { | ||
asn.push(new RelativeDistinguishedName([attr])); | ||
} | ||
level = next; | ||
} | ||
return asn; | ||
} | ||
fromJSON(data) { | ||
const asn = new Name$1(); | ||
for (const item of data) { | ||
const asnRdn = new RelativeDistinguishedName(); | ||
for (const type in item) { | ||
let typeId = type; | ||
if (!/[\d.]+/.test(type)) { | ||
typeId = this.getName(type) || ""; | ||
} | ||
if (!typeId) { | ||
throw new Error(`Cannot get OID for name type '${type}'`); | ||
} | ||
const values = item[type]; | ||
for (const value of values) { | ||
const asnAttr = new AttributeTypeAndValue({ type: typeId }); | ||
if (value[0] === "#") { | ||
asnAttr.value.anyValue = Convert.FromHex(value.slice(1)); | ||
} | ||
else { | ||
if (typeId === this.getName("E") || typeId === this.getName("DC")) { | ||
asnAttr.value.ia5String = value; | ||
} | ||
else { | ||
asnAttr.value.printableString = value; | ||
} | ||
} | ||
asnRdn.push(asnAttr); | ||
} | ||
} | ||
asn.push(asnRdn); | ||
} | ||
return asn; | ||
} | ||
toArrayBuffer() { | ||
return AsnConvert.serialize(this.asn); | ||
} | ||
} | ||
class ExtensionFactory { | ||
static register(id, type) { | ||
this.items.set(id, type); | ||
} | ||
static create(data) { | ||
const extension = new Extension(data); | ||
const Type = this.items.get(extension.type); | ||
if (Type) { | ||
return new Type(data); | ||
} | ||
return extension; | ||
} | ||
} | ||
ExtensionFactory.items = new Map(); | ||
const diAsnSignatureFormatter = "crypto.signatureFormatter"; | ||
@@ -677,4 +684,5 @@ class AsnDefaultSignatureFormatter { | ||
const spki = await crypto.subtle.exportKey("spki", publicKey); | ||
const ski = await crypto.subtle.digest("SHA-1", spki); | ||
return new AuthorityKeyIdentifierExtension(Convert.ToHex(ski), critical); | ||
const key = new PublicKey(spki); | ||
const id = await key.getKeyIdentifier(crypto); | ||
return new AuthorityKeyIdentifierExtension(Convert.ToHex(id), critical); | ||
} | ||
@@ -780,4 +788,5 @@ else { | ||
const spki = await crypto.subtle.exportKey("spki", publicKey); | ||
const ski = await crypto.subtle.digest("SHA-1", spki); | ||
return new SubjectKeyIdentifierExtension(Convert.ToHex(ski), critical); | ||
const key = new PublicKey(spki); | ||
const id = await key.getKeyIdentifier(crypto); | ||
return new SubjectKeyIdentifierExtension(Convert.ToHex(id), critical); | ||
} | ||
@@ -784,0 +793,0 @@ } |
{ | ||
"name": "@peculiar/x509", | ||
"version": "1.3.3-beta.0", | ||
"version": "1.4.0", | ||
"description": "@peculiar/x509 is an easy to use TypeScript/Javascript library based on @peculiar/asn1-schema that makes generating X.509 Certificates and Certificate Requests as well as validating certificate chains easy", | ||
@@ -5,0 +5,0 @@ "main": "build/x509.cjs.js", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
327857
4260
0