Skip to content

Commit efda700

Browse files
authored
Bugfix: try multiple keys to validate signature (#51)
* Add unit test * Remove unused use-statement * Clone document before manipulating it
1 parent 01128b2 commit efda700

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

src/XML/SignedElementTrait.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
use SimpleSAML\XMLSecurity\XML\ds\X509Certificate;
2828
use SimpleSAML\XMLSecurity\XML\ds\X509Data;
2929

30-
use function array_pop;
3130
use function base64_decode;
3231
use function hash;
3332
use function hash_equals;
@@ -137,13 +136,18 @@ private function validateReference(SignedInfo $signedInfo): SignedElementInterfa
137136
$this->validateReferenceUri($reference, $xml);
138137
}
139138

140-
$xp = XPath::getXPath($xml->ownerDocument);
141-
$sigNode = XPath::xpQuery($xml, 'child::ds:Signature', $xp);
139+
// Clone the document so we don't mess up the original DOMDocument
140+
$doc = DOMDocumentFactory::create();
141+
$node = $doc->importNode($xml->ownerDocument->documentElement, true);
142+
$doc->appendChild($node);
143+
144+
$xp = XPath::getXPath($doc);
145+
$sigNode = XPath::xpQuery($doc->documentElement, 'child::ds:Signature', $xp);
142146
Assert::minCount($sigNode, 1, NoSignatureFoundException::class);
143147
Assert::maxCount($sigNode, 1, 'More than one signature found in object.', TooManyElementsException::class);
144-
$xml->removeChild($sigNode[0]);
145148

146-
$data = XML::processTransforms($reference->getTransforms(), $xml);
149+
$doc->documentElement->removeChild($sigNode[0]);
150+
$data = XML::processTransforms($reference->getTransforms(), $doc->documentElement);
147151
$algo = $reference->getDigestMethod()->getAlgorithm();
148152
Assert::keyExists(
149153
C::$DIGEST_ALGORITHMS,

tests/XML/SignedElementTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ final class SignedElementTest extends TestCase
3434
/** @var \SimpleSAML\XMLSecurity\CryptoEncoding\PEM */
3535
private PEM $certificate;
3636

37+
/** @var \SimpleSAML\XMLSecurity\CryptoEncoding\PEM */
38+
private PEM $wrong_certificate;
39+
3740
/** @var \DOMElement */
3841
private DOMElement $signedDocumentWithComments;
3942

@@ -63,6 +66,10 @@ public function setUp(): void
6366
$this->certificate = PEM::fromString(
6467
PEMCertificatesMock::getPlainCertificate(PEMCertificatesMock::SELFSIGNED_CERTIFICATE),
6568
);
69+
70+
$this->wrong_certificate = PEM::fromString(
71+
PEMCertificatesMock::getPlainCertificate(PEMCertificatesMock::OTHER_CERTIFICATE),
72+
);
6673
}
6774

6875

@@ -108,6 +115,44 @@ public function testSuccessfulVerifyingWithGivenKey(): void
108115
}
109116

110117

118+
/**
119+
* Test the verification of a signature with the wrong key first, and the right one second.
120+
*/
121+
public function testSuccessfulVerifyingWithWrongKeyFirstRightOneSecond(): void
122+
{
123+
$customSigned = CustomSignable::fromXML($this->signedDocument);
124+
125+
$this->assertTrue($customSigned->isSigned());
126+
$signature = $customSigned->getSignature();
127+
$this->assertInstanceOf(Signature::class, $signature);
128+
$sigAlg = $signature->getSignedInfo()->getSignatureMethod()->getAlgorithm();
129+
$this->assertEquals(C::SIG_RSA_SHA256, $sigAlg);
130+
131+
$verified = null;
132+
foreach ([$this->wrong_certificate, $this->certificate] as $i => $key) {
133+
$factory = new SignatureAlgorithmFactory();
134+
$certificate = new X509Certificate($key);
135+
$verifier = $factory->getAlgorithm($sigAlg, $certificate->getPublicKey());
136+
137+
try {
138+
$verified = $customSigned->verify($verifier);
139+
break 1;
140+
} catch (\SimpleSAML\XMLSecurity\Exception\SignatureVerificationFailedException $e) {
141+
continue;
142+
}
143+
}
144+
145+
$this->assertInstanceOf(CustomSignable::class, $verified);
146+
$this->assertFalse($verified->isSigned());
147+
$this->assertEquals(
148+
'<ssp:CustomSignable xmlns:ssp="urn:x-simplesamlphp:namespace"><ssp:Chunk>Some' .
149+
'</ssp:Chunk></ssp:CustomSignable>',
150+
strval($verified),
151+
);
152+
$this->assertEquals($certificate->getPublicKey(), $verified->getVerifyingKey());
153+
}
154+
155+
111156
/**
112157
* Test the verification of a signature without passing a key, just what's in KeyInfo
113158
*/

0 commit comments

Comments
 (0)