@credo-ts/core 0.6.2-alpha-20251222120740 → 0.6.2-pr-2610-20260107224500
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.
- package/build/crypto/index.d.mts +1 -1
- package/build/crypto/index.mjs +1 -0
- package/build/crypto/webcrypto/CredoWebCryptoKey.d.mts +3 -3
- package/build/crypto/webcrypto/CredoWebCryptoKey.mjs.map +1 -1
- package/build/crypto/webcrypto/index.d.mts +1 -1
- package/build/crypto/webcrypto/index.mjs +1 -0
- package/build/crypto/webcrypto/types.d.mts +16 -2
- package/build/crypto/webcrypto/types.d.mts.map +1 -1
- package/build/crypto/webcrypto/types.mjs +50 -1
- package/build/crypto/webcrypto/types.mjs.map +1 -1
- package/build/crypto/webcrypto/utils/keyAlgorithmConversion.d.mts +2 -2
- package/build/crypto/webcrypto/utils/keyAlgorithmConversion.mjs +31 -2
- package/build/crypto/webcrypto/utils/keyAlgorithmConversion.mjs.map +1 -1
- package/build/index.d.mts +4 -3
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +3 -1
- package/build/index.mjs.map +1 -1
- package/build/modules/dids/domain/keyDidDocument.mjs +2 -2
- package/build/modules/dids/domain/keyDidDocument.mjs.map +1 -1
- package/build/modules/dids/methods/jwk/DidJwk.mjs +1 -1
- package/build/modules/dids/methods/jwk/DidJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/PublicJwk.d.mts +9 -2
- package/build/modules/kms/jwk/PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/PublicJwk.mjs +19 -9
- package/build/modules/kms/jwk/PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P256PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/ec/P256PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P256PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/ec/P256PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P384PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/ec/P384PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P384PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/ec/P384PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P521PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/ec/P521PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/ec/P521PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/ec/P521PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/ec/Secp256k1PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/ec/Secp256k1PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/ec/Secp256k1PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/ec/Secp256k1PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/okp/Ed25519PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/okp/Ed25519PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/okp/Ed25519PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/okp/Ed25519PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/okp/X25519PublicJwk.d.mts +1 -1
- package/build/modules/kms/jwk/kty/okp/X25519PublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/okp/X25519PublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/okp/X25519PublicJwk.mjs.map +1 -1
- package/build/modules/kms/jwk/kty/rsa/RsaPublicJwk.d.mts +2 -2
- package/build/modules/kms/jwk/kty/rsa/RsaPublicJwk.d.mts.map +1 -1
- package/build/modules/kms/jwk/kty/rsa/RsaPublicJwk.mjs +1 -1
- package/build/modules/kms/jwk/kty/rsa/RsaPublicJwk.mjs.map +1 -1
- package/build/modules/mdoc/MdocContext.mjs +1 -1
- package/build/modules/mdoc/MdocContext.mjs.map +1 -1
- package/build/modules/x509/CertificateSigningRequest.d.mts +58 -0
- package/build/modules/x509/CertificateSigningRequest.d.mts.map +1 -0
- package/build/modules/x509/CertificateSigningRequest.mjs +148 -0
- package/build/modules/x509/CertificateSigningRequest.mjs.map +1 -0
- package/build/modules/x509/X509Api.d.mts +4 -1
- package/build/modules/x509/X509Api.d.mts.map +1 -1
- package/build/modules/x509/X509Api.mjs +6 -0
- package/build/modules/x509/X509Api.mjs.map +1 -1
- package/build/modules/x509/X509Certificate.d.mts +4 -4
- package/build/modules/x509/X509Certificate.d.mts.map +1 -1
- package/build/modules/x509/X509Certificate.mjs +3 -3
- package/build/modules/x509/X509Certificate.mjs.map +1 -1
- package/build/modules/x509/X509Service.d.mts +8 -3
- package/build/modules/x509/X509Service.d.mts.map +1 -1
- package/build/modules/x509/X509Service.mjs +10 -2
- package/build/modules/x509/X509Service.mjs.map +1 -1
- package/build/modules/x509/X509ServiceOptions.d.mts +22 -1
- package/build/modules/x509/X509ServiceOptions.d.mts.map +1 -1
- package/build/modules/x509/index.d.mts +2 -1
- package/build/modules/x509/index.mjs +1 -0
- package/build/modules/x509/utils/nameConversion.mjs +1 -1
- package/build/modules/x509/utils/nameConversion.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AnyUint8Array } from "../../types.mjs";
|
|
2
|
+
import { CredoWebCrypto } from "../../crypto/webcrypto/CredoWebCrypto.mjs";
|
|
3
|
+
import "../../crypto/webcrypto/index.mjs";
|
|
4
|
+
import { X509CreateCertificateSigningRequestOptions } from "./X509ServiceOptions.mjs";
|
|
5
|
+
import { X509ExtendedKeyUsage, X509KeyUsage } from "./X509Certificate.mjs";
|
|
6
|
+
import { PublicJwk } from "../kms/jwk/PublicJwk.mjs";
|
|
7
|
+
import "../kms/index.mjs";
|
|
8
|
+
import * as x509 from "@peculiar/x509";
|
|
9
|
+
|
|
10
|
+
//#region src/modules/x509/CertificateSigningRequest.d.ts
|
|
11
|
+
type CertificateSigningRequestOptions = {
|
|
12
|
+
publicJwk: PublicJwk;
|
|
13
|
+
certificateRequest: x509.Pkcs10CertificateRequest;
|
|
14
|
+
};
|
|
15
|
+
declare class CertificateSigningRequest {
|
|
16
|
+
publicJwk: PublicJwk;
|
|
17
|
+
private certificateRequest;
|
|
18
|
+
private constructor();
|
|
19
|
+
set keyId(keyId: string);
|
|
20
|
+
get keyId(): string;
|
|
21
|
+
get hasKeyId(): boolean;
|
|
22
|
+
static fromRawCertificateRequest(rawCertificateRequest: AnyUint8Array): CertificateSigningRequest;
|
|
23
|
+
static fromEncodedCertificateRequest(encodedCertificateRequest: string): CertificateSigningRequest;
|
|
24
|
+
private static parseCertificateRequest;
|
|
25
|
+
private getMatchingExtensions;
|
|
26
|
+
get rawCertificateRequest(): Uint8Array<ArrayBuffer>;
|
|
27
|
+
get subjectAlternativeNames(): {
|
|
28
|
+
type: x509.GeneralNameType;
|
|
29
|
+
value: string;
|
|
30
|
+
}[];
|
|
31
|
+
get sanDnsNames(): string[];
|
|
32
|
+
get sanUriNames(): string[];
|
|
33
|
+
get subjectKeyIdentifier(): string | undefined;
|
|
34
|
+
get keyUsage(): X509KeyUsage[];
|
|
35
|
+
get extendedKeyUsage(): X509ExtendedKeyUsage[];
|
|
36
|
+
isExtensionCritical(id: string): boolean;
|
|
37
|
+
static create(options: X509CreateCertificateSigningRequestOptions, webCrypto: CredoWebCrypto): Promise<CertificateSigningRequest>;
|
|
38
|
+
get subject(): string;
|
|
39
|
+
get subjectName(): string;
|
|
40
|
+
verify(webCrypto: CredoWebCrypto): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get the data elements of the certificate signing request
|
|
43
|
+
*/
|
|
44
|
+
get data(): {
|
|
45
|
+
subjectName: string;
|
|
46
|
+
subject: string;
|
|
47
|
+
pem: string;
|
|
48
|
+
};
|
|
49
|
+
getSubjectNameField(field: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* @param format the format to export to, defaults to `pem`
|
|
52
|
+
*/
|
|
53
|
+
toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url'): string;
|
|
54
|
+
equal(certificateRequest: CertificateSigningRequest): boolean;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { CertificateSigningRequest, CertificateSigningRequestOptions };
|
|
58
|
+
//# sourceMappingURL=CertificateSigningRequest.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CertificateSigningRequest.d.mts","names":[],"sources":["../../../src/modules/x509/CertificateSigningRequest.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KA6BY,gCAAA;aACC;sBACS,IAAA,CAAK;;cAGd,yBAAA;aACO;EANR,QAAA,kBAAA;EAKC,QAAA,WAAA,CAAA;EACO,IAAA,KAAA,CAAA,KAAA,EAAA,MAAA;EAoB6C,IAAA,KAAA,CAAA,CAAA,EAAA,MAAA;EAAgB,IAAA,QAAA,CAAA,CAAA,EAAA,OAAA;EAKC,OAAA,yBAAA,CAAA,qBAAA,EALjB,aAKiB,CAAA,EALD,yBAKC;EAqBhD,OAAA,6BAAA,CAAA,yBAAA,EAAA,MAAA,CAAA,EArBgD,yBAqBhD;EAAA,eAAA,uBAAA;;EA6Bb,IAAA,qBAAA,CAAA,CAAA,EA7Ba,UA6Bb,CA7Ba,WA6Bb,CAAA;EAiBQ,IAAA,uBAAA,CAAA,CAAA,EAAA;IAqBS,IAAA,sBAAA;IAAuD,KAAA,EAAA,MAAA;EAAc,CAAA,EAAA;EAAA,IAAA,WAAA,CAAA,CAAA,EAAA,MAAA,EAAA;EAuD1E,IAAA,WAAA,CAAA,CAAA,EAAA,MAAA,EAAA;EAAc,IAAA,oBAAA,CAAA,CAAA,EAAA,MAAA,GAAA,SAAA;EAgCZ,IAAA,QAAA,CAAA,CAAA,EA7Hd,YA6Hc,EAAA;EAAyB,IAAA,gBAAA,CAAA,CAAA,EA5G/B,oBA4G+B,EAAA;;yBAvFtB,uDAAuD,iBAAc,QAAA;;;oBAuD1E,iBAAc;;;;;;;;;;;;;;4BAgCZ"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import "../kms/index.mjs";
|
|
4
|
+
import { CredoWebCryptoKey } from "../../crypto/webcrypto/CredoWebCryptoKey.mjs";
|
|
5
|
+
import { jwaAlgorithmToKeySignParams } from "../../crypto/webcrypto/types.mjs";
|
|
6
|
+
import { publicJwkToCryptoKeyAlgorithm, spkiToPublicJwk } from "../../crypto/webcrypto/utils/keyAlgorithmConversion.mjs";
|
|
7
|
+
import "../../crypto/webcrypto/utils/index.mjs";
|
|
8
|
+
import "../../crypto/webcrypto/index.mjs";
|
|
9
|
+
import { createExtendedKeyUsagesExtension, createKeyUsagesExtension, createSubjectAlternativeNameExtension, createSubjectKeyIdentifierExtension } from "./utils/extensions.mjs";
|
|
10
|
+
import { X509Error } from "./X509Error.mjs";
|
|
11
|
+
import { convertName } from "./utils/nameConversion.mjs";
|
|
12
|
+
import "./utils/index.mjs";
|
|
13
|
+
import { X509KeyUsage } from "./X509Certificate.mjs";
|
|
14
|
+
import { AsnParser } from "@peculiar/asn1-schema";
|
|
15
|
+
import { SubjectPublicKeyInfo, id_ce_extKeyUsage, id_ce_keyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier } from "@peculiar/asn1-x509";
|
|
16
|
+
import * as x509 from "@peculiar/x509";
|
|
17
|
+
|
|
18
|
+
//#region src/modules/x509/CertificateSigningRequest.ts
|
|
19
|
+
var CertificateSigningRequest = class CertificateSigningRequest {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.publicJwk = options.publicJwk;
|
|
22
|
+
this.certificateRequest = options.certificateRequest;
|
|
23
|
+
}
|
|
24
|
+
set keyId(keyId) {
|
|
25
|
+
this.publicJwk.keyId = keyId;
|
|
26
|
+
}
|
|
27
|
+
get keyId() {
|
|
28
|
+
return this.publicJwk.keyId;
|
|
29
|
+
}
|
|
30
|
+
get hasKeyId() {
|
|
31
|
+
return this.publicJwk.hasKeyId;
|
|
32
|
+
}
|
|
33
|
+
static fromRawCertificateRequest(rawCertificateRequest) {
|
|
34
|
+
const certificateRequest = new x509.Pkcs10CertificateRequest(rawCertificateRequest);
|
|
35
|
+
return CertificateSigningRequest.parseCertificateRequest(certificateRequest);
|
|
36
|
+
}
|
|
37
|
+
static fromEncodedCertificateRequest(encodedCertificateRequest) {
|
|
38
|
+
const certificateRequest = new x509.Pkcs10CertificateRequest(encodedCertificateRequest);
|
|
39
|
+
return CertificateSigningRequest.parseCertificateRequest(certificateRequest);
|
|
40
|
+
}
|
|
41
|
+
static parseCertificateRequest(certificateRequest) {
|
|
42
|
+
return new CertificateSigningRequest({
|
|
43
|
+
publicJwk: spkiToPublicJwk(AsnParser.parse(certificateRequest.publicKey.rawData, SubjectPublicKeyInfo)),
|
|
44
|
+
certificateRequest
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
getMatchingExtensions(objectIdentifier) {
|
|
48
|
+
const matchingExtensions = this.certificateRequest.extensions.filter((e) => e.type === objectIdentifier);
|
|
49
|
+
if (matchingExtensions.length === 0) return void 0;
|
|
50
|
+
return matchingExtensions;
|
|
51
|
+
}
|
|
52
|
+
get rawCertificateRequest() {
|
|
53
|
+
return new Uint8Array(this.certificateRequest.rawData);
|
|
54
|
+
}
|
|
55
|
+
get subjectAlternativeNames() {
|
|
56
|
+
return this.getMatchingExtensions(id_ce_subjectAltName)?.flatMap((s) => s.names.items ?? []).map((i) => ({
|
|
57
|
+
type: i.type,
|
|
58
|
+
value: i.value
|
|
59
|
+
})) ?? [];
|
|
60
|
+
}
|
|
61
|
+
get sanDnsNames() {
|
|
62
|
+
return this.subjectAlternativeNames.filter((san) => san.type === "dns").map((san) => san.value);
|
|
63
|
+
}
|
|
64
|
+
get sanUriNames() {
|
|
65
|
+
return this.subjectAlternativeNames.filter((san) => san.type === "url").map((san) => san.value);
|
|
66
|
+
}
|
|
67
|
+
get subjectKeyIdentifier() {
|
|
68
|
+
const keyIds = this.getMatchingExtensions(id_ce_subjectKeyIdentifier)?.map((e) => e.keyId);
|
|
69
|
+
if (keyIds && keyIds.length > 1) throw new X509Error("Multiple Subject Key Identifiers are not allowed");
|
|
70
|
+
return keyIds?.[0];
|
|
71
|
+
}
|
|
72
|
+
get keyUsage() {
|
|
73
|
+
const keyUsages = this.getMatchingExtensions(id_ce_keyUsage)?.map((e) => e.usages);
|
|
74
|
+
if (keyUsages && keyUsages.length > 1) throw new X509Error("Multiple Key Usages are not allowed");
|
|
75
|
+
if (keyUsages && keyUsages.length > 0) return Object.values(X509KeyUsage).filter((key) => typeof key === "number").filter((flagValue) => (keyUsages[0] & flagValue) === flagValue).map((flagValue) => flagValue);
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
get extendedKeyUsage() {
|
|
79
|
+
const extendedKeyUsages = this.getMatchingExtensions(id_ce_extKeyUsage)?.map((e) => e.usages);
|
|
80
|
+
if (extendedKeyUsages && extendedKeyUsages.length > 1) throw new X509Error("Multiple Extended Key Usages are not allowed");
|
|
81
|
+
return extendedKeyUsages?.[0] ?? [];
|
|
82
|
+
}
|
|
83
|
+
isExtensionCritical(id) {
|
|
84
|
+
const extension = this.getMatchingExtensions(id);
|
|
85
|
+
if (!extension) throw new X509Error(`extension with id '${id}' is not found`);
|
|
86
|
+
return !!extension[0].critical;
|
|
87
|
+
}
|
|
88
|
+
static async create(options, webCrypto) {
|
|
89
|
+
const signingKey = new CredoWebCryptoKey(options.subjectPublicKey, publicJwkToCryptoKeyAlgorithm(options.subjectPublicKey), false, "private", ["sign"]);
|
|
90
|
+
const publicKey = new CredoWebCryptoKey(options.subjectPublicKey, publicJwkToCryptoKeyAlgorithm(options.subjectPublicKey), true, "public", ["verify"]);
|
|
91
|
+
const extensions = [];
|
|
92
|
+
extensions.push(createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, { publicJwk: options.subjectPublicKey }));
|
|
93
|
+
extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage));
|
|
94
|
+
extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage));
|
|
95
|
+
extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName));
|
|
96
|
+
const subjectName = convertName(options.subject);
|
|
97
|
+
const jwaAlgorithm = options.subjectPublicKey.signatureAlgorithm;
|
|
98
|
+
const signingAlgorithm = jwaAlgorithmToKeySignParams(jwaAlgorithm);
|
|
99
|
+
const csr = await x509.Pkcs10CertificateRequestGenerator.create({
|
|
100
|
+
keys: {
|
|
101
|
+
publicKey,
|
|
102
|
+
privateKey: signingKey
|
|
103
|
+
},
|
|
104
|
+
name: subjectName,
|
|
105
|
+
signingAlgorithm,
|
|
106
|
+
extensions: extensions.filter((e) => e !== void 0)
|
|
107
|
+
}, webCrypto);
|
|
108
|
+
const csrInstance = CertificateSigningRequest.parseCertificateRequest(csr);
|
|
109
|
+
if (options.subjectPublicKey.hasKeyId) csrInstance.publicJwk.keyId = options.subjectPublicKey.keyId;
|
|
110
|
+
return csrInstance;
|
|
111
|
+
}
|
|
112
|
+
get subject() {
|
|
113
|
+
return this.certificateRequest.subject;
|
|
114
|
+
}
|
|
115
|
+
get subjectName() {
|
|
116
|
+
return this.certificateRequest.subjectName.toString();
|
|
117
|
+
}
|
|
118
|
+
async verify(webCrypto) {
|
|
119
|
+
if (!await this.certificateRequest.verify(webCrypto)) throw new X509Error(`Certificate Signing Request for '${this.certificateRequest.subject}' has an invalid signature`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the data elements of the certificate signing request
|
|
123
|
+
*/
|
|
124
|
+
get data() {
|
|
125
|
+
return {
|
|
126
|
+
subjectName: this.certificateRequest.subjectName.toString(),
|
|
127
|
+
subject: this.certificateRequest.subject,
|
|
128
|
+
pem: this.certificateRequest.toString()
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
getSubjectNameField(field) {
|
|
132
|
+
return this.certificateRequest.subjectName.getField(field);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* @param format the format to export to, defaults to `pem`
|
|
136
|
+
*/
|
|
137
|
+
toString(format) {
|
|
138
|
+
return this.certificateRequest.toString(format ?? "pem");
|
|
139
|
+
}
|
|
140
|
+
equal(certificateRequest) {
|
|
141
|
+
const parsedOther = new x509.Pkcs10CertificateRequest(certificateRequest.rawCertificateRequest);
|
|
142
|
+
return this.certificateRequest.equal(parsedOther);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
export { CertificateSigningRequest };
|
|
148
|
+
//# sourceMappingURL=CertificateSigningRequest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CertificateSigningRequest.mjs","names":["extensions: Array<x509.Extension | undefined>"],"sources":["../../../src/modules/x509/CertificateSigningRequest.ts"],"sourcesContent":["import { AsnParser } from '@peculiar/asn1-schema'\nimport {\n id_ce_extKeyUsage,\n id_ce_keyUsage,\n id_ce_subjectAltName,\n id_ce_subjectKeyIdentifier,\n SubjectPublicKeyInfo,\n} from '@peculiar/asn1-x509'\nimport * as x509 from '@peculiar/x509'\nimport {\n CredoWebCrypto,\n CredoWebCryptoKey,\n jwaAlgorithmToKeySignParams,\n publicJwkToCryptoKeyAlgorithm,\n} from '../../crypto/webcrypto'\nimport { spkiToPublicJwk } from '../../crypto/webcrypto/utils'\nimport type { AnyUint8Array } from '../../types'\nimport { PublicJwk } from '../kms'\nimport {\n convertName,\n createExtendedKeyUsagesExtension,\n createKeyUsagesExtension,\n createSubjectAlternativeNameExtension,\n createSubjectKeyIdentifierExtension,\n} from './utils'\nimport { X509ExtendedKeyUsage, X509KeyUsage } from './X509Certificate'\nimport { X509Error } from './X509Error'\nimport type { X509CreateCertificateSigningRequestOptions } from './X509ServiceOptions'\n\nexport type CertificateSigningRequestOptions = {\n publicJwk: PublicJwk\n certificateRequest: x509.Pkcs10CertificateRequest\n}\n\nexport class CertificateSigningRequest {\n public publicJwk: PublicJwk\n private certificateRequest: x509.Pkcs10CertificateRequest\n\n private constructor(options: CertificateSigningRequestOptions) {\n this.publicJwk = options.publicJwk\n this.certificateRequest = options.certificateRequest\n }\n\n public set keyId(keyId: string) {\n this.publicJwk.keyId = keyId\n }\n\n public get keyId(): string {\n return this.publicJwk.keyId\n }\n\n public get hasKeyId(): boolean {\n return this.publicJwk.hasKeyId\n }\n\n public static fromRawCertificateRequest(rawCertificateRequest: AnyUint8Array): CertificateSigningRequest {\n const certificateRequest = new x509.Pkcs10CertificateRequest(rawCertificateRequest)\n return CertificateSigningRequest.parseCertificateRequest(certificateRequest)\n }\n\n public static fromEncodedCertificateRequest(encodedCertificateRequest: string): CertificateSigningRequest {\n const certificateRequest = new x509.Pkcs10CertificateRequest(encodedCertificateRequest)\n return CertificateSigningRequest.parseCertificateRequest(certificateRequest)\n }\n\n private static parseCertificateRequest(certificateRequest: x509.Pkcs10CertificateRequest): CertificateSigningRequest {\n const spki = AsnParser.parse(certificateRequest.publicKey.rawData, SubjectPublicKeyInfo)\n const publicJwk = spkiToPublicJwk(spki)\n\n return new CertificateSigningRequest({\n publicJwk,\n certificateRequest,\n })\n }\n\n private getMatchingExtensions<T = { critical: boolean }>(objectIdentifier: string): Array<T> | undefined {\n const matchingExtensions = this.certificateRequest.extensions.filter((e) => e.type === objectIdentifier)\n if (matchingExtensions.length === 0) return undefined\n return matchingExtensions as Array<T>\n }\n\n public get rawCertificateRequest() {\n return new Uint8Array(this.certificateRequest.rawData)\n }\n\n public get subjectAlternativeNames() {\n const san = this.getMatchingExtensions<x509.SubjectAlternativeNameExtension>(id_ce_subjectAltName)\n return san?.flatMap((s) => s.names.items ?? []).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get sanDnsNames() {\n return this.subjectAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get sanUriNames() {\n return this.subjectAlternativeNames.filter((san) => san.type === 'url').map((san) => san.value)\n }\n\n public get subjectKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.SubjectKeyIdentifierExtension>(id_ce_subjectKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Subject Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n public get keyUsage() {\n const keyUsages = this.getMatchingExtensions<x509.KeyUsagesExtension>(id_ce_keyUsage)?.map((e) => e.usages)\n\n if (keyUsages && keyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n if (keyUsages && keyUsages.length > 0) {\n return Object.values(X509KeyUsage)\n .filter((key): key is number => typeof key === 'number')\n .filter((flagValue) => (keyUsages[0] & flagValue) === flagValue)\n .map((flagValue) => flagValue as X509KeyUsage)\n }\n\n return []\n }\n\n public get extendedKeyUsage() {\n const extendedKeyUsages = this.getMatchingExtensions<x509.ExtendedKeyUsageExtension>(id_ce_extKeyUsage)?.map(\n (e) => e.usages\n )\n\n if (extendedKeyUsages && extendedKeyUsages.length > 1) {\n throw new X509Error('Multiple Extended Key Usages are not allowed')\n }\n\n return (extendedKeyUsages?.[0] as Array<X509ExtendedKeyUsage> | undefined) ?? []\n }\n\n public isExtensionCritical(id: string): boolean {\n const extension = this.getMatchingExtensions(id)\n if (!extension) {\n throw new X509Error(`extension with id '${id}' is not found`)\n }\n\n return !!extension[0].critical\n }\n\n public static async create(options: X509CreateCertificateSigningRequestOptions, webCrypto: CredoWebCrypto) {\n const signingKey = new CredoWebCryptoKey(\n options.subjectPublicKey,\n publicJwkToCryptoKeyAlgorithm(options.subjectPublicKey),\n false,\n 'private',\n ['sign']\n )\n const publicKey = new CredoWebCryptoKey(\n options.subjectPublicKey,\n publicJwkToCryptoKeyAlgorithm(options.subjectPublicKey),\n true,\n 'public',\n ['verify']\n )\n\n const extensions: Array<x509.Extension | undefined> = []\n extensions.push(\n createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, {\n publicJwk: options.subjectPublicKey,\n })\n )\n extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage))\n extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage))\n extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName))\n\n const subjectName = convertName(options.subject)\n\n // Get the JWA signature algorithm from the public key and convert to KeySignParams\n const jwaAlgorithm = options.subjectPublicKey.signatureAlgorithm\n const signingAlgorithm = jwaAlgorithmToKeySignParams(jwaAlgorithm)\n\n const csr = await x509.Pkcs10CertificateRequestGenerator.create(\n {\n keys: { publicKey, privateKey: signingKey },\n name: subjectName,\n signingAlgorithm,\n extensions: extensions.filter((e) => e !== undefined),\n },\n webCrypto\n )\n\n const csrInstance = CertificateSigningRequest.parseCertificateRequest(csr)\n if (options.subjectPublicKey.hasKeyId) csrInstance.publicJwk.keyId = options.subjectPublicKey.keyId\n return csrInstance\n }\n\n public get subject() {\n return this.certificateRequest.subject\n }\n\n public get subjectName() {\n return this.certificateRequest.subjectName.toString()\n }\n\n public async verify(webCrypto: CredoWebCrypto) {\n const isValid = await this.certificateRequest.verify(webCrypto)\n\n if (!isValid) {\n throw new X509Error(\n `Certificate Signing Request for '${this.certificateRequest.subject}' has an invalid signature`\n )\n }\n }\n\n /**\n * Get the data elements of the certificate signing request\n */\n public get data() {\n return {\n subjectName: this.certificateRequest.subjectName.toString(),\n subject: this.certificateRequest.subject,\n pem: this.certificateRequest.toString(),\n }\n }\n\n public getSubjectNameField(field: string) {\n return this.certificateRequest.subjectName.getField(field)\n }\n\n /**\n * @param format the format to export to, defaults to `pem`\n */\n public toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url') {\n return this.certificateRequest.toString(format ?? 'pem')\n }\n\n public equal(certificateRequest: CertificateSigningRequest) {\n const parsedOther = new x509.Pkcs10CertificateRequest(certificateRequest.rawCertificateRequest)\n\n return this.certificateRequest.equal(parsedOther)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { X509CreateCertificateOptions, X509ValidateCertificateChainOptions } from "./X509ServiceOptions.mjs";
|
|
1
|
+
import { X509CreateCertificateOptions, X509CreateCertificateSigningRequestOptions, X509ParseCertificateSigningRequestOptions, X509ValidateCertificateChainOptions } from "./X509ServiceOptions.mjs";
|
|
2
2
|
import { X509Certificate } from "./X509Certificate.mjs";
|
|
3
|
+
import { CertificateSigningRequest } from "./CertificateSigningRequest.mjs";
|
|
3
4
|
import { X509ModuleConfig } from "./X509ModuleConfig.mjs";
|
|
4
5
|
import { AgentContext } from "../../agent/context/AgentContext.mjs";
|
|
5
6
|
import "../../agent/index.mjs";
|
|
@@ -18,6 +19,8 @@ declare class X509Api {
|
|
|
18
19
|
* @param options X509CreateCertificateOptions
|
|
19
20
|
*/
|
|
20
21
|
createCertificate(options: X509CreateCertificateOptions): Promise<X509Certificate>;
|
|
22
|
+
createCertificateSigningRequest(options: X509CreateCertificateSigningRequestOptions): Promise<CertificateSigningRequest>;
|
|
23
|
+
parseCertificateSigningRequest(options: X509ParseCertificateSigningRequestOptions): CertificateSigningRequest;
|
|
21
24
|
/**
|
|
22
25
|
* Validate a certificate chain.
|
|
23
26
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Api.d.mts","names":[],"sources":["../../../src/modules/x509/X509Api.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"X509Api.d.mts","names":[],"sources":["../../../src/modules/x509/X509Api.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;cAgBa,OAAA;;UAGM;4BADO,sBACP;EAHN;;;;;EAWyD,iBAAA,CAAA,OAAA,EAA5B,4BAA4B,CAAA,EAAA,OAAA,CAAA,eAAA,CAAA;EAAA,+BAAA,CAAA,OAAA,EAId,0CAJc,CAAA,EAI4B,OAJ5B,CAI4B,yBAAA,CAJ5B;EAId,8BAAA,CAAA,OAAA,EAIP,yCAJO,CAAA,EAIkC,yBAJlC;EAA0C;;;;;EAad,wBAAA,CAAA,OAAA,EAAnC,mCAAmC,CAAA,EAAA,OAAA,CAAA,eAAA,EAAA,CAAA"}
|
|
@@ -23,6 +23,12 @@ let X509Api = class X509Api$1 {
|
|
|
23
23
|
async createCertificate(options) {
|
|
24
24
|
return await X509Service.createCertificate(this.agentContext, options);
|
|
25
25
|
}
|
|
26
|
+
async createCertificateSigningRequest(options) {
|
|
27
|
+
return await X509Service.createCertificateSigningRequest(this.agentContext, options);
|
|
28
|
+
}
|
|
29
|
+
parseCertificateSigningRequest(options) {
|
|
30
|
+
return X509Service.parseCertificateSigningRequest(options);
|
|
31
|
+
}
|
|
26
32
|
/**
|
|
27
33
|
* Validate a certificate chain.
|
|
28
34
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Api.mjs","names":["X509Api","agentContext: AgentContext","config: X509ModuleConfig"],"sources":["../../../src/modules/x509/X509Api.ts"],"sourcesContent":["import { AgentContext } from '../../agent'\nimport { injectable } from '../../plugins'\n\nimport { X509ModuleConfig } from './X509ModuleConfig'\nimport { X509Service } from './X509Service'\nimport type {
|
|
1
|
+
{"version":3,"file":"X509Api.mjs","names":["X509Api","agentContext: AgentContext","config: X509ModuleConfig"],"sources":["../../../src/modules/x509/X509Api.ts"],"sourcesContent":["import { AgentContext } from '../../agent'\nimport { injectable } from '../../plugins'\n\nimport { X509ModuleConfig } from './X509ModuleConfig'\nimport { X509Service } from './X509Service'\nimport type {\n X509CreateCertificateOptions,\n X509CreateCertificateSigningRequestOptions,\n X509ParseCertificateSigningRequestOptions,\n X509ValidateCertificateChainOptions,\n} from './X509ServiceOptions'\n\n/**\n * @public\n */\n@injectable()\nexport class X509Api {\n public constructor(\n private agentContext: AgentContext,\n public config: X509ModuleConfig\n ) {}\n\n /**\n * Creates a X.509 certificate.\n *\n * @param options X509CreateCertificateOptions\n */\n public async createCertificate(options: X509CreateCertificateOptions) {\n return await X509Service.createCertificate(this.agentContext, options)\n }\n\n public async createCertificateSigningRequest(options: X509CreateCertificateSigningRequestOptions) {\n return await X509Service.createCertificateSigningRequest(this.agentContext, options)\n }\n\n public parseCertificateSigningRequest(options: X509ParseCertificateSigningRequestOptions) {\n return X509Service.parseCertificateSigningRequest(options)\n }\n\n /**\n * Validate a certificate chain.\n *\n * @param options X509ValidateCertificateChainOptions\n */\n public async validateCertificateChain(options: X509ValidateCertificateChainOptions) {\n return await X509Service.validateCertificateChain(this.agentContext, options)\n }\n}\n"],"mappings":";;;;;;;;;;;;AAgBO,oBAAMA,UAAQ;CACnB,AAAO,YACL,AAAQC,cACR,AAAOC,QACP;EAFQ;EACD;;;;;;;CAQT,MAAa,kBAAkB,SAAuC;AACpE,SAAO,MAAM,YAAY,kBAAkB,KAAK,cAAc,QAAQ;;CAGxE,MAAa,gCAAgC,SAAqD;AAChG,SAAO,MAAM,YAAY,gCAAgC,KAAK,cAAc,QAAQ;;CAGtF,AAAO,+BAA+B,SAAoD;AACxF,SAAO,YAAY,+BAA+B,QAAQ;;;;;;;CAQ5D,MAAa,yBAAyB,SAA8C;AAClF,SAAO,MAAM,YAAY,yBAAyB,KAAK,cAAc,QAAQ;;;sBA9BhF,YAAY"}
|
|
@@ -62,7 +62,7 @@ declare class X509Certificate {
|
|
|
62
62
|
get authorityKeyIdentifier(): string | undefined;
|
|
63
63
|
get subjectKeyIdentifier(): string | undefined;
|
|
64
64
|
get keyUsage(): X509KeyUsage[] | undefined;
|
|
65
|
-
get extendedKeyUsage(): X509ExtendedKeyUsage
|
|
65
|
+
get extendedKeyUsage(): X509ExtendedKeyUsage[];
|
|
66
66
|
isExtensionCritical(id: string): boolean;
|
|
67
67
|
static create(options: X509CreateCertificateOptions, webCrypto: CredoWebCrypto): Promise<X509Certificate>;
|
|
68
68
|
get subject(): string;
|
|
@@ -79,7 +79,7 @@ declare class X509Certificate {
|
|
|
79
79
|
* as whether the certificate is not expired).
|
|
80
80
|
*
|
|
81
81
|
* This can be useful when an non-self-signed certificate is directly trusted, and it may
|
|
82
|
-
* not be possible to verify the
|
|
82
|
+
* not be possible to verify the certificate as the root/intermediate certificate containing
|
|
83
83
|
* the key of the signer/intermediate is not present.
|
|
84
84
|
*
|
|
85
85
|
* @default false
|
|
@@ -87,9 +87,9 @@ declare class X509Certificate {
|
|
|
87
87
|
skipSignatureVerification?: boolean;
|
|
88
88
|
}, webCrypto: CredoWebCrypto): Promise<void>;
|
|
89
89
|
/**
|
|
90
|
-
* Get the
|
|
90
|
+
* Get the thumbprint of the X509 certificate in hex format.
|
|
91
91
|
*/
|
|
92
|
-
|
|
92
|
+
getThumbprintInHex(agentContext: AgentContext): Promise<string>;
|
|
93
93
|
/**
|
|
94
94
|
* Get the data elements of the x509 certificate
|
|
95
95
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Certificate.d.mts","names":[],"sources":["../../../src/modules/x509/X509Certificate.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;aA+BY,YAAA;;;;;;;EAAA,OAAA,GAAA,EAAA;EAYA,YAAA,GAAA,GAAA;EAUA,YAAA,GAAA,GAAA;;AAEG,aAZH,oBAAA;EAaO,UAAK,GAAA,mBAAA;EAAe,UAAA,GAAA,mBAAA;EAG1B,WAAA,GAAA,mBAAe;EACR,eAAA,GAAA,mBAAA;EACE,YAAA,GAAA,mBAAA;EAqB6B,WAAA,GAAA,mBAAA;EAAgB,KAAA,GAAA,iBAAA;;AA2BxC,KAxDf,sBAAA,GAwDe;EAAA,SAAA,EAvDd,SAuDc;eAtDZ;mBACI,IAAA,CAAK;CA4GH;
|
|
1
|
+
{"version":3,"file":"X509Certificate.d.mts","names":[],"sources":["../../../src/modules/x509/X509Certificate.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;aA+BY,YAAA;;;;;;;EAAA,OAAA,GAAA,EAAA;EAYA,YAAA,GAAA,GAAA;EAUA,YAAA,GAAA,GAAA;;AAEG,aAZH,oBAAA;EAaO,UAAK,GAAA,mBAAA;EAAe,UAAA,GAAA,mBAAA;EAG1B,WAAA,GAAA,mBAAe;EACR,eAAA,GAAA,mBAAA;EACE,YAAA,GAAA,mBAAA;EAqB6B,WAAA,GAAA,mBAAA;EAAgB,KAAA,GAAA,iBAAA;;AA2BxC,KAxDf,sBAAA,GAwDe;EAAA,SAAA,EAvDd,SAuDc;eAtDZ;mBACI,IAAA,CAAK;CA4GH;AAeQ,cAxHhB,eAAA,CAwHgB;EAqBS,SAAA,EA5IlB,SA4IkB;EAAyC,UAAA,CAAA,EA3IzD,aA2IyD;EAAc,QAAA,eAAA;EAAA,QAAA,WAAA,CAAA;EA6FvF,IAAA,KAAA,CAAA,KAAA,EAAA,MAAA;EACA,IAAA,KAAA,CAAA,CAAA,EAAA,MAAA;EACA,IAAA,QAAA,CAAA,CAAA,EAAA,OAAA;EAEkB,OAAA,kBAAA,CAAA,cAAA,EAvN2B,aAuN3B,CAAA,EAvN2C,eAuN3C;EACN,OAAA,sBAAA,CAAA,kBAAA,EAAA,MAAA,CAAA,EAnNkD,eAmNlD;EAcH,eAAA,gBAAA;EAAc,QAAA,qBAAA;EAiCmB,IAAA,cAAA,CAAA,CAAA,EA5OrB,UA4OqB,CA5OrB,WA4OqB,CAAA;EAAY,IAAA,uBAAA,CAAA,CAAA,EAAA;;;EAkChC,CAAA,EAAA;EAAe,IAAA,sBAAA,CAAA,CAAA,EAAA;;;;;;;;;;kBAvNtB;0BAeQ;;yBAqBS,yCAAyC,iBAAc,QAAA;;;;;;;;sBAiGrE;gBACN;;;;;;;;;;;;gBAcH,iBAAc;;;;mCAiCmB,eAAY;;;;;;;;;;;;;;;;;;;qBAkChC"}
|
|
@@ -120,7 +120,7 @@ var X509Certificate = class X509Certificate {
|
|
|
120
120
|
get extendedKeyUsage() {
|
|
121
121
|
const extendedKeyUsages = this.getMatchingExtensions(id_ce_extKeyUsage)?.map((e) => e.usages);
|
|
122
122
|
if (extendedKeyUsages && extendedKeyUsages.length > 1) throw new X509Error("Multiple Key Usages are not allowed");
|
|
123
|
-
return extendedKeyUsages?.[0];
|
|
123
|
+
return extendedKeyUsages?.[0] ?? [];
|
|
124
124
|
}
|
|
125
125
|
isExtensionCritical(id) {
|
|
126
126
|
const extension = this.getMatchingExtensions(id);
|
|
@@ -195,9 +195,9 @@ var X509Certificate = class X509Certificate {
|
|
|
195
195
|
if (!isNotAfterValid) throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used after it is allowed`);
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
* Get the
|
|
198
|
+
* Get the thumbprint of the X509 certificate in hex format.
|
|
199
199
|
*/
|
|
200
|
-
async
|
|
200
|
+
async getThumbprintInHex(agentContext) {
|
|
201
201
|
const thumbprint = await this.x509Certificate.getThumbprint(new CredoWebCrypto(agentContext));
|
|
202
202
|
return TypedArrayEncoder.toHex(new Uint8Array(thumbprint));
|
|
203
203
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Certificate.mjs","names":["extensions: Array<x509.Extension | undefined>","certificate","certificateInstance","publicCryptoKey: CredoWebCryptoKey | undefined"],"sources":["../../../src/modules/x509/X509Certificate.ts"],"sourcesContent":["import { AsnParser } from '@peculiar/asn1-schema'\nimport {\n id_ce_authorityKeyIdentifier,\n id_ce_extKeyUsage,\n id_ce_issuerAltName,\n id_ce_keyUsage,\n id_ce_subjectAltName,\n id_ce_subjectKeyIdentifier,\n SubjectPublicKeyInfo,\n} from '@peculiar/asn1-x509'\nimport * as x509 from '@peculiar/x509'\nimport type { AgentContext } from '../../agent'\nimport { CredoWebCrypto, CredoWebCryptoKey } from '../../crypto/webcrypto'\nimport { publicJwkToCryptoKeyAlgorithm, spkiToPublicJwk } from '../../crypto/webcrypto/utils'\nimport type { AnyUint8Array } from '../../types'\nimport { TypedArrayEncoder } from '../../utils'\nimport { asymmetricPublicJwkMatches, PublicJwk } from '../kms'\nimport {\n convertName,\n createAuthorityKeyIdentifierExtension,\n createBasicConstraintsExtension,\n createCrlDistributionPointsExtension,\n createExtendedKeyUsagesExtension,\n createIssuerAlternativeNameExtension,\n createKeyUsagesExtension,\n createSubjectAlternativeNameExtension,\n createSubjectKeyIdentifierExtension,\n} from './utils'\nimport { X509Error } from './X509Error'\nimport type { X509CreateCertificateOptions } from './X509ServiceOptions'\n\nexport enum X509KeyUsage {\n DigitalSignature = 1,\n NonRepudiation = 2,\n KeyEncipherment = 4,\n DataEncipherment = 8,\n KeyAgreement = 16,\n KeyCertSign = 32,\n CrlSign = 64,\n EncipherOnly = 128,\n DecipherOnly = 256,\n}\n\nexport enum X509ExtendedKeyUsage {\n ServerAuth = '1.3.6.1.5.5.7.3.1',\n ClientAuth = '1.3.6.1.5.5.7.3.2',\n CodeSigning = '1.3.6.1.5.5.7.3.3',\n EmailProtection = '1.3.6.1.5.5.7.3.4',\n TimeStamping = '1.3.6.1.5.5.7.3.8',\n OcspSigning = '1.3.6.1.5.5.7.3.9',\n MdlDs = '1.0.18013.5.1.2',\n}\n\nexport type X509CertificateOptions = {\n publicJwk: PublicJwk\n privateKey?: AnyUint8Array\n x509Certificate: x509.X509Certificate\n}\n\nexport class X509Certificate {\n public publicJwk: PublicJwk\n public privateKey?: AnyUint8Array\n private x509Certificate: x509.X509Certificate\n\n private constructor(options: X509CertificateOptions) {\n this.publicJwk = options.publicJwk\n this.privateKey = options.privateKey\n this.x509Certificate = options.x509Certificate\n }\n\n public set keyId(keyId: string) {\n this.publicJwk.keyId = keyId\n }\n\n public get keyId(): string {\n return this.publicJwk.keyId\n }\n\n public get hasKeyId(): boolean {\n return this.publicJwk.hasKeyId\n }\n\n public static fromRawCertificate(rawCertificate: AnyUint8Array): X509Certificate {\n const certificate = new x509.X509Certificate(rawCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n public static fromEncodedCertificate(encodedCertificate: string): X509Certificate {\n const certificate = new x509.X509Certificate(encodedCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n private static parseCertificate(certificate: x509.X509Certificate): X509Certificate {\n const spki = AsnParser.parse(certificate.publicKey.rawData, SubjectPublicKeyInfo)\n const privateKey = certificate.privateKey ? new Uint8Array(certificate.privateKey.rawData) : undefined\n\n const publicJwk = spkiToPublicJwk(spki)\n\n return new X509Certificate({\n publicJwk,\n privateKey,\n x509Certificate: certificate,\n })\n }\n\n private getMatchingExtensions<T = { critical: boolean }>(objectIdentifier: string): Array<T> | undefined {\n return this.x509Certificate.extensions.filter((e) => e.type === objectIdentifier) as Array<T> | undefined\n }\n\n public get rawCertificate() {\n return new Uint8Array(this.x509Certificate.rawData)\n }\n\n public get subjectAlternativeNames() {\n const san = this.getMatchingExtensions<x509.SubjectAlternativeNameExtension>(id_ce_subjectAltName)\n return san?.flatMap((s) => s.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get issuerAlternativeNames() {\n const ian = this.getMatchingExtensions<x509.IssuerAlternativeNameExtension>(id_ce_issuerAltName)\n return ian?.flatMap((i) => i.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get sanDnsNames() {\n return this.subjectAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get sanUriNames() {\n return this.subjectAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get ianDnsNames() {\n return this.issuerAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get ianUriNames() {\n return this.issuerAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get authorityKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.AuthorityKeyIdentifierExtension>(id_ce_authorityKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Authority Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n public get subjectKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.SubjectKeyIdentifierExtension>(id_ce_subjectKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Subject Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n // biome-ignore lint/suspicious/useGetterReturn: no explanation\n public get keyUsage() {\n const keyUsages = this.getMatchingExtensions<x509.KeyUsagesExtension>(id_ce_keyUsage)?.map((e) => e.usages)\n\n if (keyUsages && keyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n if (keyUsages) {\n return Object.values(X509KeyUsage)\n .filter((key): key is number => typeof key === 'number')\n .filter((flagValue) => (keyUsages[0] & flagValue) === flagValue)\n .map((flagValue) => flagValue as X509KeyUsage)\n }\n }\n\n public get extendedKeyUsage() {\n const extendedKeyUsages = this.getMatchingExtensions<x509.ExtendedKeyUsageExtension>(id_ce_extKeyUsage)?.map(\n (e) => e.usages\n )\n\n if (extendedKeyUsages && extendedKeyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n return extendedKeyUsages?.[0] as X509ExtendedKeyUsage | undefined\n }\n\n public isExtensionCritical(id: string): boolean {\n const extension = this.getMatchingExtensions(id)\n if (!extension) {\n throw new X509Error(`extension with id '${id}' is not found`)\n }\n\n return !!extension[0].critical\n }\n\n public static async create(options: X509CreateCertificateOptions, webCrypto: CredoWebCrypto) {\n const subjectPublicKey = options.subjectPublicKey ?? options.authorityKey\n const isSelfSignedCertificate = asymmetricPublicJwkMatches(options.authorityKey.toJson(), subjectPublicKey.toJson())\n\n const signingKey = new CredoWebCryptoKey(\n options.authorityKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n false,\n 'private',\n ['sign']\n )\n const publicKey = new CredoWebCryptoKey(\n subjectPublicKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n true,\n 'public',\n ['verify']\n )\n\n const issuerName = convertName(options.issuer)\n\n const extensions: Array<x509.Extension | undefined> = []\n extensions.push(\n createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, { publicJwk: subjectPublicKey })\n )\n extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage))\n extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage))\n extensions.push(\n createAuthorityKeyIdentifierExtension(options.extensions?.authorityKeyIdentifier, {\n publicJwk: options.authorityKey,\n })\n )\n extensions.push(createIssuerAlternativeNameExtension(options.extensions?.issuerAlternativeName))\n extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName))\n extensions.push(createBasicConstraintsExtension(options.extensions?.basicConstraints))\n extensions.push(createCrlDistributionPointsExtension(options.extensions?.crlDistributionPoints))\n\n if (isSelfSignedCertificate) {\n if (options.subject) {\n throw new X509Error('Do not provide a subject name when the certificate is supposed to be self signed')\n }\n\n const certificate = await x509.X509CertificateGenerator.createSelfSigned(\n {\n keys: { publicKey, privateKey: signingKey },\n name: issuerName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n serialNumber: options.serialNumber,\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n if (!options.subject) {\n throw new X509Error('Provide a subject name when the certificate is not supposed to be self signed')\n }\n\n const subjectName = convertName(options.subject)\n\n const certificate = await x509.X509CertificateGenerator.create(\n {\n signingKey,\n publicKey,\n issuer: issuerName,\n subject: subjectName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n public get subject() {\n return this.x509Certificate.subject\n }\n\n public get issuer() {\n return this.x509Certificate.issuer\n }\n\n public async verify(\n {\n verificationDate = new Date(),\n publicJwk,\n skipSignatureVerification = false,\n }: {\n verificationDate: Date\n publicJwk?: PublicJwk\n\n /**\n * Whether to skip the verification of the signature and only perform other checks (such\n * as whether the certificate is not expired).\n *\n * This can be useful when an non-self-signed certificate is directly trusted, and it may\n * not be possible to verify the certifcate as the root/intermediate certificate containing\n * the key of the signer/intermediate is not present.\n *\n * @default false\n */\n skipSignatureVerification?: boolean\n },\n webCrypto: CredoWebCrypto\n ) {\n let publicCryptoKey: CredoWebCryptoKey | undefined\n if (publicJwk) {\n const cryptoKeyAlgorithm = publicJwkToCryptoKeyAlgorithm(publicJwk)\n publicCryptoKey = new CredoWebCryptoKey(publicJwk, cryptoKeyAlgorithm, true, 'public', ['verify'])\n }\n\n // We use the library to validate the signature, but the date is manually verified\n const isSignatureValid = skipSignatureVerification\n ? true\n : await this.x509Certificate.verify({ signatureOnly: true, publicKey: publicCryptoKey }, webCrypto)\n const time = verificationDate.getTime()\n\n const isNotBeforeValid = this.x509Certificate.notBefore.getTime() <= time\n const isNotAfterValid = time <= this.x509Certificate.notAfter.getTime()\n\n if (!isSignatureValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' has an invalid signature`)\n }\n\n if (!isNotBeforeValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used before it is allowed`)\n }\n\n if (!isNotAfterValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used after it is allowed`)\n }\n }\n\n /**\n * Get the thumprint of the X509 certificate in hex format.\n */\n public async getThumprintInHex(agentContext: AgentContext) {\n const thumbprint = await this.x509Certificate.getThumbprint(new CredoWebCrypto(agentContext))\n const thumbprintHex = TypedArrayEncoder.toHex(new Uint8Array(thumbprint))\n\n return thumbprintHex\n }\n\n /**\n * Get the data elements of the x509 certificate\n */\n public get data() {\n return {\n issuerName: this.x509Certificate.issuerName.toString(),\n issuer: this.x509Certificate.issuer,\n subjectName: this.x509Certificate.subjectName.toString(),\n subject: this.x509Certificate.subject,\n serialNumber: this.x509Certificate.serialNumber,\n pem: this.x509Certificate.toString(),\n notBefore: this.x509Certificate.notBefore,\n notAfter: this.x509Certificate.notAfter,\n }\n }\n\n public getIssuerNameField(field: string) {\n return this.x509Certificate.issuerName.getField(field)\n }\n\n /**\n * @param format the format to export to, defaults to `pem`\n */\n public toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url') {\n return this.x509Certificate.toString(format ?? 'pem')\n }\n\n public equal(certificate: X509Certificate) {\n const parsedOther = new x509.X509Certificate(certificate.rawCertificate)\n\n return this.x509Certificate.equal(parsedOther)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"X509Certificate.mjs","names":["extensions: Array<x509.Extension | undefined>","certificate","certificateInstance","publicCryptoKey: CredoWebCryptoKey | undefined"],"sources":["../../../src/modules/x509/X509Certificate.ts"],"sourcesContent":["import { AsnParser } from '@peculiar/asn1-schema'\nimport {\n id_ce_authorityKeyIdentifier,\n id_ce_extKeyUsage,\n id_ce_issuerAltName,\n id_ce_keyUsage,\n id_ce_subjectAltName,\n id_ce_subjectKeyIdentifier,\n SubjectPublicKeyInfo,\n} from '@peculiar/asn1-x509'\nimport * as x509 from '@peculiar/x509'\nimport type { AgentContext } from '../../agent'\nimport { CredoWebCrypto, CredoWebCryptoKey } from '../../crypto/webcrypto'\nimport { publicJwkToCryptoKeyAlgorithm, spkiToPublicJwk } from '../../crypto/webcrypto/utils'\nimport type { AnyUint8Array } from '../../types'\nimport { TypedArrayEncoder } from '../../utils'\nimport { asymmetricPublicJwkMatches, PublicJwk } from '../kms'\nimport {\n convertName,\n createAuthorityKeyIdentifierExtension,\n createBasicConstraintsExtension,\n createCrlDistributionPointsExtension,\n createExtendedKeyUsagesExtension,\n createIssuerAlternativeNameExtension,\n createKeyUsagesExtension,\n createSubjectAlternativeNameExtension,\n createSubjectKeyIdentifierExtension,\n} from './utils'\nimport { X509Error } from './X509Error'\nimport type { X509CreateCertificateOptions } from './X509ServiceOptions'\n\nexport enum X509KeyUsage {\n DigitalSignature = 1,\n NonRepudiation = 2,\n KeyEncipherment = 4,\n DataEncipherment = 8,\n KeyAgreement = 16,\n KeyCertSign = 32,\n CrlSign = 64,\n EncipherOnly = 128,\n DecipherOnly = 256,\n}\n\nexport enum X509ExtendedKeyUsage {\n ServerAuth = '1.3.6.1.5.5.7.3.1',\n ClientAuth = '1.3.6.1.5.5.7.3.2',\n CodeSigning = '1.3.6.1.5.5.7.3.3',\n EmailProtection = '1.3.6.1.5.5.7.3.4',\n TimeStamping = '1.3.6.1.5.5.7.3.8',\n OcspSigning = '1.3.6.1.5.5.7.3.9',\n MdlDs = '1.0.18013.5.1.2',\n}\n\nexport type X509CertificateOptions = {\n publicJwk: PublicJwk\n privateKey?: AnyUint8Array\n x509Certificate: x509.X509Certificate\n}\n\nexport class X509Certificate {\n public publicJwk: PublicJwk\n public privateKey?: AnyUint8Array\n private x509Certificate: x509.X509Certificate\n\n private constructor(options: X509CertificateOptions) {\n this.publicJwk = options.publicJwk\n this.privateKey = options.privateKey\n this.x509Certificate = options.x509Certificate\n }\n\n public set keyId(keyId: string) {\n this.publicJwk.keyId = keyId\n }\n\n public get keyId(): string {\n return this.publicJwk.keyId\n }\n\n public get hasKeyId(): boolean {\n return this.publicJwk.hasKeyId\n }\n\n public static fromRawCertificate(rawCertificate: AnyUint8Array): X509Certificate {\n const certificate = new x509.X509Certificate(rawCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n public static fromEncodedCertificate(encodedCertificate: string): X509Certificate {\n const certificate = new x509.X509Certificate(encodedCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n private static parseCertificate(certificate: x509.X509Certificate): X509Certificate {\n const spki = AsnParser.parse(certificate.publicKey.rawData, SubjectPublicKeyInfo)\n const privateKey = certificate.privateKey ? new Uint8Array(certificate.privateKey.rawData) : undefined\n\n const publicJwk = spkiToPublicJwk(spki)\n\n return new X509Certificate({\n publicJwk,\n privateKey,\n x509Certificate: certificate,\n })\n }\n\n private getMatchingExtensions<T = { critical: boolean }>(objectIdentifier: string): Array<T> | undefined {\n return this.x509Certificate.extensions.filter((e) => e.type === objectIdentifier) as Array<T> | undefined\n }\n\n public get rawCertificate() {\n return new Uint8Array(this.x509Certificate.rawData)\n }\n\n public get subjectAlternativeNames() {\n const san = this.getMatchingExtensions<x509.SubjectAlternativeNameExtension>(id_ce_subjectAltName)\n return san?.flatMap((s) => s.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get issuerAlternativeNames() {\n const ian = this.getMatchingExtensions<x509.IssuerAlternativeNameExtension>(id_ce_issuerAltName)\n return ian?.flatMap((i) => i.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get sanDnsNames() {\n return this.subjectAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get sanUriNames() {\n return this.subjectAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get ianDnsNames() {\n return this.issuerAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get ianUriNames() {\n return this.issuerAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get authorityKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.AuthorityKeyIdentifierExtension>(id_ce_authorityKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Authority Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n public get subjectKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.SubjectKeyIdentifierExtension>(id_ce_subjectKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Subject Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n // biome-ignore lint/suspicious/useGetterReturn: no explanation\n public get keyUsage() {\n const keyUsages = this.getMatchingExtensions<x509.KeyUsagesExtension>(id_ce_keyUsage)?.map((e) => e.usages)\n\n if (keyUsages && keyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n if (keyUsages) {\n return Object.values(X509KeyUsage)\n .filter((key): key is number => typeof key === 'number')\n .filter((flagValue) => (keyUsages[0] & flagValue) === flagValue)\n .map((flagValue) => flagValue as X509KeyUsage)\n }\n }\n\n public get extendedKeyUsage() {\n const extendedKeyUsages = this.getMatchingExtensions<x509.ExtendedKeyUsageExtension>(id_ce_extKeyUsage)?.map(\n (e) => e.usages\n )\n\n if (extendedKeyUsages && extendedKeyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n return (extendedKeyUsages?.[0] as Array<X509ExtendedKeyUsage> | undefined) ?? []\n }\n\n public isExtensionCritical(id: string): boolean {\n const extension = this.getMatchingExtensions(id)\n if (!extension) {\n throw new X509Error(`extension with id '${id}' is not found`)\n }\n\n return !!extension[0].critical\n }\n\n public static async create(options: X509CreateCertificateOptions, webCrypto: CredoWebCrypto) {\n const subjectPublicKey = options.subjectPublicKey ?? options.authorityKey\n const isSelfSignedCertificate = asymmetricPublicJwkMatches(options.authorityKey.toJson(), subjectPublicKey.toJson())\n\n const signingKey = new CredoWebCryptoKey(\n options.authorityKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n false,\n 'private',\n ['sign']\n )\n const publicKey = new CredoWebCryptoKey(\n subjectPublicKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n true,\n 'public',\n ['verify']\n )\n\n const issuerName = convertName(options.issuer)\n\n const extensions: Array<x509.Extension | undefined> = []\n extensions.push(\n createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, { publicJwk: subjectPublicKey })\n )\n extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage))\n extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage))\n extensions.push(\n createAuthorityKeyIdentifierExtension(options.extensions?.authorityKeyIdentifier, {\n publicJwk: options.authorityKey,\n })\n )\n extensions.push(createIssuerAlternativeNameExtension(options.extensions?.issuerAlternativeName))\n extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName))\n extensions.push(createBasicConstraintsExtension(options.extensions?.basicConstraints))\n extensions.push(createCrlDistributionPointsExtension(options.extensions?.crlDistributionPoints))\n\n if (isSelfSignedCertificate) {\n if (options.subject) {\n throw new X509Error('Do not provide a subject name when the certificate is supposed to be self signed')\n }\n\n const certificate = await x509.X509CertificateGenerator.createSelfSigned(\n {\n keys: { publicKey, privateKey: signingKey },\n name: issuerName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n serialNumber: options.serialNumber,\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n if (!options.subject) {\n throw new X509Error('Provide a subject name when the certificate is not supposed to be self signed')\n }\n\n const subjectName = convertName(options.subject)\n\n const certificate = await x509.X509CertificateGenerator.create(\n {\n signingKey,\n publicKey,\n issuer: issuerName,\n subject: subjectName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n public get subject() {\n return this.x509Certificate.subject\n }\n\n public get issuer() {\n return this.x509Certificate.issuer\n }\n\n public async verify(\n {\n verificationDate = new Date(),\n publicJwk,\n skipSignatureVerification = false,\n }: {\n verificationDate: Date\n publicJwk?: PublicJwk\n\n /**\n * Whether to skip the verification of the signature and only perform other checks (such\n * as whether the certificate is not expired).\n *\n * This can be useful when an non-self-signed certificate is directly trusted, and it may\n * not be possible to verify the certificate as the root/intermediate certificate containing\n * the key of the signer/intermediate is not present.\n *\n * @default false\n */\n skipSignatureVerification?: boolean\n },\n webCrypto: CredoWebCrypto\n ) {\n let publicCryptoKey: CredoWebCryptoKey | undefined\n if (publicJwk) {\n const cryptoKeyAlgorithm = publicJwkToCryptoKeyAlgorithm(publicJwk)\n publicCryptoKey = new CredoWebCryptoKey(publicJwk, cryptoKeyAlgorithm, true, 'public', ['verify'])\n }\n\n // We use the library to validate the signature, but the date is manually verified\n const isSignatureValid = skipSignatureVerification\n ? true\n : await this.x509Certificate.verify({ signatureOnly: true, publicKey: publicCryptoKey }, webCrypto)\n const time = verificationDate.getTime()\n\n const isNotBeforeValid = this.x509Certificate.notBefore.getTime() <= time\n const isNotAfterValid = time <= this.x509Certificate.notAfter.getTime()\n\n if (!isSignatureValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' has an invalid signature`)\n }\n\n if (!isNotBeforeValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used before it is allowed`)\n }\n\n if (!isNotAfterValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used after it is allowed`)\n }\n }\n\n /**\n * Get the thumbprint of the X509 certificate in hex format.\n */\n public async getThumbprintInHex(agentContext: AgentContext) {\n const thumbprint = await this.x509Certificate.getThumbprint(new CredoWebCrypto(agentContext))\n const thumbprintHex = TypedArrayEncoder.toHex(new Uint8Array(thumbprint))\n\n return thumbprintHex\n }\n\n /**\n * Get the data elements of the x509 certificate\n */\n public get data() {\n return {\n issuerName: this.x509Certificate.issuerName.toString(),\n issuer: this.x509Certificate.issuer,\n subjectName: this.x509Certificate.subjectName.toString(),\n subject: this.x509Certificate.subject,\n serialNumber: this.x509Certificate.serialNumber,\n pem: this.x509Certificate.toString(),\n notBefore: this.x509Certificate.notBefore,\n notAfter: this.x509Certificate.notAfter,\n }\n }\n\n public getIssuerNameField(field: string) {\n return this.x509Certificate.issuerName.getField(field)\n }\n\n /**\n * @param format the format to export to, defaults to `pem`\n */\n public toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url') {\n return this.x509Certificate.toString(format ?? 'pem')\n }\n\n public equal(certificate: X509Certificate) {\n const parsedOther = new x509.X509Certificate(certificate.rawCertificate)\n\n return this.x509Certificate.equal(parsedOther)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { X509CreateCertificateOptions, X509GetLeafCertificateOptions, X509ParseCertificateOptions, X509ValidateCertificateChainOptions } from "./X509ServiceOptions.mjs";
|
|
1
|
+
import { X509CreateCertificateOptions, X509CreateCertificateSigningRequestOptions, X509GetLeafCertificateOptions, X509ParseCertificateOptions, X509ParseCertificateSigningRequestOptions, X509ValidateCertificateChainOptions } from "./X509ServiceOptions.mjs";
|
|
2
2
|
import { X509Certificate } from "./X509Certificate.mjs";
|
|
3
|
+
import { CertificateSigningRequest } from "./CertificateSigningRequest.mjs";
|
|
3
4
|
import { AgentContext } from "../../agent/context/AgentContext.mjs";
|
|
4
5
|
import "../../agent/index.mjs";
|
|
5
6
|
|
|
@@ -16,11 +17,11 @@ declare class X509Service {
|
|
|
16
17
|
*
|
|
17
18
|
* The Issuer of the certificate is found with the following algorithm:
|
|
18
19
|
* - Check if there is an AuthorityKeyIdentifierExtension
|
|
19
|
-
* - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to
|
|
20
|
+
* - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to the AuthorityKeyIdentifier
|
|
20
21
|
* - If they are equal, the certificate is verified and returned as the issuer
|
|
21
22
|
*
|
|
22
23
|
* Additional validation:
|
|
23
|
-
* - Make sure
|
|
24
|
+
* - Make sure at least a single certificate is in the chain
|
|
24
25
|
* - Check whether a certificate in the chain matches with a trusted certificate
|
|
25
26
|
*/
|
|
26
27
|
static validateCertificateChain(agentContext: AgentContext, {
|
|
@@ -41,6 +42,10 @@ declare class X509Service {
|
|
|
41
42
|
certificateChain
|
|
42
43
|
}: X509GetLeafCertificateOptions): X509Certificate;
|
|
43
44
|
static createCertificate(agentContext: AgentContext, options: X509CreateCertificateOptions): Promise<X509Certificate>;
|
|
45
|
+
static createCertificateSigningRequest(agentContext: AgentContext, options: X509CreateCertificateSigningRequestOptions): Promise<CertificateSigningRequest>;
|
|
46
|
+
static parseCertificateSigningRequest({
|
|
47
|
+
encodedCertificateSigningRequest
|
|
48
|
+
}: X509ParseCertificateSigningRequestOptions): CertificateSigningRequest;
|
|
44
49
|
}
|
|
45
50
|
//#endregion
|
|
46
51
|
export { X509Service };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Service.d.mts","names":[],"sources":["../../../src/modules/x509/X509Service.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"X509Service.d.mts","names":[],"sources":["../../../src/modules/x509/X509Service.ts"],"sourcesContent":[],"mappings":";;;;;;;cAkBa,WAAA;;;;;AAFb;;;;;;;;;;;;;;EA4IM,OAAA,wBAAA,CAAA,YAAA,EAtHY,YAsHZ,EAAA;IAAA,gBAAA;IAAA,WAAA;IAAA,gBAAA;IAAA;EAAA,CAAA,EAhHC,mCAgHD,CAAA,EAhHoC,OAgHpC,CAhHoC,eAgHpC,EAAA,CAAA;EAAoB;;;;;EAS+E,OAAA,gBAAA,CAAA,aAAA,EAnBtF,YAmBsF,EAAA;IAAA;EAAA,CAAA,EAlB7E,2BAkB6E,CAAA,EAjBpG,eAiBoG;EASvF,OAAA,kBAAA,CAAA,aAAA,EAnBC,YAmBD,EAAA;IAAA;EAAA,CAAA,EAlBQ,6BAkBR,CAAA,EAjBb,eAiBa;EACL,OAAA,iBAAA,CAAA,YAAA,EAVyC,YAUzC,EAAA,OAAA,EAVgE,4BAUhE,CAAA,EAV4F,OAU5F,CAV4F,eAU5F,CAAA;EAA0C,OAAA,+BAAA,CAAA,YAAA,EADrC,YACqC,EAAA,OAAA,EAA1C,0CAA0C,CAAA,EAAA,OAAA,CAAA,yBAAA,CAAA;EAAA,OAAA,8BAAA,CAAA;IAAA;EAAA,CAAA,EAWlD,yCAXkD,CAAA,EAWT,yBAXS"}
|
|
@@ -6,6 +6,7 @@ import { CredoWebCrypto } from "../../crypto/webcrypto/CredoWebCrypto.mjs";
|
|
|
6
6
|
import "../../crypto/webcrypto/index.mjs";
|
|
7
7
|
import { X509Error } from "./X509Error.mjs";
|
|
8
8
|
import { X509Certificate } from "./X509Certificate.mjs";
|
|
9
|
+
import { CertificateSigningRequest } from "./CertificateSigningRequest.mjs";
|
|
9
10
|
import { injectable } from "tsyringe";
|
|
10
11
|
import * as x509 from "@peculiar/x509";
|
|
11
12
|
|
|
@@ -22,11 +23,11 @@ let X509Service = class X509Service$1 {
|
|
|
22
23
|
*
|
|
23
24
|
* The Issuer of the certificate is found with the following algorithm:
|
|
24
25
|
* - Check if there is an AuthorityKeyIdentifierExtension
|
|
25
|
-
* - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to
|
|
26
|
+
* - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to the AuthorityKeyIdentifier
|
|
26
27
|
* - If they are equal, the certificate is verified and returned as the issuer
|
|
27
28
|
*
|
|
28
29
|
* Additional validation:
|
|
29
|
-
* - Make sure
|
|
30
|
+
* - Make sure at least a single certificate is in the chain
|
|
30
31
|
* - Check whether a certificate in the chain matches with a trusted certificate
|
|
31
32
|
*/
|
|
32
33
|
static async validateCertificateChain(agentContext, { certificateChain, certificate = certificateChain[0], verificationDate = /* @__PURE__ */ new Date(), trustedCertificates }) {
|
|
@@ -81,6 +82,13 @@ let X509Service = class X509Service$1 {
|
|
|
81
82
|
const webCrypto = new CredoWebCrypto(agentContext);
|
|
82
83
|
return await X509Certificate.create(options, webCrypto);
|
|
83
84
|
}
|
|
85
|
+
static async createCertificateSigningRequest(agentContext, options) {
|
|
86
|
+
const webCrypto = new CredoWebCrypto(agentContext);
|
|
87
|
+
return await CertificateSigningRequest.create(options, webCrypto);
|
|
88
|
+
}
|
|
89
|
+
static parseCertificateSigningRequest({ encodedCertificateSigningRequest }) {
|
|
90
|
+
return CertificateSigningRequest.fromEncodedCertificateRequest(encodedCertificateSigningRequest);
|
|
91
|
+
}
|
|
84
92
|
};
|
|
85
93
|
X509Service = __decorate([injectable()], X509Service);
|
|
86
94
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509Service.mjs","names":["X509Service","parsedLeafCertificate: x509.X509Certificate","certificatesToBuildChain: x509.X509Certificate[]","previousCertificate: X509Certificate | undefined"],"sources":["../../../src/modules/x509/X509Service.ts"],"sourcesContent":["import * as x509 from '@peculiar/x509'\nimport { injectable } from 'tsyringe'\nimport { AgentContext } from '../../agent'\nimport { CredoWebCrypto } from '../../crypto/webcrypto'\nimport { X509Certificate } from './X509Certificate'\nimport { X509Error } from './X509Error'\nimport type {\n X509CreateCertificateOptions,\n X509GetLeafCertificateOptions,\n X509ParseCertificateOptions,\n X509ValidateCertificateChainOptions,\n} from './X509ServiceOptions'\n\n@injectable()\n// biome-ignore lint/complexity/noStaticOnlyClass: no explanation\nexport class X509Service {\n /**\n *\n * Validate a chain of X.509 certificates according to RFC 5280\n *\n * This function requires a list of base64 encoded certificates and, optionally, a certificate that should be found in the chain.\n * If no certificate is provided, it will just assume the leaf certificate\n *\n * The leaf certificate should be the 0th index and the root the last\n *\n * The Issuer of the certificate is found with the following algorithm:\n * - Check if there is an AuthorityKeyIdentifierExtension\n * - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to thje AuthorityKeyIdentifier\n * - If they are equal, the certificate is verified and returned as the issuer\n *\n * Additional validation:\n * - Make sure atleast a single certificate is in the chain\n * - Check whether a certificate in the chain matches with a trusted certificate\n */\n public static async validateCertificateChain(\n agentContext: AgentContext,\n {\n certificateChain,\n certificate = certificateChain[0],\n verificationDate = new Date(),\n trustedCertificates,\n }: X509ValidateCertificateChainOptions\n ) {\n if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')\n const webCrypto = new CredoWebCrypto(agentContext)\n\n let parsedLeafCertificate: x509.X509Certificate\n let certificatesToBuildChain: x509.X509Certificate[]\n try {\n parsedLeafCertificate = new x509.X509Certificate(certificate)\n certificatesToBuildChain = [...certificateChain, ...(trustedCertificates ?? [])].map(\n (c) => new x509.X509Certificate(c)\n )\n } catch (error) {\n throw new X509Error('Error during parsing of x509 certificate', { cause: error })\n }\n\n const certificateChainBuilder = new x509.X509ChainBuilder({\n certificates: certificatesToBuildChain,\n })\n\n const chain = await certificateChainBuilder.build(parsedLeafCertificate, webCrypto)\n\n // The chain is reversed here as the `x5c` header (the expected input),\n // has the leaf certificate as the first entry, while the `x509` library expects this as the last\n let parsedChain = chain.map((c) => X509Certificate.fromRawCertificate(new Uint8Array(c.rawData))).reverse()\n\n // We allow longer parsed chain, in case the root cert was not part of the chain, but in the\n // list of trusted certificates\n if (parsedChain.length < certificateChain.length) {\n throw new X509Error('Could not parse the full chain. Likely due to incorrect ordering')\n }\n\n let previousCertificate: X509Certificate | undefined\n\n if (trustedCertificates) {\n const parsedTrustedCertificates = trustedCertificates.map((trustedCertificate) =>\n X509Certificate.fromEncodedCertificate(trustedCertificate)\n )\n\n const trustedCertificateIndex = parsedChain.findIndex((cert) =>\n parsedTrustedCertificates.some((tCert) => cert.equal(tCert))\n )\n\n if (trustedCertificateIndex === -1) {\n throw new X509Error('No trusted certificate was found while validating the X.509 chain')\n }\n\n if (trustedCertificateIndex > 0) {\n // When we trust a certificate other than the first certificate in the provided chain we keep a reference to the\n // previous certificate as we need the key of this certificate to verify the first certificate in the chain as\n // it's not self-sigend.\n previousCertificate = parsedChain[trustedCertificateIndex - 1]\n\n // Pop everything off before the index of the trusted certificate (those are more root) as it is not relevant for validation\n parsedChain = parsedChain.slice(trustedCertificateIndex)\n }\n }\n\n // Verify the certificate with the publicKey of the certificate above\n for (let i = 0; i < parsedChain.length; i++) {\n const cert = parsedChain[i]\n const publicJwk = previousCertificate ? previousCertificate.publicJwk : undefined\n\n // The only scenario where this will trigger is if the trusted certificates and the x509 chain both do not contain the\n // intermediate/root certificate needed. E.g. for ISO 18013-5 mDL the root cert MUST NOT be in the chain. If the signer\n // certificate is then trusted, it will fail, as we can't verify the signer certifciate without having access to the signer\n // key of the root certificate.\n // See also https://github.com/openid/OpenID4VCI/issues/62\n //\n // In this case we could skip the signature verification (not other verifications), as we already trust the signer certificate,\n // but i think the purpose of ISO 18013-5 mDL is that you trust the root certificate. If we can't verify the whole chain e.g.\n // when we receive a credential we have the chance it will fail later on.\n const skipSignatureVerification = i === 0 && trustedCertificates && !publicJwk\n // NOTE: at some point we might want to change this to throw an error instead of skipping the signature verification of the trusted\n // but it would basically prevent mDOCs from unknown issuers to be verified in the wallet. Verifiers should only trust the root certificate\n // anyway.\n // if (i === 0 && trustedCertificates && cert.issuer !== cert.subject && !publicKey) {\n // throw new X509Error(\n // 'Unable to verify the certificate chain. A non-self-signed certificate is the first certificate in the chain, and no parent certificate was found in the trusted certificates, meaning the first certificate in the chain cannot be verified. Ensure the certificate is added '\n // )\n // }\n\n await cert.verify(\n {\n publicJwk,\n verificationDate,\n skipSignatureVerification,\n },\n webCrypto\n )\n previousCertificate = cert\n }\n\n return parsedChain\n }\n\n /**\n *\n * Parses a base64-encoded X.509 certificate into a {@link X509Certificate}\n *\n */\n public static parseCertificate(\n _agentContext: AgentContext,\n { encodedCertificate }: X509ParseCertificateOptions\n ): X509Certificate {\n const certificate = X509Certificate.fromEncodedCertificate(encodedCertificate)\n\n return certificate\n }\n\n public static getLeafCertificate(\n _agentContext: AgentContext,\n { certificateChain }: X509GetLeafCertificateOptions\n ): X509Certificate {\n if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')\n\n const certificate = X509Certificate.fromEncodedCertificate(certificateChain[0])\n\n return certificate\n }\n\n public static async createCertificate(agentContext: AgentContext, options: X509CreateCertificateOptions) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const certificate = await X509Certificate.create(options, webCrypto)\n\n return certificate\n }\n}\n"],"mappings":";;;;;;;;;;;;AAeO,wBAAMA,cAAY;;;;;;;;;;;;;;;;;;;CAmBvB,aAAoB,yBAClB,cACA,EACE,kBACA,cAAc,iBAAiB,IAC/B,mCAAmB,IAAI,MAAM,EAC7B,uBAEF;AACA,MAAI,iBAAiB,WAAW,EAAG,OAAM,IAAI,UAAU,6BAA6B;EACpF,MAAM,YAAY,IAAI,eAAe,aAAa;EAElD,IAAIC;EACJ,IAAIC;AACJ,MAAI;AACF,2BAAwB,IAAI,KAAK,gBAAgB,YAAY;AAC7D,8BAA2B,CAAC,GAAG,kBAAkB,GAAI,uBAAuB,EAAE,CAAE,CAAC,KAC9E,MAAM,IAAI,KAAK,gBAAgB,EAAE,CACnC;WACM,OAAO;AACd,SAAM,IAAI,UAAU,4CAA4C,EAAE,OAAO,OAAO,CAAC;;EAWnF,IAAI,eAJU,MAJkB,IAAI,KAAK,iBAAiB,EACxD,cAAc,0BACf,CAAC,CAE0C,MAAM,uBAAuB,UAAU,EAI3D,KAAK,MAAM,gBAAgB,mBAAmB,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;AAI3G,MAAI,YAAY,SAAS,iBAAiB,OACxC,OAAM,IAAI,UAAU,mEAAmE;EAGzF,IAAIC;AAEJ,MAAI,qBAAqB;GACvB,MAAM,4BAA4B,oBAAoB,KAAK,uBACzD,gBAAgB,uBAAuB,mBAAmB,CAC3D;GAED,MAAM,0BAA0B,YAAY,WAAW,SACrD,0BAA0B,MAAM,UAAU,KAAK,MAAM,MAAM,CAAC,CAC7D;AAED,OAAI,4BAA4B,GAC9B,OAAM,IAAI,UAAU,oEAAoE;AAG1F,OAAI,0BAA0B,GAAG;AAI/B,0BAAsB,YAAY,0BAA0B;AAG5D,kBAAc,YAAY,MAAM,wBAAwB;;;AAK5D,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;GAC3C,MAAM,OAAO,YAAY;GACzB,MAAM,YAAY,sBAAsB,oBAAoB,YAAY;GAWxE,MAAM,4BAA4B,MAAM,KAAK,uBAAuB,CAAC;AAUrE,SAAM,KAAK,OACT;IACE;IACA;IACA;IACD,EACD,UACD;AACD,yBAAsB;;AAGxB,SAAO;;;;;;;CAQT,OAAc,iBACZ,eACA,EAAE,sBACe;AAGjB,SAFoB,gBAAgB,uBAAuB,mBAAmB;;CAKhF,OAAc,mBACZ,eACA,EAAE,oBACe;AACjB,MAAI,iBAAiB,WAAW,EAAG,OAAM,IAAI,UAAU,6BAA6B;AAIpF,SAFoB,gBAAgB,uBAAuB,iBAAiB,GAAG;;CAKjF,aAAoB,kBAAkB,cAA4B,SAAuC;EACvG,MAAM,YAAY,IAAI,eAAe,aAAa;AAIlD,SAFoB,MAAM,gBAAgB,OAAO,SAAS,UAAU;;;0BAxJvE,YAAY"}
|
|
1
|
+
{"version":3,"file":"X509Service.mjs","names":["X509Service","parsedLeafCertificate: x509.X509Certificate","certificatesToBuildChain: x509.X509Certificate[]","previousCertificate: X509Certificate | undefined"],"sources":["../../../src/modules/x509/X509Service.ts"],"sourcesContent":["import * as x509 from '@peculiar/x509'\nimport { injectable } from 'tsyringe'\nimport { AgentContext } from '../../agent'\nimport { CredoWebCrypto } from '../../crypto/webcrypto'\nimport { CertificateSigningRequest } from './CertificateSigningRequest'\nimport { X509Certificate } from './X509Certificate'\nimport { X509Error } from './X509Error'\nimport type {\n X509CreateCertificateOptions,\n X509CreateCertificateSigningRequestOptions,\n X509GetLeafCertificateOptions,\n X509ParseCertificateOptions,\n X509ParseCertificateSigningRequestOptions,\n X509ValidateCertificateChainOptions,\n} from './X509ServiceOptions'\n\n@injectable()\n// biome-ignore lint/complexity/noStaticOnlyClass: no explanation\nexport class X509Service {\n /**\n *\n * Validate a chain of X.509 certificates according to RFC 5280\n *\n * This function requires a list of base64 encoded certificates and, optionally, a certificate that should be found in the chain.\n * If no certificate is provided, it will just assume the leaf certificate\n *\n * The leaf certificate should be the 0th index and the root the last\n *\n * The Issuer of the certificate is found with the following algorithm:\n * - Check if there is an AuthorityKeyIdentifierExtension\n * - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to the AuthorityKeyIdentifier\n * - If they are equal, the certificate is verified and returned as the issuer\n *\n * Additional validation:\n * - Make sure at least a single certificate is in the chain\n * - Check whether a certificate in the chain matches with a trusted certificate\n */\n public static async validateCertificateChain(\n agentContext: AgentContext,\n {\n certificateChain,\n certificate = certificateChain[0],\n verificationDate = new Date(),\n trustedCertificates,\n }: X509ValidateCertificateChainOptions\n ) {\n if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')\n const webCrypto = new CredoWebCrypto(agentContext)\n\n let parsedLeafCertificate: x509.X509Certificate\n let certificatesToBuildChain: x509.X509Certificate[]\n try {\n parsedLeafCertificate = new x509.X509Certificate(certificate)\n certificatesToBuildChain = [...certificateChain, ...(trustedCertificates ?? [])].map(\n (c) => new x509.X509Certificate(c)\n )\n } catch (error) {\n throw new X509Error('Error during parsing of x509 certificate', { cause: error })\n }\n\n const certificateChainBuilder = new x509.X509ChainBuilder({\n certificates: certificatesToBuildChain,\n })\n\n const chain = await certificateChainBuilder.build(parsedLeafCertificate, webCrypto)\n\n // The chain is reversed here as the `x5c` header (the expected input),\n // has the leaf certificate as the first entry, while the `x509` library expects this as the last\n let parsedChain = chain.map((c) => X509Certificate.fromRawCertificate(new Uint8Array(c.rawData))).reverse()\n\n // We allow longer parsed chain, in case the root cert was not part of the chain, but in the\n // list of trusted certificates\n if (parsedChain.length < certificateChain.length) {\n throw new X509Error('Could not parse the full chain. Likely due to incorrect ordering')\n }\n\n let previousCertificate: X509Certificate | undefined\n\n if (trustedCertificates) {\n const parsedTrustedCertificates = trustedCertificates.map((trustedCertificate) =>\n X509Certificate.fromEncodedCertificate(trustedCertificate)\n )\n\n const trustedCertificateIndex = parsedChain.findIndex((cert) =>\n parsedTrustedCertificates.some((tCert) => cert.equal(tCert))\n )\n\n if (trustedCertificateIndex === -1) {\n throw new X509Error('No trusted certificate was found while validating the X.509 chain')\n }\n\n if (trustedCertificateIndex > 0) {\n // When we trust a certificate other than the first certificate in the provided chain we keep a reference to the\n // previous certificate as we need the key of this certificate to verify the first certificate in the chain as\n // it's not self-sigend.\n previousCertificate = parsedChain[trustedCertificateIndex - 1]\n\n // Pop everything off before the index of the trusted certificate (those are more root) as it is not relevant for validation\n parsedChain = parsedChain.slice(trustedCertificateIndex)\n }\n }\n\n // Verify the certificate with the publicKey of the certificate above\n for (let i = 0; i < parsedChain.length; i++) {\n const cert = parsedChain[i]\n const publicJwk = previousCertificate ? previousCertificate.publicJwk : undefined\n\n // The only scenario where this will trigger is if the trusted certificates and the x509 chain both do not contain the\n // intermediate/root certificate needed. E.g. for ISO 18013-5 mDL the root cert MUST NOT be in the chain. If the signer\n // certificate is then trusted, it will fail, as we can't verify the signer certifciate without having access to the signer\n // key of the root certificate.\n // See also https://github.com/openid/OpenID4VCI/issues/62\n //\n // In this case we could skip the signature verification (not other verifications), as we already trust the signer certificate,\n // but i think the purpose of ISO 18013-5 mDL is that you trust the root certificate. If we can't verify the whole chain e.g.\n // when we receive a credential we have the chance it will fail later on.\n const skipSignatureVerification = i === 0 && trustedCertificates && !publicJwk\n // NOTE: at some point we might want to change this to throw an error instead of skipping the signature verification of the trusted\n // but it would basically prevent mDOCs from unknown issuers to be verified in the wallet. Verifiers should only trust the root certificate\n // anyway.\n // if (i === 0 && trustedCertificates && cert.issuer !== cert.subject && !publicKey) {\n // throw new X509Error(\n // 'Unable to verify the certificate chain. A non-self-signed certificate is the first certificate in the chain, and no parent certificate was found in the trusted certificates, meaning the first certificate in the chain cannot be verified. Ensure the certificate is added '\n // )\n // }\n\n await cert.verify(\n {\n publicJwk,\n verificationDate,\n skipSignatureVerification,\n },\n webCrypto\n )\n previousCertificate = cert\n }\n\n return parsedChain\n }\n\n /**\n *\n * Parses a base64-encoded X.509 certificate into a {@link X509Certificate}\n *\n */\n public static parseCertificate(\n _agentContext: AgentContext,\n { encodedCertificate }: X509ParseCertificateOptions\n ): X509Certificate {\n const certificate = X509Certificate.fromEncodedCertificate(encodedCertificate)\n\n return certificate\n }\n\n public static getLeafCertificate(\n _agentContext: AgentContext,\n { certificateChain }: X509GetLeafCertificateOptions\n ): X509Certificate {\n if (certificateChain.length === 0) throw new X509Error('Certificate chain is empty')\n\n const certificate = X509Certificate.fromEncodedCertificate(certificateChain[0])\n\n return certificate\n }\n\n public static async createCertificate(agentContext: AgentContext, options: X509CreateCertificateOptions) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const certificate = await X509Certificate.create(options, webCrypto)\n\n return certificate\n }\n\n public static async createCertificateSigningRequest(\n agentContext: AgentContext,\n options: X509CreateCertificateSigningRequestOptions\n ) {\n const webCrypto = new CredoWebCrypto(agentContext)\n\n const csr = await CertificateSigningRequest.create(options, webCrypto)\n\n return csr\n }\n\n public static parseCertificateSigningRequest({\n encodedCertificateSigningRequest,\n }: X509ParseCertificateSigningRequestOptions) {\n const csr = CertificateSigningRequest.fromEncodedCertificateRequest(encodedCertificateSigningRequest)\n\n return csr\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBO,wBAAMA,cAAY;;;;;;;;;;;;;;;;;;;CAmBvB,aAAoB,yBAClB,cACA,EACE,kBACA,cAAc,iBAAiB,IAC/B,mCAAmB,IAAI,MAAM,EAC7B,uBAEF;AACA,MAAI,iBAAiB,WAAW,EAAG,OAAM,IAAI,UAAU,6BAA6B;EACpF,MAAM,YAAY,IAAI,eAAe,aAAa;EAElD,IAAIC;EACJ,IAAIC;AACJ,MAAI;AACF,2BAAwB,IAAI,KAAK,gBAAgB,YAAY;AAC7D,8BAA2B,CAAC,GAAG,kBAAkB,GAAI,uBAAuB,EAAE,CAAE,CAAC,KAC9E,MAAM,IAAI,KAAK,gBAAgB,EAAE,CACnC;WACM,OAAO;AACd,SAAM,IAAI,UAAU,4CAA4C,EAAE,OAAO,OAAO,CAAC;;EAWnF,IAAI,eAJU,MAJkB,IAAI,KAAK,iBAAiB,EACxD,cAAc,0BACf,CAAC,CAE0C,MAAM,uBAAuB,UAAU,EAI3D,KAAK,MAAM,gBAAgB,mBAAmB,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS;AAI3G,MAAI,YAAY,SAAS,iBAAiB,OACxC,OAAM,IAAI,UAAU,mEAAmE;EAGzF,IAAIC;AAEJ,MAAI,qBAAqB;GACvB,MAAM,4BAA4B,oBAAoB,KAAK,uBACzD,gBAAgB,uBAAuB,mBAAmB,CAC3D;GAED,MAAM,0BAA0B,YAAY,WAAW,SACrD,0BAA0B,MAAM,UAAU,KAAK,MAAM,MAAM,CAAC,CAC7D;AAED,OAAI,4BAA4B,GAC9B,OAAM,IAAI,UAAU,oEAAoE;AAG1F,OAAI,0BAA0B,GAAG;AAI/B,0BAAsB,YAAY,0BAA0B;AAG5D,kBAAc,YAAY,MAAM,wBAAwB;;;AAK5D,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;GAC3C,MAAM,OAAO,YAAY;GACzB,MAAM,YAAY,sBAAsB,oBAAoB,YAAY;GAWxE,MAAM,4BAA4B,MAAM,KAAK,uBAAuB,CAAC;AAUrE,SAAM,KAAK,OACT;IACE;IACA;IACA;IACD,EACD,UACD;AACD,yBAAsB;;AAGxB,SAAO;;;;;;;CAQT,OAAc,iBACZ,eACA,EAAE,sBACe;AAGjB,SAFoB,gBAAgB,uBAAuB,mBAAmB;;CAKhF,OAAc,mBACZ,eACA,EAAE,oBACe;AACjB,MAAI,iBAAiB,WAAW,EAAG,OAAM,IAAI,UAAU,6BAA6B;AAIpF,SAFoB,gBAAgB,uBAAuB,iBAAiB,GAAG;;CAKjF,aAAoB,kBAAkB,cAA4B,SAAuC;EACvG,MAAM,YAAY,IAAI,eAAe,aAAa;AAIlD,SAFoB,MAAM,gBAAgB,OAAO,SAAS,UAAU;;CAKtE,aAAoB,gCAClB,cACA,SACA;EACA,MAAM,YAAY,IAAI,eAAe,aAAa;AAIlD,SAFY,MAAM,0BAA0B,OAAO,SAAS,UAAU;;CAKxE,OAAc,+BAA+B,EAC3C,oCAC4C;AAG5C,SAFY,0BAA0B,8BAA8B,iCAAiC;;;0BA3KxG,YAAY"}
|
|
@@ -68,6 +68,7 @@ type X509CertificateExtensionsOptions = AddMarkAsCritical<{
|
|
|
68
68
|
urls: Array<string>;
|
|
69
69
|
};
|
|
70
70
|
}>;
|
|
71
|
+
type X509CertificateSigningRequestExtensionsOptions = Pick<X509CertificateExtensionsOptions, 'subjectKeyIdentifier' | 'keyUsage' | 'extendedKeyUsage' | 'subjectAlternativeName'>;
|
|
71
72
|
interface X509CertificateIssuerAndSubjectOptions {
|
|
72
73
|
countryName?: string;
|
|
73
74
|
stateOrProvinceName?: string;
|
|
@@ -137,6 +138,26 @@ interface X509CreateCertificateOptions {
|
|
|
137
138
|
*/
|
|
138
139
|
extensions?: X509CertificateExtensionsOptions;
|
|
139
140
|
}
|
|
141
|
+
interface X509CreateCertificateSigningRequestOptions {
|
|
142
|
+
/**
|
|
143
|
+
* The key that is the subject of the certificate signing request.
|
|
144
|
+
*
|
|
145
|
+
* If you want to influence the specific signature algorithm to use
|
|
146
|
+
* make sure to set the `alg` on the jwk.
|
|
147
|
+
*/
|
|
148
|
+
subjectPublicKey: PublicJwk;
|
|
149
|
+
/**
|
|
150
|
+
* The subject information of the certificate signing request
|
|
151
|
+
*/
|
|
152
|
+
subject: string | X509CertificateIssuerAndSubjectOptions;
|
|
153
|
+
/**
|
|
154
|
+
* X.509 v3 Extensions to be added to the certificate signing request
|
|
155
|
+
*/
|
|
156
|
+
extensions?: X509CertificateSigningRequestExtensionsOptions;
|
|
157
|
+
}
|
|
158
|
+
interface X509ParseCertificateSigningRequestOptions {
|
|
159
|
+
encodedCertificateSigningRequest: string;
|
|
160
|
+
}
|
|
140
161
|
//#endregion
|
|
141
|
-
export { EncodedX509Certificate, X509CertificateExtensionsOptions, X509CertificateIssuerAndSubjectOptions, X509CreateCertificateChainOptions, X509CreateCertificateOptions, X509GetLeafCertificateOptions, X509ParseCertificateOptions, X509ValidateCertificateChainOptions };
|
|
162
|
+
export { EncodedX509Certificate, X509CertificateExtensionsOptions, X509CertificateIssuerAndSubjectOptions, X509CertificateSigningRequestExtensionsOptions, X509CreateCertificateChainOptions, X509CreateCertificateOptions, X509CreateCertificateSigningRequestOptions, X509GetLeafCertificateOptions, X509ParseCertificateOptions, X509ParseCertificateSigningRequestOptions, X509ValidateCertificateChainOptions };
|
|
142
163
|
//# sourceMappingURL=X509ServiceOptions.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"X509ServiceOptions.d.mts","names":[],"sources":["../../../src/modules/x509/X509ServiceOptions.ts"],"sourcesContent":[],"mappings":";;;;;;KAIK,4BAA4B,eAAe,4BAA4B,kBAC9D,IAAI,EAAE;;;AAHwE;;;AAEhB,KAShE,sBAAA,GATgE,MAAA;AAC9D,UAUG,mCAAA,CAVH;EAAI,gBAAA,EAWE,KAXF,CAWQ,sBAXR,CAAA;EAAE,WAAA,CAAA,EAaJ,sBAbI;EAAC;AAQrB;AAEA;;;;;;EAewB,gBAAA,CAAA,EAFH,IAEG;EAAK,mBAAA,CAAA,EAAL,KAAK,CAAC,sBAAD,CAAA;AAG7B;AAIiB,UAJA,6BAAA,CAI2B;EAI3B,gBAAA,EAPG,KAOH,CAAA,MAAA,CAAA;AAKjB;AAKkB,UAdD,2BAAA,CAcC;EAAN,kBAAA,EAAA,MAAA;;AAGA,UAbK,iCAAA,CAaL;EAMY,YAAA,EAlBR,KAkBQ,CAlBF,eAkBE,GAAA,MAAA,CAAA;EAAd,YAAA,CAAA,EAAA,KAAA,GAAA,QAAA;;AAGA,KAjBE,gCAAA,GAAmC,iBAiBrC,CAAA;EAOA,oBAAA,CAAA,EAAA;IAxBqC,OAAA,EAAA,OAAA;EAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"X509ServiceOptions.d.mts","names":[],"sources":["../../../src/modules/x509/X509ServiceOptions.ts"],"sourcesContent":[],"mappings":";;;;;;KAIK,4BAA4B,eAAe,4BAA4B,kBAC9D,IAAI,EAAE;;;AAHwE;;;AAEhB,KAShE,sBAAA,GATgE,MAAA;AAC9D,UAUG,mCAAA,CAVH;EAAI,gBAAA,EAWE,KAXF,CAWQ,sBAXR,CAAA;EAAE,WAAA,CAAA,EAaJ,sBAbI;EAAC;AAQrB;AAEA;;;;;;EAewB,gBAAA,CAAA,EAFH,IAEG;EAAK,mBAAA,CAAA,EAAL,KAAK,CAAC,sBAAD,CAAA;AAG7B;AAIiB,UAJA,6BAAA,CAI2B;EAI3B,gBAAA,EAPG,KAOH,CAAA,MAAA,CAAA;AAKjB;AAKkB,UAdD,2BAAA,CAcC;EAAN,kBAAA,EAAA,MAAA;;AAGA,UAbK,iCAAA,CAaL;EAMY,YAAA,EAlBR,KAkBQ,CAlBF,eAkBE,GAAA,MAAA,CAAA;EAAd,YAAA,CAAA,EAAA,KAAA,GAAA,QAAA;;AAGA,KAjBE,gCAAA,GAAmC,iBAiBrC,CAAA;EAOA,oBAAA,CAAA,EAAA;IAxBqC,OAAA,EAAA,OAAA;EAAiB,CAAA;EA4BpD,QAAA,CAAA,EAAA;IAKK,MAAA,EA5BL,KA4BK,CA5BC,YA4BD,CAAA;EAOA,CAAA;EAaD,gBAAA,CAAA,EAAA;IAUK,MAAA,EAvDT,KAuDS,CAvDH,oBAuDG,CAAA;EAOF,CAAA;EAUE,sBAAA,CAAA,EAAA;IAaL,OAAA,EAAA,OAAA;EAOD,CAAA;EAQA,qBAAA,CAAA,EAAA;IAAgC,IAAA,EA9FrC,KA8FqC,CAAA;MAG9B,IAAA,EAjGO,eAiGP;MAOG,KAAA,EAAA,MAAA;IAKA,CAAA,CAAA;EAKL,CAAA;EAA8C,sBAAA,CAAA,EAAA;IAG5C,IAAA,EAlHP,KAkHO,CAAA;YAlHO;;;;;;;;;UAOd;;;KAIE,8CAAA,GAAiD,KAC3D;UAIe,sCAAA;;;;;;UAOA,4BAAA;;;;;;;;;;;;gBAaD;;;;;;;;;qBAUK;;;;;;mBAOF;;;;;;;;;qBAUE;;;;;;;;;;;;gBAaL;;;;;;eAOD;;;;;;;eAQA;;UAGE,0CAAA;;;;;;;oBAOG;;;;oBAKA;;;;eAKL;;UAGE,yCAAA"}
|