@@ -362,6 +362,46 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
362
362
363
363
var doc = new Dom ( ) . parseFromString ( xml ) ;
364
364
365
+ // Reset the references as only references from our re-parsed signedInfo node can be trusted
366
+ this . references = [ ] ;
367
+
368
+ const unverifiedSignedInfoCanon = this . getCanonSignedInfoXml ( doc ) ;
369
+ if ( ! unverifiedSignedInfoCanon ) {
370
+ if ( callback ) {
371
+ callback ( new Error ( "Canonical signed info cannot be empty" ) ) ;
372
+ return ;
373
+ }
374
+
375
+ throw new Error ( "Canonical signed info cannot be empty" ) ;
376
+ }
377
+
378
+ // unsigned, verify later to keep with consistent callback behavior
379
+ const parsedUnverifiedSignedInfo = new Dom ( ) . parseFromString ( unverifiedSignedInfoCanon , "text/xml" ) ;
380
+
381
+ const unverifiedSignedInfoDoc = parsedUnverifiedSignedInfo . documentElement ;
382
+ if ( ! unverifiedSignedInfoDoc ) {
383
+ if ( callback ) {
384
+ callback ( new Error ( "Could not parse signedInfoCanon into a document" ) ) ;
385
+ return ;
386
+ }
387
+
388
+ throw new Error ( "Could not parse signedInfoCanon into a document" ) ;
389
+ }
390
+
391
+ const references = utils . findChilds ( unverifiedSignedInfoDoc , "Reference" ) ;
392
+ if ( references . length === 0 ) {
393
+ if ( callback ) {
394
+ callback ( new Error ( "could not find any Reference elements" ) ) ;
395
+ return ;
396
+ }
397
+
398
+ throw new Error ( "could not find any Reference elements" ) ;
399
+ }
400
+
401
+ for ( const reference of references ) {
402
+ this . loadReference ( reference ) ;
403
+ }
404
+
365
405
if ( ! this . validateReferences ( doc ) ) {
366
406
if ( ! callback ) {
367
407
return false ;
@@ -371,6 +411,7 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
371
411
}
372
412
}
373
413
414
+ // Stage B: Take the signature algorithm and key and verify the SignatureValue against the canonicalized SignedInfo
374
415
if ( ! callback ) {
375
416
// Synchronous flow
376
417
if ( ! this . validateSignatureValue ( doc ) ) {
@@ -394,7 +435,14 @@ SignedXml.prototype.checkSignature = function (xml, callback) {
394
435
395
436
SignedXml . prototype . getCanonSignedInfoXml = function ( doc ) {
396
437
var signedInfo = utils . findChilds ( this . signatureNode , "SignedInfo" ) ;
397
- if ( signedInfo . length == 0 ) throw new Error ( "could not find SignedInfo element in the message" ) ;
438
+ if ( signedInfo . length == 0 ) {
439
+ throw new Error ( "could not find SignedInfo element in the message" ) ;
440
+ }
441
+ if ( signedInfo . length > 1 ) {
442
+ throw new Error (
443
+ "could not get canonicalized signed info for a signature that contains multiple SignedInfo nodes"
444
+ ) ;
445
+ }
398
446
399
447
if (
400
448
this . canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
@@ -477,7 +525,7 @@ SignedXml.prototype.validateReferences = function (doc) {
477
525
478
526
var ref = this . references [ r ] ;
479
527
480
- var uri = ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri ;
528
+ var uri = ref . uri ? ( ref . uri [ 0 ] == "#" ? ref . uri . substring ( 1 ) : ref . uri ) : "" ;
481
529
var elem = [ ] ;
482
530
483
531
if ( uri == "" ) {
@@ -594,11 +642,43 @@ SignedXml.prototype.loadSignature = function (signatureNode) {
594
642
".//*[local-name(.)='SignatureMethod']/@Algorithm"
595
643
) . value ;
596
644
597
- this . references = [ ] ;
598
- var references = xpath . select (
599
- ".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']" ,
600
- signatureNode
645
+ const signedInfoNodes = utils . findChilds ( this . signatureNode , "SignedInfo" ) ;
646
+ if ( signedInfoNodes . length == 0 ) {
647
+ throw new Error ( "no signed info node found" ) ;
648
+ }
649
+ if ( signedInfoNodes . length > 1 ) {
650
+ throw new Error ( "could not load signature that contains multiple SignedInfo nodes" ) ;
651
+ }
652
+
653
+ // Try to operate on the c14n version of signedInfo. This forces the initial getReferences()
654
+ // API call to always return references that are loaded under the canonical SignedInfo
655
+ // in the case that the client access the .references **before** signature verification.
656
+
657
+ // Ensure canonicalization algorithm is exclusive, otherwise we'd need the entire document
658
+ let canonicalizationAlgorithmForSignedInfo = this . canonicalizationAlgorithm ;
659
+ if (
660
+ ! canonicalizationAlgorithmForSignedInfo ||
661
+ canonicalizationAlgorithmForSignedInfo ===
662
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ||
663
+ canonicalizationAlgorithmForSignedInfo ===
664
+ "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
665
+ ) {
666
+ canonicalizationAlgorithmForSignedInfo = "http://www.w3.org/2001/10/xml-exc-c14n#" ;
667
+ }
668
+
669
+ const temporaryCanonSignedInfo = this . getCanonXml (
670
+ [ this . canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#" ] ,
671
+ signedInfoNodes [ 0 ]
601
672
) ;
673
+ const temporaryCanonSignedInfoXml = new Dom ( ) . parseFromString (
674
+ temporaryCanonSignedInfo ,
675
+ "text/xml"
676
+ ) ;
677
+ const signedInfoDoc = temporaryCanonSignedInfoXml . documentElement ;
678
+
679
+ this . references = [ ] ;
680
+
681
+ const references = utils . findChilds ( signedInfoDoc , "Reference" ) ;
602
682
if ( references . length == 0 ) throw new Error ( "could not find any Reference elements" ) ;
603
683
604
684
for ( var i in references ) {
@@ -632,10 +712,17 @@ SignedXml.prototype.loadReference = function (ref) {
632
712
nodes = utils . findChilds ( ref , "DigestValue" ) ;
633
713
if ( nodes . length == 0 )
634
714
throw new Error ( "could not find DigestValue node in reference " + ref . toString ( ) ) ;
635
- if ( nodes [ 0 ] . childNodes . length == 0 || ! nodes [ 0 ] . firstChild . data ) {
636
- throw new Error ( "could not find the value of DigestValue in " + nodes [ 0 ] . toString ( ) ) ;
715
+
716
+ if ( nodes . length > 1 ) {
717
+ throw new Error (
718
+ `could not load reference for a node that contains multiple DigestValue nodes: ${ ref . toString ( ) } `
719
+ ) ;
720
+ }
721
+
722
+ const digestValue = nodes [ 0 ] . textContent ;
723
+ if ( ! digestValue ) {
724
+ throw new Error ( `could not find the value of DigestValue in ${ ref . toString ( ) } ` ) ;
637
725
}
638
- var digestValue = nodes [ 0 ] . firstChild . data ;
639
726
640
727
var transforms = [ ] ;
641
728
var inclusiveNamespacesPrefixList ;
@@ -688,11 +775,12 @@ SignedXml.prototype.loadReference = function (ref) {
688
775
transforms . push ( "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" ) ;
689
776
}
690
777
778
+ const refUri = ref . getAttribute ( "URI" ) || undefined ;
691
779
this . addReference (
692
780
null ,
693
781
transforms ,
694
782
digestAlgo ,
695
- utils . findAttr ( ref , "URI" ) . value ,
783
+ refUri ,
696
784
digestValue ,
697
785
inclusiveNamespacesPrefixList ,
698
786
false
0 commit comments