@sphereon/ssi-sdk-ext.x509-utils 0.26.1-next.9 → 0.27.1-feature.SPRIND.113.interop.6

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.
@@ -4,18 +4,10 @@ import { AlgorithmProvider, X509Certificate } from '@peculiar/x509'
4
4
  // import {calculateJwkThumbprint} from "@sphereon/ssi-sdk-ext.key-utils";
5
5
  import { JWK } from '@sphereon/ssi-types'
6
6
  import x509 from 'js-x509-utils'
7
- import {
8
- AltName,
9
- AttributeTypeAndValue,
10
- Certificate,
11
- CertificateChainValidationEngine,
12
- CryptoEngine,
13
- getCrypto,
14
- id_SubjectAltName,
15
- setEngine,
16
- } from 'pkijs'
7
+ import { AltName, AttributeTypeAndValue, Certificate, CryptoEngine, getCrypto, id_SubjectAltName, setEngine } from 'pkijs'
17
8
  import { container } from 'tsyringe'
18
9
  import * as u8a from 'uint8arrays'
10
+ import { globalCrypto } from './crypto'
19
11
  import { areCertificatesEqual, derToPEM, pemOrDerToX509Certificate } from './x509-utils'
20
12
 
21
13
  export type DNInfo = {
@@ -41,6 +33,7 @@ export type X509ValidationResult = {
41
33
  error: boolean
42
34
  critical: boolean
43
35
  message: string
36
+ detailMessage?: string
44
37
  verificationTime: Date
45
38
  certificateChain?: Array<CertificateInfo>
46
39
  trustAnchor?: CertificateInfo
@@ -52,23 +45,9 @@ export type X509ValidationResult = {
52
45
  }
53
46
 
54
47
  const defaultCryptoEngine = () => {
55
- if (typeof self !== 'undefined') {
56
- if ('crypto' in self) {
57
- let engineName = 'webcrypto'
58
- if ('webkitSubtle' in self.crypto) {
59
- engineName = 'safari'
60
- }
61
- setEngine(engineName, new CryptoEngine({ name: engineName, crypto: crypto }))
62
- }
63
- } else if (typeof crypto !== 'undefined' && 'webcrypto' in crypto) {
64
- const name = 'NodeJS ^15'
65
- const nodeCrypto = crypto.webcrypto
66
- // @ts-ignore
67
- setEngine(name, new CryptoEngine({ name, crypto: nodeCrypto }))
68
- } else if (typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined') {
69
- const name = 'crypto'
70
- setEngine(name, new CryptoEngine({ name, crypto: crypto }))
71
- }
48
+ const name = 'crypto'
49
+ setEngine(name, new CryptoEngine({ name, crypto: globalCrypto(false) }))
50
+ return getCrypto(true)
72
51
  }
73
52
 
74
53
  export const getCertificateInfo = async (
@@ -77,14 +56,17 @@ export const getCertificateInfo = async (
77
56
  sanTypeFilter: SubjectAlternativeGeneralName | SubjectAlternativeGeneralName[]
78
57
  }
79
58
  ): Promise<CertificateInfo> => {
80
- const publicKeyJWK = await getCertificateSubjectPublicKeyJWK(certificate)
59
+ let publicKeyJWK: JWK | undefined
60
+ try {
61
+ publicKeyJWK = (await getCertificateSubjectPublicKeyJWK(certificate)) as JWK
62
+ } catch (e) {}
81
63
  return {
82
64
  issuer: { dn: getIssuerDN(certificate) },
83
65
  subject: {
84
66
  dn: getSubjectDN(certificate),
85
67
  subjectAlternativeNames: getSubjectAlternativeNames(certificate, { typeFilter: opts?.sanTypeFilter }),
86
68
  },
87
- publicKeyJWK: publicKeyJWK,
69
+ publicKeyJWK,
88
70
  notBefore: certificate.notBefore.value,
89
71
  notAfter: certificate.notAfter.value,
90
72
  // certificate
@@ -92,6 +74,9 @@ export const getCertificateInfo = async (
92
74
  }
93
75
 
94
76
  export type X509CertificateChainValidationOpts = {
77
+ // If no trust anchor is found, but the chain itself checks out, allow. (defaults to false:)
78
+ allowNoTrustAnchorsFound?: boolean
79
+
95
80
  // Trust the supplied root from the chain, when no anchors are being passed in.
96
81
  trustRootWhenNoAnchors?: boolean
97
82
  // Do not perform a chain validation check if the chain only has a single value. This means only the certificate itself will be validated. No chain checks for CA certs will be performed. Only used when the cert has no issuer
@@ -114,6 +99,8 @@ export const validateX509CertificateChain = async ({
114
99
  trustAnchors,
115
100
  verificationTime = new Date(),
116
101
  opts = {
102
+ // If no trust anchor is found, but the chain itself checks out, allow. (defaults to false:)
103
+ allowNoTrustAnchorsFound: false,
117
104
  trustRootWhenNoAnchors: false,
118
105
  allowSingleNoCAChainElement: true,
119
106
  blindlyTrustedAnchors: [],
@@ -128,7 +115,7 @@ export const validateX509CertificateChain = async ({
128
115
  // We allow 1 reversal. We reverse by default as the implementation expects the root ca first, whilst x5c is the opposite. Reversed becomes true if the impl reverses the chain
129
116
  return await validateX509CertificateChainImpl({
130
117
  reversed: false,
131
- chain: pemOrDerChain.reverse(),
118
+ chain: [...pemOrDerChain].reverse(),
132
119
  trustAnchors,
133
120
  verificationTime,
134
121
  opts,
@@ -149,6 +136,7 @@ const validateX509CertificateChainImpl = async ({
149
136
  }): Promise<X509ValidationResult> => {
150
137
  const verificationTime: Date = typeof verifyAt === 'string' ? new Date(verifyAt) : verifyAt
151
138
  const {
139
+ allowNoTrustAnchorsFound = false,
152
140
  trustRootWhenNoAnchors = false,
153
141
  allowSingleNoCAChainElement = true,
154
142
  blindlyTrustedAnchors = [],
@@ -167,34 +155,52 @@ const validateX509CertificateChainImpl = async ({
167
155
  }
168
156
  defaultCryptoEngine()
169
157
 
170
- // x5c always starts with the leaf cert at index 0 and then the cas. Our internal pkijs service expects it the other way around
158
+ // x5c always starts with the leaf cert at index 0 and then the cas. Our internal pkijs service expects it the other way around. Before calling this function the change has been revered
171
159
  const chain = await Promise.all(pemOrDerChain.map((raw) => parseCertificate(raw)))
160
+ const x5cOrdereredChain = reversed ? [...chain] : [...chain].reverse()
161
+
172
162
  const trustedCerts = trustedPEMs ? await Promise.all(trustedPEMs.map((raw) => parseCertificate(raw))) : undefined
173
- const blindlyTrusted = (await Promise.all(blindlyTrustedAnchors.map((raw) => parseCertificate(raw)))) ?? []
174
- const leafCert = chain[chain.length - 1]
163
+ const blindlyTrusted =
164
+ (
165
+ await Promise.all(
166
+ blindlyTrustedAnchors.map((raw) => {
167
+ try {
168
+ return parseCertificate(raw)
169
+ } catch (e) {
170
+ // @ts-ignore
171
+ console.log(`Failed to parse blindly trusted certificate ${raw}. Error: ${e.message}`)
172
+ return undefined
173
+ }
174
+ })
175
+ )
176
+ ).filter((cert): cert is ParsedCertificate => cert !== undefined) ?? []
177
+ const leafCert = x5cOrdereredChain[0]
175
178
 
176
179
  const chainLength = chain.length
177
180
  var foundTrustAnchor: ParsedCertificate | undefined = undefined
178
181
  for (let i = 0; i < chainLength; i++) {
179
- const cert = chain[i]
180
- const prevCert = i > 0 ? chain[i - 1] : undefined
181
- if (blindlyTrusted.some((trusted) => areCertificatesEqual(trusted.certificate, cert.certificate))) {
182
+ const currentCert = chain[i]
183
+ const previousCert = i > 0 ? chain[i - 1] : undefined
184
+ const blindlyTrustedCert = blindlyTrusted.find((trusted) => areCertificatesEqual(trusted.certificate, currentCert.certificate))
185
+ if (blindlyTrustedCert) {
182
186
  console.log(`Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`)
183
187
  return {
184
188
  error: false,
185
189
  critical: false,
186
190
  message: `Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`,
191
+ detailMessage: `Blindly trusted certificate ${blindlyTrustedCert.certificateInfo.subject.dn.DN} was found in the chain.`,
192
+ trustAnchor: blindlyTrustedCert?.certificateInfo,
187
193
  verificationTime,
188
- certificateChain: chain.map((cert) => cert.certificateInfo),
194
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
189
195
  ...(client && { client }),
190
196
  }
191
197
  }
192
- if (i > 0) {
193
- if (cert.x509Certificate.issuer !== chain[i - 1].x509Certificate.subject) {
198
+ if (previousCert) {
199
+ if (currentCert.x509Certificate.issuer !== previousCert.x509Certificate.subject) {
194
200
  if (!reversed && !disallowReversedChain) {
195
201
  return await validateX509CertificateChainImpl({
196
202
  reversed: true,
197
- chain: pemOrDerChain.reverse(),
203
+ chain: [...pemOrDerChain].reverse(),
198
204
  opts,
199
205
  verificationTime,
200
206
  trustAnchors,
@@ -203,45 +209,54 @@ const validateX509CertificateChainImpl = async ({
203
209
  return {
204
210
  error: true,
205
211
  critical: true,
212
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
206
213
  message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`,
214
+ detailMessage: `The certificate ${currentCert.certificateInfo.subject.dn.DN} with issuer ${currentCert.x509Certificate.issuer}, is not signed by the previous certificate ${previousCert?.certificateInfo.subject.dn.DN} with subject string ${previousCert?.x509Certificate.subject}.`,
207
215
  verificationTime,
208
216
  ...(client && { client }),
209
217
  }
210
218
  }
211
219
  }
212
- const result = await cert.x509Certificate.verify(
220
+ const result = await currentCert.x509Certificate.verify(
213
221
  {
214
222
  date: verificationTime,
215
- publicKey: prevCert?.x509Certificate?.publicKey,
223
+ publicKey: previousCert?.x509Certificate?.publicKey,
216
224
  },
217
225
  getCrypto()?.crypto ?? crypto ?? global.crypto
218
226
  )
219
227
  if (!result) {
228
+ // First cert needs to be self signed
220
229
  if (i == 0 && !reversed && !disallowReversedChain) {
221
230
  return await validateX509CertificateChainImpl({
222
231
  reversed: true,
223
- chain: pemOrDerChain.reverse(),
232
+ chain: [...pemOrDerChain].reverse(),
224
233
  opts,
225
234
  verificationTime,
226
235
  trustAnchors,
227
236
  })
228
237
  }
238
+
229
239
  return {
230
240
  error: true,
231
241
  critical: true,
232
242
  message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`,
243
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
244
+ detailMessage: `Verification of the certificate ${currentCert.certificateInfo.subject.dn.DN} with issuer ${
245
+ currentCert.x509Certificate.issuer
246
+ } failed. Public key: ${JSON.stringify(currentCert.certificateInfo.publicKeyJWK)}.`,
233
247
  verificationTime,
234
248
  ...(client && { client }),
235
249
  }
236
250
  }
237
251
 
238
- foundTrustAnchor = foundTrustAnchor ?? trustedCerts?.find((trusted) => isSameCertificate(trusted.x509Certificate, cert.x509Certificate))
252
+ foundTrustAnchor = foundTrustAnchor ?? trustedCerts?.find((trusted) => isSameCertificate(trusted.x509Certificate, currentCert.x509Certificate))
239
253
 
240
254
  if (i === 0 && chainLength === 1 && allowSingleNoCAChainElement) {
241
255
  return {
242
256
  error: false,
243
257
  critical: false,
244
258
  message: `Certificate chain succeeded as allow single cert result is allowed: ${leafCert.certificateInfo.subject.dn.DN}.`,
259
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
245
260
  trustAnchor: foundTrustAnchor?.certificateInfo,
246
261
  verificationTime,
247
262
  ...(client && { client }),
@@ -249,11 +264,15 @@ const validateX509CertificateChainImpl = async ({
249
264
  }
250
265
  }
251
266
 
252
- if (foundTrustAnchor) {
267
+ if (foundTrustAnchor?.certificateInfo || allowNoTrustAnchorsFound) {
253
268
  return {
254
269
  error: false,
255
270
  critical: false,
256
271
  message: `Certificate chain was valid`,
272
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
273
+ detailMessage: foundTrustAnchor
274
+ ? `The leaf certificate ${leafCert.certificateInfo.subject.dn.DN} is part of a chain with trust anchor ${foundTrustAnchor?.certificateInfo.subject.dn.DN}.`
275
+ : `The leaf certificate ${leafCert.certificateInfo.subject.dn.DN} and chain were valid, but no trust anchor has been found. Ignoring as user allowed (allowNoTrustAnchorsFound: ${allowNoTrustAnchorsFound}).)`,
257
276
  trustAnchor: foundTrustAnchor?.certificateInfo,
258
277
  verificationTime,
259
278
  ...(client && { client }),
@@ -264,6 +283,10 @@ const validateX509CertificateChainImpl = async ({
264
283
  error: true,
265
284
  critical: true,
266
285
  message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`,
286
+ certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo),
287
+ detailMessage: `No trust anchor was found in the chain. between (intermediate) CA ${
288
+ x5cOrdereredChain[chain.length - 1].certificateInfo.subject.dn.DN
289
+ } and leaf ${x5cOrdereredChain[0].certificateInfo.subject.dn.DN}.`,
267
290
  verificationTime,
268
291
  ...(client && { client }),
269
292
  }
@@ -280,7 +303,7 @@ export const getX509AlgorithmProvider = (): AlgorithmProvider => {
280
303
 
281
304
  export type ParsedCertificate = {
282
305
  publicKeyInfo: SubjectPublicKeyInfo
283
- publicKeyJwk: JWK
306
+ publicKeyJwk?: JWK
284
307
  publicKeyRaw: Uint8Array
285
308
  publicKeyAlgorithm: Algorithm
286
309
  certificateInfo: CertificateInfo
@@ -292,7 +315,12 @@ export const parseCertificate = async (rawCert: string | Uint8Array): Promise<Pa
292
315
  const x509Certificate = new X509Certificate(rawCert)
293
316
  const publicKeyInfo = AsnParser.parse(x509Certificate.publicKey.rawData, SubjectPublicKeyInfo)
294
317
  const publicKeyRaw = new Uint8Array(publicKeyInfo.subjectPublicKey)
295
- const publicKeyJwk: JWK = (await getCertificateSubjectPublicKeyJWK(new Uint8Array(x509Certificate.rawData))) as JWK
318
+ let publicKeyJwk: JWK | undefined = undefined
319
+ try {
320
+ publicKeyJwk = (await getCertificateSubjectPublicKeyJWK(new Uint8Array(x509Certificate.rawData))) as JWK
321
+ } catch (e: any) {
322
+ console.error(e.message)
323
+ }
296
324
  const certificate = pemOrDerToX509Certificate(rawCert)
297
325
  const certificateInfo = await getCertificateInfo(certificate)
298
326
  const publicKeyAlgorithm = getX509AlgorithmProvider().toWebAlgorithm(publicKeyInfo.algorithm)
@@ -306,129 +334,142 @@ export const parseCertificate = async (rawCert: string | Uint8Array): Promise<Pa
306
334
  x509Certificate,
307
335
  }
308
336
  }
337
+ /*
309
338
 
310
- /**
339
+ /!**
311
340
  *
312
341
  * @param pemOrDerChain The order must be that the Certs signing another cert must come one after another. So first the signing cert, then any cert signing that cert and so on
313
342
  * @param trustedPEMs
314
343
  * @param verificationTime
315
344
  * @param opts
316
- */
345
+ *!/
317
346
  export const validateX509CertificateChainOrg = async ({
318
- chain: pemOrDerChain,
319
- trustAnchors,
320
- verificationTime = new Date(),
321
- opts = {
322
- trustRootWhenNoAnchors: false,
323
- allowSingleNoCAChainElement: true,
324
- blindlyTrustedAnchors: [],
325
- },
326
- }: {
327
- chain: (Uint8Array | string)[]
328
- trustAnchors?: string[]
329
- verificationTime?: Date
330
- opts?: X509CertificateChainValidationOpts
347
+ chain: pemOrDerChain,
348
+ trustAnchors,
349
+ verificationTime = new Date(),
350
+ opts = {
351
+ trustRootWhenNoAnchors: false,
352
+ allowSingleNoCAChainElement: true,
353
+ blindlyTrustedAnchors: [],
354
+ },
355
+ }: {
356
+ chain: (Uint8Array | string)[]
357
+ trustAnchors?: string[]
358
+ verificationTime?: Date
359
+ opts?: X509CertificateChainValidationOpts
331
360
  }): Promise<X509ValidationResult> => {
332
- const { trustRootWhenNoAnchors = false, allowSingleNoCAChainElement = true, blindlyTrustedAnchors = [], client } = opts
333
- const trustedPEMs = trustRootWhenNoAnchors && !trustAnchors ? [pemOrDerChain[pemOrDerChain.length - 1]] : trustAnchors
361
+ const {
362
+ trustRootWhenNoAnchors = false,
363
+ allowSingleNoCAChainElement = true,
364
+ blindlyTrustedAnchors = [],
365
+ client
366
+ } = opts
367
+ const trustedPEMs = trustRootWhenNoAnchors && !trustAnchors ? [pemOrDerChain[pemOrDerChain.length - 1]] : trustAnchors
368
+
369
+ if (pemOrDerChain.length === 0) {
370
+ return {
371
+ error: true,
372
+ critical: true,
373
+ message: 'Certificate chain in DER or PEM format must not be empty',
374
+ verificationTime,
375
+ }
376
+ }
334
377
 
335
- if (pemOrDerChain.length === 0) {
336
- return {
337
- error: true,
338
- critical: true,
339
- message: 'Certificate chain in DER or PEM format must not be empty',
340
- verificationTime,
378
+ // x5c always starts with the leaf cert at index 0 and then the cas. Our internal pkijs service expects it the other way around
379
+ const certs = pemOrDerChain.map(pemOrDerToX509Certificate).reverse()
380
+ const trustedCerts = trustedPEMs ? trustedPEMs.map(pemOrDerToX509Certificate) : undefined
381
+ defaultCryptoEngine()
382
+
383
+ if (pemOrDerChain.length === 1) {
384
+ const singleCert = typeof pemOrDerChain[0] === 'string' ? pemOrDerChain[0] : u8a.toString(pemOrDerChain[0], 'base64pad')
385
+ const cert = pemOrDerToX509Certificate(singleCert)
386
+ if (client) {
387
+ const validation = await validateCertificateChainMatchesClientIdScheme(cert, client.clientId, client.clientIdScheme)
388
+ if (validation.error) {
389
+ return validation
390
+ }
391
+ }
392
+ if (blindlyTrustedAnchors.includes(singleCert)) {
393
+ console.log(`Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`)
394
+ return {
395
+ error: false,
396
+ critical: true,
397
+ message: `Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`,
398
+ verificationTime,
399
+ certificateChain: [await getCertificateInfo(cert)],
400
+ ...(client && {client}),
401
+ }
402
+ }
403
+ if (allowSingleNoCAChainElement) {
404
+ const subjectDN = getSubjectDN(cert).DN
405
+ if (!getIssuerDN(cert).DN || getIssuerDN(cert).DN === subjectDN) {
406
+ const passed = await cert.verify()
407
+ return {
408
+ error: !passed,
409
+ critical: true,
410
+ message: `Certificate chain validation for ${subjectDN}: ${passed ? 'successful' : 'failed'}.`,
411
+ verificationTime,
412
+ certificateChain: [await getCertificateInfo(cert)],
413
+ ...(client && {client}),
414
+ }
415
+ }
416
+ }
341
417
  }
342
- }
343
418
 
344
- // x5c always starts with the leaf cert at index 0 and then the cas. Our internal pkijs service expects it the other way around
345
- const certs = pemOrDerChain.map(pemOrDerToX509Certificate).reverse()
346
- const trustedCerts = trustedPEMs ? trustedPEMs.map(pemOrDerToX509Certificate) : undefined
347
- defaultCryptoEngine()
419
+ const validationEngine = new CertificateChainValidationEngine({
420
+ certs /!*crls: [crl1], ocsps: [ocsp1], *!/,
421
+ checkDate: verificationTime,
422
+ trustedCerts,
423
+ })
348
424
 
349
- if (pemOrDerChain.length === 1) {
350
- const singleCert = typeof pemOrDerChain[0] === 'string' ? pemOrDerChain[0] : u8a.toString(pemOrDerChain[0], 'base64pad')
351
- const cert = pemOrDerToX509Certificate(singleCert)
352
- if (client) {
353
- const validation = await validateCertificateChainMatchesClientIdScheme(cert, client.clientId, client.clientIdScheme)
354
- if (validation.error) {
355
- return validation
356
- }
357
- }
358
- if (blindlyTrustedAnchors.includes(singleCert)) {
359
- console.log(`Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`)
360
- return {
361
- error: false,
362
- critical: true,
363
- message: `Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`,
364
- verificationTime,
365
- certificateChain: [await getCertificateInfo(cert)],
366
- ...(client && { client }),
367
- }
368
- }
369
- if (allowSingleNoCAChainElement) {
370
- const subjectDN = getSubjectDN(cert).DN
371
- if (!getIssuerDN(cert).DN || getIssuerDN(cert).DN === subjectDN) {
372
- const passed = await cert.verify()
373
- return {
374
- error: !passed,
375
- critical: true,
376
- message: `Certificate chain validation for ${subjectDN}: ${passed ? 'successful' : 'failed'}.`,
377
- verificationTime,
378
- certificateChain: [await getCertificateInfo(cert)],
379
- ...(client && { client }),
425
+ try {
426
+ const verification = await validationEngine.verify()
427
+ if (!verification.result || !verification.certificatePath) {
428
+ return {
429
+ error: true,
430
+ critical: true,
431
+ message: verification.resultMessage !== '' ? verification.resultMessage : `Certificate chain validation failed.`,
432
+ verificationTime,
433
+ ...(client && {client}),
434
+ }
435
+ }
436
+ const certPath = verification.certificatePath
437
+ if (client) {
438
+ const clientIdValidation = await validateCertificateChainMatchesClientIdScheme(certs[0], client.clientId, client.clientIdScheme)
439
+ if (clientIdValidation.error) {
440
+ return clientIdValidation
441
+ }
442
+ }
443
+ let certInfos: Array<CertificateInfo> | undefined
444
+
445
+ for (const certificate of certPath) {
446
+ try {
447
+ certInfos?.push(await getCertificateInfo(certificate))
448
+ } catch (e: any) {
449
+ console.log(`Error getting certificate info ${e.message}`)
450
+ }
380
451
  }
381
- }
382
- }
383
- }
384
452
 
385
- const validationEngine = new CertificateChainValidationEngine({
386
- certs /*crls: [crl1], ocsps: [ocsp1], */,
387
- checkDate: verificationTime,
388
- trustedCerts,
389
- })
390
453
 
391
- try {
392
- const verification = await validationEngine.verify()
393
- if (!verification.result || !verification.certificatePath) {
394
- return {
395
- error: true,
396
- critical: true,
397
- message: verification.resultMessage !== '' ? verification.resultMessage : `Certificate chain validation failed.`,
398
- verificationTime,
399
- ...(client && { client }),
400
- }
401
- }
402
- const certPath = verification.certificatePath
403
- if (client) {
404
- const clientIdValidation = await validateCertificateChainMatchesClientIdScheme(certs[0], client.clientId, client.clientIdScheme)
405
- if (clientIdValidation.error) {
406
- return clientIdValidation
407
- }
408
- }
409
- const certInfos: Array<CertificateInfo> = await Promise.all(
410
- certPath.map(async (certificate) => {
411
- return getCertificateInfo(certificate)
412
- })
413
- )
414
- return {
415
- error: false,
416
- critical: false,
417
- message: `Certificate chain was valid`,
418
- verificationTime,
419
- certificateChain: certInfos,
420
- ...(client && { client }),
421
- }
422
- } catch (error: any) {
423
- return {
424
- error: true,
425
- critical: true,
426
- message: `Certificate chain was invalid, ${error.message ?? '<unknown error>'}`,
427
- verificationTime,
428
- ...(client && { client }),
454
+ return {
455
+ error: false,
456
+ critical: false,
457
+ message: `Certificate chain was valid`,
458
+ verificationTime,
459
+ certificateChain: certInfos,
460
+ ...(client && {client}),
461
+ }
462
+ } catch (error: any) {
463
+ return {
464
+ error: true,
465
+ critical: true,
466
+ message: `Certificate chain was invalid, ${error.message ?? '<unknown error>'}`,
467
+ verificationTime,
468
+ ...(client && {client}),
469
+ }
429
470
  }
430
- }
431
471
  }
472
+ */
432
473
 
433
474
  const rdnmap: Record<string, string> = {
434
475
  '2.5.4.6': 'C',
@@ -472,23 +513,34 @@ const getDNString = (typesAndValues: AttributeTypeAndValue[]): string => {
472
513
  .join(',')
473
514
  }
474
515
 
475
- export const getCertificateSubjectPublicKeyJWK = async (pemOrDerCert: string | Uint8Array | Certificate): Promise<JsonWebKey> => {
516
+ export const getCertificateSubjectPublicKeyJWK = async (pemOrDerCert: string | Uint8Array | Certificate): Promise<JWK> => {
476
517
  const pemOrDerStr =
477
518
  typeof pemOrDerCert === 'string'
478
- ? pemOrDerCert
519
+ ? u8a.toString(u8a.fromString(pemOrDerCert, 'base64pad'), 'base64pad')
479
520
  : pemOrDerCert instanceof Uint8Array
480
521
  ? u8a.toString(pemOrDerCert, 'base64pad')
481
- : pemOrDerCert.toString('base64')
522
+ : u8a.toString(u8a.fromString(pemOrDerCert.toString('base64'), 'base64pad'), 'base64pad')
482
523
  const pem = derToPEM(pemOrDerStr)
483
524
  const certificate = pemOrDerToX509Certificate(pem)
525
+ var jwk: JWK | undefined
484
526
  try {
485
527
  const subtle = getCrypto(true).subtle
486
- const pk = await certificate.getPublicKey()
487
- return await subtle.exportKey('jwk', pk)
528
+ const pk = await certificate.getPublicKey(undefined, defaultCryptoEngine())
529
+ jwk = (await subtle.exportKey('jwk', pk)) as JWK | undefined
488
530
  } catch (error: any) {
489
531
  console.log(`Error in primary get JWK from cert:`, error?.message)
490
532
  }
491
- return await x509.toJwk(pem, 'pem')
533
+ if (!jwk) {
534
+ try {
535
+ jwk = (await x509.toJwk(pem, 'pem')) as JWK
536
+ } catch (error: any) {
537
+ console.log(`Error in secondary get JWK from cert as well:`, error?.message)
538
+ }
539
+ }
540
+ if (!jwk) {
541
+ throw Error(`Failed to get JWK from certificate ${pem}`)
542
+ }
543
+ return jwk
492
544
  }
493
545
 
494
546
  /**