samlesa 2.17.3 → 2.18.0

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.
@@ -383,22 +383,32 @@ async function parseLoginResponseResolve(params) {
383
383
  return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
384
384
  }
385
385
  samlContent = verifiedAssertionNode1;
386
- const [verified, verifiedAssertionNode, isDecryptRequired, noSignature] = libsaml.verifySignature(samlContent, verificationOptions);
387
- if (isDecryptRequired && noSignature) {
388
- const result = await libsaml.decryptAssertion(sp, samlContent);
389
- samlContent = result[0];
390
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
391
- }
392
- if (!verified && !noSignature && !isDecryptRequired) {
393
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
386
+ // 改进的postFlow函数中关于签名验证的部分
387
+ const verificationResult = libsaml.verifySignature(samlContent, verificationOptions, self);
388
+ // 检查验证结果
389
+ if (!verificationResult.status) {
390
+ // 如果验证失败,根据具体情况返回错误
391
+ if (verificationResult.isMessageSigned && !verificationResult.MessageSignatureStatus) {
392
+ return Promise.reject('ERR_FAIL_TO_VERIFY_MESSAGE_SIGNATURE');
393
+ }
394
+ if (verificationResult.isAssertionSigned && !verificationResult.AssertionSignatureStatus) {
395
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ASSERTION_SIGNATURE');
396
+ }
397
+ if (verificationResult.encrypted && !verificationResult.decrypted) {
398
+ return Promise.reject('ERR_FAIL_TO_DECRYPT_ASSERTION');
399
+ }
400
+ // 通用验证失败
401
+ return Promise.reject('ERR_FAIL_TO_VERIFY_SIGNATURE_OR_DECRYPTION');
394
402
  }
395
- if (!isDecryptRequired) {
396
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
403
+ // 更新samlContent为验证后的版本(可能已解密)
404
+ samlContent = verificationResult.samlContent;
405
+ // 根据验证结果设置extractorFields
406
+ if (verificationResult.assertionContent) {
407
+ extractorFields = getDefaultExtractorFields(parserType, verificationResult.assertionContent);
397
408
  }
398
- if (parserType === 'SAMLResponse' && isDecryptRequired && !noSignature) {
399
- const result = await libsaml.decryptAssertion(sp, samlContent);
400
- samlContent = result[0];
401
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
409
+ else {
410
+ // 如果没有断言内容(例如注销请求/响应),使用适当的处理方式
411
+ extractorFields = getDefaultExtractorFields(parserType, null);
402
412
  }
403
413
  const parseResult = {
404
414
  samlContent: samlContent,
package/build/src/flow.js CHANGED
@@ -151,23 +151,149 @@ async function postFlow(options) {
151
151
  // check status based on different scenarios
152
152
  await checkStatus(samlContent, parserType);
153
153
  /**检查签名顺序 */
154
- const [verified, verifiedAssertionNode, isDecryptRequired, noSignature] = libsaml.verifySignature(samlContent, verificationOptions);
155
- decryptRequired = isDecryptRequired;
156
- if (isDecryptRequired && noSignature) {
157
- const result = await libsaml.decryptAssertion(self, samlContent);
158
- samlContent = result[0];
159
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
160
- }
161
- if (!verified && !noSignature && !isDecryptRequired) {
162
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
154
+ /*
155
+ const [verified, verifiedAssertionNode, isDecryptRequired, noSignature] = libsaml.verifySignature(samlContent, verificationOptions);
156
+ decryptRequired = isDecryptRequired
157
+ if (isDecryptRequired && noSignature) {
158
+
159
+ const result = await libsaml.decryptAssertion(self, samlContent);
160
+ samlContent = result[0];
161
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
162
+ }
163
+ if (!verified && !noSignature && !isDecryptRequired) {
164
+
165
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
166
+ }
167
+ if (!isDecryptRequired) {
168
+
169
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
170
+ }
171
+ if (parserType === 'SAMLResponse' && isDecryptRequired && !noSignature) {
172
+ const result = await libsaml.decryptAssertion(self, samlContent);
173
+ samlContent = result[0];
174
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
175
+ console.log("走这里来了=========")
176
+ console.log(result[1])
177
+ }
178
+
179
+
180
+ const parseResult = {
181
+ samlContent: samlContent,
182
+ extract: extract(samlContent, extractorFields),
183
+ };
184
+ /!**
185
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
186
+ *!/
187
+ const targetEntityMetadata = from.entityMeta;
188
+ const issuer = targetEntityMetadata.getEntityID();
189
+ const extractedProperties = parseResult.extract;
190
+ // unmatched issuer
191
+ if (
192
+ (parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
193
+ && extractedProperties
194
+ && extractedProperties.issuer !== issuer
195
+ ) {
196
+ return Promise.reject('ERR_UNMATCH_ISSUER');
197
+ }
198
+
199
+ // invalid session time
200
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
201
+ if (
202
+ parserType === 'SAMLResponse'
203
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
204
+ && !verifyTime(
205
+ undefined,
206
+ extractedProperties.sessionIndex.sessionNotOnOrAfter,
207
+ self.entitySetting.clockDrifts
208
+ )
209
+ ) {
210
+ return Promise.reject('ERR_EXPIRED_SESSION');
211
+ }
212
+
213
+ // invalid time
214
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
215
+ if (
216
+ parserType === 'SAMLResponse'
217
+ && extractedProperties.conditions
218
+ && !verifyTime(
219
+ extractedProperties.conditions.notBefore,
220
+ extractedProperties.conditions.notOnOrAfter,
221
+ self.entitySetting.clockDrifts
222
+ )
223
+ ) {
224
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
225
+ }
226
+ //valid destination
227
+ //There is no validation of the response here. The upper-layer application
228
+ // should verify the result by itself to see if the destination is equal to the SP acs and
229
+ // whether the response.id is used to prevent replay attacks.
230
+ /!*
231
+ let destination = extractedProperties?.response?.destination
232
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item) => {
233
+ return item?.Location === destination
234
+ })
235
+ if (isExit?.length === 0) {
236
+ return Promise.reject('ERR_Destination_URL');
237
+ }
238
+ if (parserType === 'SAMLResponse') {
239
+ let destination = extractedProperties?.response?.destination
240
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item: { Location: any; }) => {
241
+ return item?.Location === destination
242
+ })
243
+ if (isExit?.length === 0) {
244
+ return Promise.reject('ERR_Destination_URL');
245
+ }
246
+ }
247
+ *!/
248
+
249
+
250
+ return Promise.resolve(parseResult);*/
251
+ // 改进的postFlow函数中关于签名验证的部分
252
+ const verificationResult = libsaml.verifySignature(samlContent, verificationOptions, self);
253
+ /* console.log(verificationResult)
254
+ console.log("解析对象")*/
255
+ let resultObject = {
256
+ isMessageSigned: true, //是否有外层的消息签名(Response或者Request 等最外层的签名)
257
+ MessageSignatureStatus: true, //外层的签名是否经过验证
258
+ isAssertionSigned: true, //是否有断言的签名
259
+ AssertionSignatureStatus: true, //断言签名是否经过验证
260
+ encrypted: true, //断言是否加密
261
+ decrypted: true, //断言加密后断言是否解密成功,
262
+ status: true, //是否全部通过验证,
263
+ samlContent: 'xxx', //xxx是通过验证后 解密后的整个响应,
264
+ assertionContent: 'xxx', //xxx是通过验证后 解密后的整个响应中的assertion 断言部分字符串
265
+ };
266
+ // 检查验证结果
267
+ if (!verificationResult.status) {
268
+ // 如果验证失败,根据具体情况返回错误
269
+ /** 需要判断是不是 */
270
+ if (verificationResult.isMessageSigned && !verificationResult.MessageSignatureStatus) {
271
+ return Promise.reject('ERR_FAIL_TO_VERIFY_MESSAGE_SIGNATURE');
272
+ }
273
+ if (verificationResult.isAssertionSigned && !verificationResult.AssertionSignatureStatus) {
274
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ASSERTION_SIGNATURE');
275
+ }
276
+ if (verificationResult.encrypted && !verificationResult.decrypted) {
277
+ return Promise.reject('ERR_FAIL_TO_DECRYPT_ASSERTION');
278
+ }
279
+ if (!verificationResult.isMessageSigned && verificationResult.type === 'LogoutRequest') {
280
+ return Promise.reject('ERR_LogoutRequest_Need_Signature');
281
+ }
282
+ if (!verificationResult.isMessageSigned && verificationResult.type === 'LogoutResponse') {
283
+ return Promise.reject('ERR_LogoutResponse_Need_Signature');
284
+ }
285
+ // 通用验证失败
286
+ return Promise.reject('ERR_FAIL_TO_VERIFY_SIGNATURE_OR_DECRYPTION');
163
287
  }
164
- if (!isDecryptRequired) {
165
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
288
+ // 更新samlContent为验证后的版本(可能已解密)
289
+ samlContent = verificationResult.samlContent;
290
+ // 根据验证结果设置extractorFields
291
+ if (verificationResult.assertionContent) {
292
+ extractorFields = getDefaultExtractorFields(parserType, verificationResult.assertionContent);
166
293
  }
167
- if (parserType === 'SAMLResponse' && isDecryptRequired && !noSignature) {
168
- const result = await libsaml.decryptAssertion(self, samlContent);
169
- samlContent = result[0];
170
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
294
+ else {
295
+ // 如果没有断言内容(例如注销请求/响应),使用适当的处理方式
296
+ extractorFields = getDefaultExtractorFields(parserType, null);
171
297
  }
172
298
  const parseResult = {
173
299
  samlContent: samlContent,
@@ -245,18 +371,34 @@ async function postArtifactFlow(options) {
245
371
  // check status based on different scenarios
246
372
  await checkStatus(samlContent, parserType);
247
373
  /**检查签名顺序 */
248
- const [verified, verifiedAssertionNode, isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
249
- decryptRequired = isDecryptRequired;
250
- if (!verified) {
251
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
252
- }
253
- if (!decryptRequired) {
254
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
255
- }
256
- if (parserType === 'SAMLResponse' && decryptRequired) {
257
- const result = await libsaml.decryptAssertion(self, samlContent);
258
- samlContent = result[0];
259
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
374
+ // 改进的postFlow函数中关于签名验证的部分
375
+ const verificationResult = libsaml.verifySignature(samlContent, verificationOptions, self);
376
+ console.log(verificationResult);
377
+ console.log("最终结果====");
378
+ // 检查验证结果
379
+ if (!verificationResult.status) {
380
+ // 如果验证失败,根据具体情况返回错误
381
+ if (verificationResult.isMessageSigned && !verificationResult.MessageSignatureStatus) {
382
+ return Promise.reject('ERR_FAIL_TO_VERIFY_MESSAGE_SIGNATURE');
383
+ }
384
+ if (verificationResult.isAssertionSigned && !verificationResult.AssertionSignatureStatus) {
385
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ASSERTION_SIGNATURE');
386
+ }
387
+ if (verificationResult.encrypted && !verificationResult.decrypted) {
388
+ return Promise.reject('ERR_FAIL_TO_DECRYPT_ASSERTION');
389
+ }
390
+ // 通用验证失败
391
+ return Promise.reject('ERR_FAIL_TO_VERIFY_SIGNATURE_OR_DECRYPTION');
392
+ }
393
+ // 更新samlContent为验证后的版本(可能已解密)
394
+ samlContent = verificationResult.samlContent;
395
+ // 根据验证结果设置extractorFields
396
+ if (verificationResult.assertionContent) {
397
+ extractorFields = getDefaultExtractorFields(parserType, verificationResult.assertionContent);
398
+ }
399
+ else {
400
+ // 如果没有断言内容(例如注销请求/响应),使用适当的处理方式
401
+ extractorFields = getDefaultExtractorFields(parserType, null);
260
402
  }
261
403
  const parseResult = {
262
404
  samlContent: samlContent,
@@ -3,6 +3,7 @@
3
3
  * @author tngan
4
4
  * @desc A simple library including some common functions
5
5
  */
6
+ import { X509Certificate } from 'node:crypto';
6
7
  import xml from 'xml';
7
8
  import utility, { flattenDeep, inflateString, isString } from './utility.js';
8
9
  import { algorithms, namespace, wording } from './urn.js';
@@ -73,9 +74,9 @@ const libSaml = () => {
73
74
  };
74
75
  const defaultSoapResponseFailTemplate = {
75
76
  context: `<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header></SOAP-ENV:Header>
76
- <samlp:ArtifactResponse xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
77
+ <samlp:ArtifactResponse xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
77
78
  xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
78
- InResponseTo="{InResponseTo}" Version="2.0"
79
+ InResponseTo="{InResponseTo}" Version="2.0"
79
80
  IssueInstant="{IssueInstant}">
80
81
  <saml:Issuer>{Issuer}</saml:Issuer>
81
82
  <samlp:Status>
@@ -336,6 +337,37 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
336
337
  }
337
338
  return isBase64Output ? utility.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
338
339
  },
340
+ // 安全的证书验证函数
341
+ validateCertificate(certificateBase64, expectedIssuer) {
342
+ try {
343
+ const cert = new X509Certificate(Buffer.from(certificateBase64, 'base64'));
344
+ // 1. 检查有效期
345
+ const now = new Date();
346
+ if (new Date(cert.validFrom) > now || new Date(cert.validTo) < now) {
347
+ throw new Error('Certificate has expired or is not yet valid');
348
+ }
349
+ // 2. 检查颁发者(如果提供)
350
+ if (expectedIssuer && !cert.subject.includes(expectedIssuer)) {
351
+ throw new Error('Certificate issuer does not match expected value');
352
+ }
353
+ // 3. 检查公钥类型(推荐 RSA 或 EC)
354
+ if (!['rsa', 'ec'].includes(cert.publicKey.type.toLowerCase())) {
355
+ throw new Error('Certificate uses unsupported public key type');
356
+ }
357
+ return {
358
+ isValid: true,
359
+ subject: cert.subject,
360
+ issuer: cert.issuer,
361
+ publicKey: cert.publicKey
362
+ };
363
+ }
364
+ catch (error) {
365
+ return {
366
+ isValid: false,
367
+ error: error.message
368
+ };
369
+ }
370
+ },
339
371
  /**
340
372
  * @desc Verify the XML signature
341
373
  * @param {string} xml xml
@@ -345,7 +377,7 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
345
377
  * - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
346
378
  */
347
379
  // tslint:disable-next-line:no-shadowed-variable
348
- verifySignature(xml, opts) {
380
+ verifySignature1(xml, opts) {
349
381
  const { dom } = getContext();
350
382
  const doc = dom.parseFromString(xml, 'application/xml');
351
383
  const docParser = new DOMParser();
@@ -389,6 +421,7 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
389
421
  if (selection.length === 0) {
390
422
  /** 判断有没有加密如果没有加密返回 [false, null]*/
391
423
  if (encryptedAssertions.length > 0) {
424
+ console.log("走加密了====");
392
425
  if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
393
426
  return [false, null, false, true]; // we return false now
394
427
  }
@@ -399,6 +432,7 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
399
432
  }
400
433
  }
401
434
  if (selection.length !== 0) {
435
+ console.log("走加密了1====");
402
436
  /** 判断有没有加密如果没有加密返回 [false, null]*/
403
437
  if (logoutRequestSignature.length === 0 && LogoutResponseSignatureElementNode.length === 0 && encryptedAssertions.length > 0) {
404
438
  if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
@@ -418,10 +452,16 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
418
452
  if (!opts.keyFile && !opts.metadata) {
419
453
  throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
420
454
  }
455
+ console.log("开始1");
421
456
  if (opts.keyFile) {
457
+ console.log("开始11");
458
+ let publicCertResult = this.validateCertificate(fs.readFileSync(opts.keyFile));
459
+ console.log(publicCertResult);
460
+ console.log("结果");
422
461
  sig.publicCert = fs.readFileSync(opts.keyFile);
423
462
  }
424
463
  if (opts.metadata) {
464
+ console.log("开始111");
425
465
  const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
426
466
  // certificate in metadata
427
467
  let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
@@ -448,10 +488,17 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
448
488
  // to make sure the response certificate is one of those specified in metadata
449
489
  throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
450
490
  }
491
+ let publicCertResult = this.validateCertificate(x509Certificate);
492
+ console.log(publicCertResult);
493
+ console.log("结果");
451
494
  sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
452
495
  }
453
496
  else {
497
+ console.log("开始11111");
454
498
  // Select first one from metadata
499
+ let publicCertResult = this.validateCertificate(metadataCert[0]);
500
+ console.log(publicCertResult);
501
+ console.log("结果");
455
502
  sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
456
503
  }
457
504
  }
@@ -509,232 +556,317 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
509
556
  return [false, null, false, true]; // return encryptedAssert
510
557
  /* throw new Error('ERR_ZERO_SIGNATURE');*/
511
558
  },
512
- /* verifySignatureSoap(xml: string, opts: SignatureVerifierOptions & { isAssertion?: boolean }) {
513
- const {dom} = getContext();
559
+ /**
560
+ * 改进的SAML签名验证函数,支持多种签名和加密组合场景
561
+ * @param xml SAML XML内容
562
+ * @param opts 验证选项
563
+ * @param self
564
+ * @returns 验证结果对象
565
+ */
566
+ /**
567
+ * 改进的SAML签名验证函数,支持多种签名和加密组合场景
568
+ * @param xml SAML XML内容
569
+ * @param opts 验证选项
570
+ * @param self
571
+ * @returns 验证结果对象
572
+ */
573
+ verifySignature(xml, opts, self) {
574
+ const { dom } = getContext();
514
575
  const doc = dom.parseFromString(xml, 'application/xml');
515
576
  const docParser = new DOMParser();
516
-
517
- let selection: any = [];
518
-
519
- if (opts.isAssertion) {
520
- // 断言模式下的专用逻辑
521
- const assertionSignatureXpath = "./!*[local-name()='Signature']";
522
- // @ts-expect-error misssing Node properties are not needed
523
- const signatureNode = select(assertionSignatureXpath, doc.documentElement);
524
-
525
- if (signatureNode.length === 0) {
526
- throw new Error('ERR_ASSERTION_SIGNATURE_NOT_FOUND');
527
- }
528
-
529
- selection = selection.concat(signatureNode);
530
- } else {
531
- // 原始的SOAP响应验证逻辑
532
- const messageSignatureXpath =
533
- "/!*[local-name()='Envelope']/!*[local-name()='Body']" +
534
- "/!*[local-name()='ArtifactResponse']/!*[local-name()='Signature'] | " +
535
- "/!*[local-name()='Envelope']/!*[local-name()='Body']" +
536
- "/!*[local-name()='ArtifactResponse']/!*[local-name()='Response']/!*[local-name()='Signature']";
537
-
538
- const assertionSignatureXpath =
539
- "/!*[local-name()='Envelope']/!*[local-name()='Body']" +
540
- "/!*[local-name()='ArtifactResponse']/!*[local-name()='Response']" +
541
- "/!*[local-name()='Assertion']/!*[local-name()='Signature'] | " +
542
- "/!*[local-name()='Envelope']/!*[local-name()='Body']" +
543
- "/!*[local-name()='ArtifactResponse']/!*[local-name()='Response']" +
544
- "/!*[local-name()='EncryptedAssertion']";
545
-
546
- const wrappingElementsXPath =
547
- "/!*[local-name()='Envelope']/!*[local-name()='Body']" +
548
- "/!*[local-name()='ArtifactResponse']/!*[local-name()='Response']" +
549
- "/!*[local-name()='Assertion']/!*[local-name()='Subject']" +
550
- "/!*[local-name()='SubjectConfirmation']" +
551
- "/!*[local-name()='SubjectConfirmationData']" +
552
- "//!*[local-name()='Assertion' or local-name()='Signature']";
553
-
554
- // @ts-expect-error misssing Node properties are not needed
555
- const messageSignatureNode = select(messageSignatureXpath, doc);
556
- // @ts-expect-error misssing Node properties are not needed
557
- const assertionSignatureNode = select(assertionSignatureXpath, doc);
558
- // @ts-expect-error misssing Node properties are not needed
559
- const wrappingElementNode = select(wrappingElementsXPath, doc);
560
-
561
- // 检测包装攻击
562
- if (wrappingElementNode.length !== 0) {
577
+ // 定义各种XPath路径
578
+ const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
579
+ const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
580
+ const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
581
+ const encryptedAssertionsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']";
582
+ // 检测包装攻击
583
+ // @ts-expect-error misssing Node properties are not needed
584
+ const wrappingElementNode = select(wrappingElementsXPath, doc);
585
+ if (wrappingElementNode.length !== 0) {
563
586
  throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
564
- }
565
-
566
- // 保证响应中至少有一个签名
567
- if (messageSignatureNode.length === 0 && assertionSignatureNode.length === 0) {
568
- throw new Error('ERR_ZERO_SIGNATURE');
569
- }
570
-
571
- selection = selection.concat(messageSignatureNode, assertionSignatureNode);
572
587
  }
573
-
574
- for (const signatureNode of selection) {
575
- const sig = new SignedXml();
576
- let verified = false;
577
-
578
- sig.signatureAlgorithm = opts.signatureAlgorithm!;
579
-
580
- if (!opts.keyFile && !opts.metadata) {
581
- throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
582
- }
583
-
584
- if (opts.keyFile) {
585
- sig.publicCert = fs.readFileSync(opts.keyFile, 'utf-8');
586
- }
587
-
588
- if (opts.metadata) {
589
- const certificateNodes = select(".//!*[local-name(.)='X509Certificate']", signatureNode) as any[];
590
-
591
- // 获取元数据中的证书
592
- let metadataCert: any = opts.metadata.getX509Certificate(certUse.signing);
593
-
594
- // 规范化元数据证书
595
- if (Array.isArray(metadataCert)) {
596
- metadataCert = flattenDeep(metadataCert);
597
- } else if (typeof metadataCert === 'string') {
598
- metadataCert = [metadataCert];
588
+ // 获取各种元素
589
+ // @ts-expect-error misssing Node properties are not needed
590
+ const messageSignatureNode = select(messageSignatureXpath, doc);
591
+ // @ts-expect-error misssing Node properties are not needed
592
+ const assertionSignatureNode = select(assertionSignatureXpath, doc);
593
+ // @ts-expect-error misssing Node properties are not needed
594
+ const encryptedAssertions = select(encryptedAssertionsXPath, doc);
595
+ // 初始化验证状态
596
+ let isMessageSigned = messageSignatureNode.length > 0;
597
+ let isAssertionSigned = assertionSignatureNode.length > 0;
598
+ let encrypted = encryptedAssertions.length > 0;
599
+ let decrypted = false;
600
+ let MessageSignatureStatus = false;
601
+ let AssertionSignatureStatus = false;
602
+ let status = false;
603
+ let samlContent = xml;
604
+ let assertionContent = null;
605
+ // 检测SAML消息类型
606
+ // 检测SAML消息类型 - 使用精确匹配避免包含关系导致的误判
607
+ const rootElementName = doc.documentElement.localName;
608
+ let type = 'Unknown';
609
+ // 使用精确字符串比较,避免包含关系的问题
610
+ switch (rootElementName) {
611
+ case 'AuthnRequest':
612
+ type = 'AuthnRequest';
613
+ break;
614
+ case 'Response':
615
+ type = 'Response';
616
+ break;
617
+ case 'LogoutRequest':
618
+ type = 'LogoutRequest';
619
+ break;
620
+ case 'LogoutResponse':
621
+ type = 'LogoutResponse';
622
+ break;
623
+ default:
624
+ // 如果不是完全匹配,尝试模糊匹配
625
+ if (rootElementName.includes('AuthnRequest')) {
626
+ type = 'AuthnRequest';
627
+ }
628
+ else if (rootElementName.includes('LogoutResponse')) {
629
+ type = 'LogoutResponse';
630
+ }
631
+ else if (rootElementName.includes('LogoutRequest')) {
632
+ type = 'LogoutRequest';
633
+ }
634
+ else if (rootElementName.includes('Response')) {
635
+ type = 'Response';
636
+ }
637
+ else {
638
+ type = 'Unknown';
639
+ }
640
+ }
641
+ // 特殊情况:带未签名断言的未签名SAML响应,应该拒绝
642
+ if (!isMessageSigned && !isAssertionSigned && !encrypted) {
643
+ return {
644
+ isMessageSigned,
645
+ MessageSignatureStatus,
646
+ isAssertionSigned,
647
+ AssertionSignatureStatus,
648
+ encrypted,
649
+ decrypted,
650
+ type, // 添加类型字段
651
+ status: false, // 明确拒绝未签名未加密的响应
652
+ samlContent,
653
+ assertionContent
654
+ };
655
+ }
656
+ // 处理最外层有签名且断言加密的情况(关键逻辑补充)
657
+ if (isMessageSigned && encrypted) {
658
+ // 1. 首先解密断言
659
+ try {
660
+ const result = this.decryptAssertionSync(self, xml, opts);
661
+ // 更新文档为解密后的版本
662
+ samlContent = result[0];
663
+ assertionContent = result[1];
664
+ // 更新验证状态
665
+ decrypted = true;
666
+ AssertionSignatureStatus = result?.[2]?.AssertionSignatureStatus || false;
667
+ isAssertionSigned = result?.[2]?.isAssertionSigned || false;
668
+ // 解密后的文档
669
+ const decryptedDoc = dom.parseFromString(samlContent, 'application/xml');
670
+ // 2. 验证最外层消息签名(使用解密后的文档)
671
+ const signatureNode = messageSignatureNode[0];
672
+ const sig = new SignedXml();
673
+ if (!opts.keyFile && !opts.metadata) {
674
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
675
+ }
676
+ if (opts.keyFile) {
677
+ sig.publicCert = fs.readFileSync(opts.keyFile);
678
+ }
679
+ else if (opts.metadata) {
680
+ // @ts-expect-error misssing Node properties are not needed
681
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
682
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
683
+ if (Array.isArray(metadataCert)) {
684
+ metadataCert = flattenDeep(metadataCert);
685
+ }
686
+ else if (typeof metadataCert === 'string') {
687
+ metadataCert = [metadataCert];
688
+ }
689
+ metadataCert = metadataCert.map(utility.normalizeCerString);
690
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
691
+ throw new Error('NO_SELECTED_CERTIFICATE');
692
+ }
693
+ if (certificateNode.length !== 0) {
694
+ const x509CertificateData = certificateNode[0].firstChild.data;
695
+ const x509Certificate = utility.normalizeCerString(x509CertificateData);
696
+ if (metadataCert.length >= 1 && !metadataCert.find((cert) => cert.trim() === x509Certificate.trim())) {
697
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
698
+ }
699
+ sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
700
+ }
701
+ else {
702
+ sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
703
+ }
704
+ }
705
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
706
+ // @ts-expect-error misssing Node properties are not needed
707
+ sig.loadSignature(signatureNode);
708
+ // 使用解密后的文档验证最外层签名
709
+ MessageSignatureStatus = sig.checkSignature(decryptedDoc.toString());
710
+ if (!MessageSignatureStatus) {
711
+ throw new Error('ERR_FAILED_TO_VERIFY_MESSAGE_SIGNATURE_AFTER_DECRYPTION');
712
+ }
713
+ // 3. 验证解密后断言的签名(如果存在)
714
+ if (isAssertionSigned && AssertionSignatureStatus) {
715
+ /* console.log("断言签名验证已通过");*/
716
+ }
717
+ else if (isAssertionSigned && !AssertionSignatureStatus) {
718
+ throw new Error('ERR_FAILED_TO_VERIFY_ASSERTION_SIGNATURE_AFTER_DECRYPTION');
719
+ }
720
+ }
721
+ catch (err) {
722
+ throw err;
599
723
  }
600
-
601
- metadataCert = metadataCert.map(utility.normalizeCerString);
602
-
603
- // 检查证书可用性
604
- if (certificateNodes.length === 0 && metadataCert.length === 0) {
605
- throw new Error('NO_SELECTED_CERTIFICATE');
724
+ }
725
+ // 处理最外层有签名但断言未加密的情况
726
+ else if (isMessageSigned && !encrypted) {
727
+ const signatureNode = messageSignatureNode[0];
728
+ const sig = new SignedXml();
729
+ if (!opts.keyFile && !opts.metadata) {
730
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
606
731
  }
607
-
608
- // 响应中有证书节点
609
- if (certificateNodes.length !== 0) {
610
- // 安全获取证书数据
611
- let x509CertificateData = '';
612
- if (certificateNodes[0].firstChild) {
613
- x509CertificateData = certificateNodes[0].firstChild.data;
614
- } else if (certificateNodes[0].textContent) {
615
- x509CertificateData = certificateNodes[0].textContent;
616
- }
617
-
618
- const x509Certificate = utility.normalizeCerString(x509CertificateData);
619
-
620
- // 验证证书匹配
621
- if (
622
- metadataCert.length >= 1 &&
623
- !metadataCert.find(cert => cert.trim() === x509Certificate.trim())
624
- ) {
625
- throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
626
- }
627
-
628
- sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
629
- } else {
630
- // 使用元数据中的第一个证书
631
- sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
732
+ if (opts.keyFile) {
733
+ sig.publicCert = fs.readFileSync(opts.keyFile);
632
734
  }
633
- }
634
-
635
- // 加载签名
636
- sig.loadSignature(signatureNode);
637
- // 使用原始 XML 进行验证
638
- verified = sig.checkSignature(xml);
639
-
640
- if (!verified) {
641
- throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
642
- }
643
-
644
- // 检查签名引用
645
- if (!(sig.getSignedReferences().length >= 1)) {
646
- throw new Error('NO_SIGNATURE_REFERENCES');
647
- }
648
-
649
- const signedVerifiedXML = sig.getSignedReferences()[0];
650
- const verifiedDoc = docParser.parseFromString(signedVerifiedXML, 'application/xml');
651
- const rootNode = verifiedDoc.documentElement;
652
-
653
-
654
- // 断言模式专用返回逻辑
655
- if (opts.isAssertion) {
656
- if (rootNode?.localName === 'Assertion') {
657
- return [true, rootNode.toString(), false];
658
- } else {
659
- throw new Error('ERR_INVALID_ASSERTION_SIGNATURE');
735
+ else if (opts.metadata) {
736
+ // @ts-expect-error misssing Node properties are not needed
737
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
738
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
739
+ if (Array.isArray(metadataCert)) {
740
+ metadataCert = flattenDeep(metadataCert);
741
+ }
742
+ else if (typeof metadataCert === 'string') {
743
+ metadataCert = [metadataCert];
744
+ }
745
+ metadataCert = metadataCert.map(utility.normalizeCerString);
746
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
747
+ throw new Error('NO_SELECTED_CERTIFICATE');
748
+ }
749
+ if (certificateNode.length !== 0) {
750
+ const x509CertificateData = certificateNode[0].firstChild.data;
751
+ const x509Certificate = utility.normalizeCerString(x509CertificateData);
752
+ if (metadataCert.length >= 1 && !metadataCert.find((cert) => cert.trim() === x509Certificate.trim())) {
753
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
754
+ }
755
+ sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
756
+ }
757
+ else {
758
+ sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
759
+ }
660
760
  }
661
- }
662
-
663
- // 处理已验证的签名
664
- // @ts-expect-error misssing Node properties are not needed
665
- if (rootNode.localName === 'ArtifactResponse') {
666
- // 在 ArtifactResponse 中查找 Response
761
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
667
762
  // @ts-expect-error misssing Node properties are not needed
668
- const responseNodes = select(
669
- "./!*[local-name()='Response']",
670
- // @ts-expect-error misssing Node properties are not needed
671
- rootNode
672
- ) as Element[];
673
-
674
- if (responseNodes.length === 0) {
675
- continue;
763
+ sig.loadSignature(signatureNode);
764
+ MessageSignatureStatus = sig.checkSignature(doc.toString());
765
+ if (!MessageSignatureStatus) {
766
+ throw new Error('ERR_FAILED_TO_VERIFY_MESSAGE_SIGNATURE');
676
767
  }
677
-
678
- const responseNode = responseNodes[0];
679
-
680
- // Response 中查找断言
681
- const encryptedAssertions = select(
682
- "./!*[local-name()='EncryptedAssertion']",
683
- responseNode
684
- ) as Element[];
685
-
686
- const assertions = select(
687
- "./!*[local-name()='Assertion']",
688
- responseNode
689
- ) as Element[];
690
-
691
- if (encryptedAssertions.length === 1) {
692
- return [true, encryptedAssertions[0].toString(), true];
768
+ }
769
+ // 验证断言级签名(如果存在且未加密)
770
+ if (isAssertionSigned && !encrypted) {
771
+ const signatureNode = assertionSignatureNode[0];
772
+ const sig = new SignedXml();
773
+ if (!opts.keyFile && !opts.metadata) {
774
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
693
775
  }
694
-
695
- if (assertions.length === 1) {
696
- return [true, assertions[0].toString(), false];
776
+ if (opts.keyFile) {
777
+ sig.publicCert = fs.readFileSync(opts.keyFile);
697
778
  }
698
- }
699
- // 直接处理 Response
700
-
701
- else if (rootNode?.localName === 'Response') {
779
+ else if (opts.metadata) {
780
+ // @ts-expect-error misssing Node properties are not needed
781
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
782
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
783
+ if (Array.isArray(metadataCert)) {
784
+ metadataCert = flattenDeep(metadataCert);
785
+ }
786
+ else if (typeof metadataCert === 'string') {
787
+ metadataCert = [metadataCert];
788
+ }
789
+ metadataCert = metadataCert.map(utility.normalizeCerString);
790
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
791
+ throw new Error('NO_SELECTED_CERTIFICATE');
792
+ }
793
+ if (certificateNode.length !== 0) {
794
+ const x509CertificateData = certificateNode[0].firstChild.data;
795
+ const x509Certificate = utility.normalizeCerString(x509CertificateData);
796
+ if (metadataCert.length >= 1 && !metadataCert.find((cert) => cert.trim() === x509Certificate.trim())) {
797
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
798
+ }
799
+ sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
800
+ }
801
+ else {
802
+ sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
803
+ }
804
+ }
805
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
702
806
  // @ts-expect-error misssing Node properties are not needed
703
- const encryptedAssertions = select(
704
- "./!*[local-name()='EncryptedAssertion']",
705
- // @ts-expect-error misssing Node properties are not needed
706
- rootNode
707
- ) as Element[];
807
+ sig.loadSignature(signatureNode);
808
+ // 为断言签名验证,我们需要从根文档中获取断言部分
708
809
  // @ts-expect-error misssing Node properties are not needed
709
- const assertions = select(
710
- "./!*[local-name()='Assertion']",
711
- // @ts-expect-error misssing Node properties are not needed
712
- rootNode
713
- ) as Element[];
714
-
715
- if (encryptedAssertions.length === 1) {
716
- return [true, encryptedAssertions[0].toString(), true];
810
+ const assertionNode = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc)[0];
811
+ if (assertionNode) {
812
+ const assertionDoc = dom.parseFromString(assertionNode.toString(), 'application/xml');
813
+ AssertionSignatureStatus = sig.checkSignature(assertionDoc.toString());
814
+ }
815
+ else {
816
+ AssertionSignatureStatus = false;
817
+ }
818
+ if (!AssertionSignatureStatus) {
819
+ throw new Error('ERR_FAILED_TO_VERIFY_ASSERTION_SIGNATURE');
820
+ }
821
+ }
822
+ // 处理仅加密断言的情况(无消息签名)
823
+ if (encrypted && !isMessageSigned) {
824
+ if (!encryptedAssertions || encryptedAssertions.length === 0) {
825
+ throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
826
+ }
827
+ if (encryptedAssertions.length > 1) {
828
+ throw new Error('ERR_MULTIPLE_ASSERTION');
717
829
  }
718
-
719
- if (assertions.length === 1) {
720
- return [true, assertions[0].toString(), false];
830
+ const encAssertionNode = encryptedAssertions[0];
831
+ // 解密断言
832
+ try {
833
+ const result = this.decryptAssertionSync(self, xml, opts);
834
+ samlContent = result[0];
835
+ assertionContent = result[1];
836
+ decrypted = true;
837
+ AssertionSignatureStatus = result?.[2]?.AssertionSignatureStatus;
838
+ isAssertionSigned = result?.[2]?.isAssertionSigned;
839
+ }
840
+ catch (err) {
841
+ throw new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION');
842
+ }
843
+ }
844
+ else if (!encrypted && (isMessageSigned || isAssertionSigned)) {
845
+ // 如果没有加密但有签名,提取断言内容
846
+ // @ts-expect-error misssing Node properties are not needed
847
+ const assertions = select("/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']", doc);
848
+ if (assertions.length > 0) {
849
+ // @ts-expect-error misssing Node properties are not needed
850
+ assertionContent = assertions[0].toString();
721
851
  }
722
- }
723
- // 直接处理 Assertion
724
- else if (rootNode?.localName === 'Assertion') {
725
- return [true, rootNode.toString(), false];
726
- }
727
- // 直接处理 EncryptedAssertion
728
- else if (rootNode?.localName === 'EncryptedAssertion') {
729
- return [true, rootNode.toString(), true];
730
- } else {
731
-
732
- console.warn("未知的根节点类型:", rootNode?.localName);
733
- }
734
852
  }
735
-
736
- throw new Error('ERR_ZERO_SIGNATURE');
737
- },*/
853
+ // 检查整体状态
854
+ status = (!isMessageSigned || MessageSignatureStatus) &&
855
+ (!isAssertionSigned || AssertionSignatureStatus) &&
856
+ (!encrypted || decrypted);
857
+ return {
858
+ isMessageSigned,
859
+ MessageSignatureStatus,
860
+ isAssertionSigned,
861
+ AssertionSignatureStatus,
862
+ encrypted,
863
+ decrypted,
864
+ type, // 添加类型字段
865
+ status,
866
+ samlContent,
867
+ assertionContent
868
+ };
869
+ },
738
870
  verifySignatureSoap(xml, opts) {
739
871
  const { dom } = getContext();
740
872
  const doc = dom.parseFromString(xml, 'application/xml');
@@ -930,7 +1062,7 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
930
1062
  verifier.update(octetString);
931
1063
  const isValid = verifier.verify(utility.getPublicKeyPemFromCertificate(signCert), Buffer.isBuffer(signature) ? signature : Buffer.from(signature, 'base64'));
932
1064
  return isValid
933
-
1065
+
934
1066
  },*/
935
1067
  /**
936
1068
  * @desc Verifies message signature
@@ -1063,6 +1195,103 @@ xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="{ID}"
1063
1195
  });
1064
1196
  });
1065
1197
  },
1198
+ /**
1199
+ * 同步版本的断言解密函数
1200
+ */
1201
+ /**
1202
+ * 同步版本的断言解密函数,支持解密后验证断言签名
1203
+ */
1204
+ decryptAssertionSync(here, entireXML, opts) {
1205
+ const hereSetting = here.entitySetting;
1206
+ const { dom } = getContext();
1207
+ const doc = dom.parseFromString(entireXML, 'application/xml');
1208
+ // @ts-expect-error misssing Node properties are not needed
1209
+ const encryptedAssertions = select("/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']", doc);
1210
+ if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
1211
+ throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
1212
+ }
1213
+ if (encryptedAssertions.length > 1) {
1214
+ throw new Error('ERR_MULTIPLE_ASSERTION');
1215
+ }
1216
+ const encAssertionNode = encryptedAssertions[0];
1217
+ let decryptedResult = null;
1218
+ // 使用同步方式处理解密
1219
+ xmlenc.decrypt(encAssertionNode.toString(), {
1220
+ key: utility.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
1221
+ }, (err, res) => {
1222
+ if (err) {
1223
+ throw new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION');
1224
+ }
1225
+ if (!res) {
1226
+ throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
1227
+ }
1228
+ decryptedResult = res;
1229
+ });
1230
+ if (!decryptedResult) {
1231
+ throw new Error('ERR_UNDEFINED_DECRYPTED_ASSERTION');
1232
+ }
1233
+ // 解密完成后,检查解密后的断言是否还有签名需要验证
1234
+ const decryptedAssertionDoc = dom.parseFromString(decryptedResult, 'application/xml');
1235
+ let AssertionSignatureStatus = false;
1236
+ // 检查解密后的断言是否有签名
1237
+ // @ts-expect-error misssing Node properties are not needed
1238
+ const assertionSignatureNode = select("/*[local-name(.)='Assertion']/*[local-name(.)='Signature']", decryptedAssertionDoc);
1239
+ if (assertionSignatureNode.length > 0 && opts) {
1240
+ // 解密后的断言有签名,需要验证
1241
+ const signatureNode = assertionSignatureNode[0];
1242
+ const sig = new SignedXml();
1243
+ if (!opts.keyFile && !opts.metadata) {
1244
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
1245
+ }
1246
+ if (opts.keyFile) {
1247
+ sig.publicCert = fs.readFileSync(opts.keyFile);
1248
+ }
1249
+ else if (opts.metadata) {
1250
+ // @ts-expect-error misssing Node properties are not needed
1251
+ const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
1252
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
1253
+ if (Array.isArray(metadataCert)) {
1254
+ metadataCert = flattenDeep(metadataCert);
1255
+ }
1256
+ else if (typeof metadataCert === 'string') {
1257
+ metadataCert = [metadataCert];
1258
+ }
1259
+ metadataCert = metadataCert.map(utility.normalizeCerString);
1260
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
1261
+ throw new Error('NO_SELECTED_CERTIFICATE');
1262
+ }
1263
+ if (certificateNode.length !== 0) {
1264
+ const x509CertificateData = certificateNode[0].firstChild.data;
1265
+ const x509Certificate = utility.normalizeCerString(x509CertificateData);
1266
+ if (metadataCert.length >= 1 && !metadataCert.find((cert) => cert.trim() === x509Certificate.trim())) {
1267
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
1268
+ }
1269
+ sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
1270
+ }
1271
+ else {
1272
+ sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
1273
+ }
1274
+ }
1275
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
1276
+ // @ts-expect-error misssing Node properties are not needed
1277
+ sig.loadSignature(signatureNode);
1278
+ // 验证解密后断言的签名
1279
+ const assertionDocForVerification = dom.parseFromString(decryptedResult, 'application/xml');
1280
+ const assertionValid = sig.checkSignature(assertionDocForVerification.toString());
1281
+ AssertionSignatureStatus = assertionValid;
1282
+ if (!assertionValid) {
1283
+ throw new Error('ERR_FAILED_TO_VERIFY_DECRYPTED_ASSERTION_SIGNATURE');
1284
+ }
1285
+ }
1286
+ // 将解密后的断言替换原始文档中的加密断言
1287
+ const rawAssertionDoc = dom.parseFromString(decryptedResult, 'application/xml');
1288
+ // @ts-ignore
1289
+ doc.documentElement.replaceChild(rawAssertionDoc.documentElement, encAssertionNode);
1290
+ return [doc.toString(), decryptedResult, {
1291
+ isAssertionSigned: !!(assertionSignatureNode.length > 0 && opts),
1292
+ AssertionSignatureStatus: AssertionSignatureStatus
1293
+ }];
1294
+ },
1066
1295
  /**
1067
1296
  * 解密 SOAP 响应中的加密断言
1068
1297
  * @param self 当前实体(SP 或 IdP)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samlesa",
3
- "version": "2.17.3",
3
+ "version": "2.18.0",
4
4
  "description": "High-level API for Single Sign On (SAML 2.0) baseed on samlify ",
5
5
  "main": "build/index.js",
6
6
  "keywords": [
@@ -1 +1 @@
1
- {"version":3,"file":"binding-artifact.d.ts","sourceRoot":"","sources":["../../src/binding-artifact.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAMhD,OAAO,EACH,2BAA2B,IAAI,gBAAgB,EAC/C,0BAA0B,IAAI,eAAe,EAChD,MAAM,YAAY,CAAC;AAwCpB;;;;;GAKG;AACH,iBAAS,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,GAAG,GAAG,CA+FlI;AAED;;;;;;;;GAQG;AACH,iBAAe,iBAAiB,CAAC,WAAW,EAAE,GAAG,YAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAE,GAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,EAAE,eAAe,GAAE,OAAe,EAAE,kBAAkB,UAAK,GAAG,OAAO,CAAC,cAAc,CAAC,CAqIpO;AAED,iBAAe,wBAAwB,CAAC,MAAM,EAAE;IAC5C,GAAG,EAAE,gBAAgB,CAAC;IACtB,EAAE,EAAE,eAAe,CAAC;IACpB,GAAG,EAAE,MAAM,CAAA;CACd;;;GAqDA;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;;;GA0P3G;AAED,QAAA,MAAM,mBAAmB;;;;;CAOxB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"binding-artifact.d.ts","sourceRoot":"","sources":["../../src/binding-artifact.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAMhD,OAAO,EACH,2BAA2B,IAAI,gBAAgB,EAC/C,0BAA0B,IAAI,eAAe,EAChD,MAAM,YAAY,CAAC;AAwCpB;;;;;GAKG;AACH,iBAAS,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,GAAG,GAAG,CA+FlI;AAED;;;;;;;;GAQG;AACH,iBAAe,iBAAiB,CAAC,WAAW,EAAE,GAAG,YAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAE,GAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,EAAE,eAAe,GAAE,OAAe,EAAE,kBAAkB,UAAK,GAAG,OAAO,CAAC,cAAc,CAAC,CAqIpO;AAED,iBAAe,wBAAwB,CAAC,MAAM,EAAE;IAC5C,GAAG,EAAE,gBAAgB,CAAC;IACtB,EAAE,EAAE,eAAe,CAAC;IACpB,GAAG,EAAE,MAAM,CAAA;CACd;;;GAqDA;AAED,iBAAe,yBAAyB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;;;GAqQ3G;AAED,QAAA,MAAM,mBAAmB;;;;;CAOxB,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/flow.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA8iBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BhG;AAED,wBAAgB,IAAI,CAAC,OAAO,KAAA,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BjD"}
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/flow.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAgsBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BhG;AAED,wBAAgB,IAAI,CAAC,OAAO,KAAA,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BjD"}
@@ -164,6 +164,19 @@ declare const _default: {
164
164
  * @return {string} base64 encoded string
165
165
  */
166
166
  constructSAMLSignature(opts: SignatureConstructor): string;
167
+ validateCertificate(certificateBase64: string, expectedIssuer?: string): {
168
+ isValid: boolean;
169
+ subject: string;
170
+ issuer: string;
171
+ publicKey: import("crypto").KeyObject;
172
+ error?: undefined;
173
+ } | {
174
+ isValid: boolean;
175
+ error: any;
176
+ subject?: undefined;
177
+ issuer?: undefined;
178
+ publicKey?: undefined;
179
+ };
167
180
  /**
168
181
  * @desc Verify the XML signature
169
182
  * @param {string} xml xml
@@ -172,7 +185,33 @@ declare const _default: {
172
185
  * - The first element is `true` if the signature is valid, `false` otherwise.
173
186
  * - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
174
187
  */
175
- verifySignature(xml: string, opts: SignatureVerifierOptions): (boolean | null)[] | (string | boolean)[];
188
+ verifySignature1(xml: string, opts: SignatureVerifierOptions): (boolean | null)[] | (string | boolean)[];
189
+ /**
190
+ * 改进的SAML签名验证函数,支持多种签名和加密组合场景
191
+ * @param xml SAML XML内容
192
+ * @param opts 验证选项
193
+ * @param self
194
+ * @returns 验证结果对象
195
+ */
196
+ /**
197
+ * 改进的SAML签名验证函数,支持多种签名和加密组合场景
198
+ * @param xml SAML XML内容
199
+ * @param opts 验证选项
200
+ * @param self
201
+ * @returns 验证结果对象
202
+ */
203
+ verifySignature(xml: string, opts: SignatureVerifierOptions, self: any): {
204
+ isMessageSigned: boolean;
205
+ MessageSignatureStatus: boolean;
206
+ isAssertionSigned: boolean;
207
+ AssertionSignatureStatus: boolean;
208
+ encrypted: boolean;
209
+ decrypted: boolean;
210
+ type: "AuthnRequest" | "LogoutRequest" | "Response" | "LogoutResponse" | "Unknown";
211
+ status: boolean;
212
+ samlContent: string;
213
+ assertionContent: null;
214
+ };
176
215
  verifySignatureSoap(xml: string, opts: SignatureVerifierOptions): (boolean | null)[] | (string | boolean)[];
177
216
  /**
178
217
  * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
@@ -226,6 +265,16 @@ declare const _default: {
226
265
  * @return {function} a promise to get back the entire xml with decrypted assertion
227
266
  */
228
267
  decryptAssertion(here: any, entireXML: string): Promise<[string, any]>;
268
+ /**
269
+ * 同步版本的断言解密函数
270
+ */
271
+ /**
272
+ * 同步版本的断言解密函数,支持解密后验证断言签名
273
+ */
274
+ decryptAssertionSync(here: any, entireXML: string, opts?: SignatureVerifierOptions): (string | {
275
+ isAssertionSigned: boolean;
276
+ AssertionSignatureStatus: boolean;
277
+ })[];
229
278
  /**
230
279
  * 解密 SOAP 响应中的加密断言
231
280
  * @param self 当前实体(SP 或 IdP)
@@ -1 +1 @@
1
- {"version":3,"file":"libsaml.d.ts","sourceRoot":"","sources":["../../src/libsaml.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"libsaml.d.ts","sourceRoot":"","sources":["../../src/libsaml.ts"],"names":[],"mappings":"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"}