Skip to content

Commit f2d03e7

Browse files
authored
Add XML-implementation for ds:X509Digest (#7)
Add XML-representation for ds:X509Digest element
1 parent 2e6afee commit f2d03e7

7 files changed

Lines changed: 269 additions & 7 deletions

File tree

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
"ext-dom": "*",
4040

4141
"robrichards/xmlseclibs": "^3.1.1",
42-
"simplesamlphp/assert": "~0.1.0",
43-
"simplesamlphp/xml-common": "~0.3.0"
42+
"simplesamlphp/assert": "~0.2.1",
43+
"simplesamlphp/xml-common": "~0.3.1"
4444
},
4545
"require-dev": {
4646
"simplesamlphp/simplesamlphp-test-framework": "^1.0.5"

src/XML/ds/X509Data.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ final class X509Data extends AbstractDsElement
2525
*
2626
* @var (\SimpleSAML\XML\Chunk|
2727
* \SimpleSAML\XMLSecurity\XML\ds\X509Certificate|
28+
* \SimpleSAML\XMLSecurity\XML\ds\X509Digest|
2829
* \SimpleSAML\XMLSecurity\XML\ds\X509SubjectName)[]
2930
*/
3031
protected array $data = [];
@@ -35,6 +36,7 @@ final class X509Data extends AbstractDsElement
3536
*
3637
* @param (\SimpleSAML\XML\Chunk|
3738
* \SimpleSAML\XMLSecurity\XML\ds\X509Certificate|
39+
* \SimpleSAML\XMLSecurity\XML\ds\X509Digest|
3840
* \SimpleSAML\XMLSecurity\XML\ds\X509SubjectName)[] $data
3941
*/
4042
public function __construct(array $data)
@@ -48,6 +50,7 @@ public function __construct(array $data)
4850
*
4951
* @return (\SimpleSAML\XML\Chunk|
5052
* \SimpleSAML\XMLSecurity\XML\ds\X509Certificate|
53+
* \SimpleSAML\XMLSecurity\XML\ds\X509Digest|
5154
* \SimpleSAML\XMLSecurity\XML\ds\X509SubjectName)[]
5255
*/
5356
public function getData(): array
@@ -61,13 +64,17 @@ public function getData(): array
6164
*
6265
* @param (\SimpleSAML\XML\Chunk|
6366
* \SimpleSAML\XMLSecurity\XML\ds\X509Certificate|
67+
* \SimpleSAML\XMLSecurity\XML\ds\X509Digest|
6468
* \SimpleSAML\XMLSecurity\XML\ds\X509SubjectName)[] $data
6569
* @throws \SimpleSAML\Assert\AssertionFailedException
6670
* if $data contains anything other than X509Certificate or Chunk
6771
*/
6872
private function setData(array $data): void
6973
{
70-
Assert::allIsInstanceOfAny($data, [Chunk::class, X509Certificate::class, X509SubjectName::class]);
74+
Assert::allIsInstanceOfAny(
75+
$data,
76+
[Chunk::class, X509Certificate::class, X509Digest::class, X509SubjectName::class]
77+
);
7178

7279
$this->data = $data;
7380
}
@@ -101,6 +108,9 @@ public static function fromXML(DOMElement $xml): object
101108
case 'X509Certificate':
102109
$data[] = X509Certificate::fromXML($n);
103110
break;
111+
case 'X509Digest':
112+
$data[] = X509Digest::fromXML($n);
113+
break;
104114
case 'X509SubjectName':
105115
$data[] = X509SubjectName::fromXML($n);
106116
break;

src/XML/ds/X509Digest.php

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\XMLSecurity\XML\ds;
6+
7+
use DOMElement;
8+
use SimpleSAML\Assert\Assert;
9+
use SimpleSAML\XML\Exception\InvalidDOMElementException;
10+
use SimpleSAML\XMLSecurity\Constants;
11+
use SimpleSAML\XMLSecurity\Exception\InvalidArgumentException;
12+
13+
/**
14+
* Class representing a ds:X509Digest element.
15+
*
16+
* @package simplesaml/xml-security
17+
*/
18+
final class X509Digest extends AbstractDsElement
19+
{
20+
/**
21+
* The digest.
22+
*
23+
* @var string
24+
*/
25+
protected string $digest;
26+
27+
28+
/**
29+
* The digest algorithm.
30+
*
31+
* @var string
32+
*/
33+
protected string $algorithm;
34+
35+
36+
/**
37+
* Initialize a X509Digest element.
38+
*
39+
* @param string $digest
40+
* @param string $algorithm
41+
*/
42+
public function __construct(string $digest, string $algorithm)
43+
{
44+
$this->setDigest($digest);
45+
$this->setAlgorithm($algorithm);
46+
}
47+
48+
49+
/**
50+
* Collect the value of the digest-property
51+
*
52+
* @return string
53+
*/
54+
public function getDigest(): string
55+
{
56+
return $this->digest;
57+
}
58+
59+
60+
/**
61+
* Set the value of the digest-property
62+
*
63+
* @param string $digest
64+
*/
65+
private function setDigest(string $digest): void
66+
{
67+
Assert::notEmpty($digest, 'X509Digest cannot be empty');
68+
Assert::stringPlausibleBase64($digest, 'ds:X509Digest is not a valid Base64 encoded string');
69+
70+
$this->digest = $digest;
71+
}
72+
73+
74+
/**
75+
* Collect the value of the algorithm-property
76+
*
77+
* @return string
78+
*/
79+
public function getAlgorithm(): string
80+
{
81+
return $this->algorithm;
82+
}
83+
84+
85+
/**
86+
* Set the value of the algorithm-property
87+
*
88+
* @param string $algorithm
89+
*/
90+
private function setAlgorithm(string $algorithm): void
91+
{
92+
Assert::oneOf(
93+
$algorithm,
94+
[
95+
Constants::DIGEST_SHA1,
96+
Constants::DIGEST_SHA224,
97+
Constants::DIGEST_SHA256,
98+
Constants::DIGEST_SHA384,
99+
Constants::DIGEST_SHA512,
100+
Constants::DIGEST_RIPEMD160,
101+
],
102+
'Invalid digest method',
103+
InvalidArgumentException::class
104+
);
105+
106+
$this->algorithm = $algorithm;
107+
}
108+
109+
110+
/**
111+
* Convert XML into a X509Digest
112+
*
113+
* @param \DOMElement $xml The XML element we should load
114+
* @return self
115+
*
116+
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
117+
* If the qualified name of the supplied element is wrong
118+
*/
119+
public static function fromXML(DOMElement $xml): object
120+
{
121+
Assert::same($xml->localName, 'X509Digest', InvalidDOMElementException::class);
122+
Assert::same($xml->namespaceURI, X509Digest::NS, InvalidDOMElementException::class);
123+
124+
$algorithm = self::getAttribute($xml, 'Algorithm');
125+
126+
$digest = $xml->textContent;
127+
Assert::stringPlausibleBase64($digest);
128+
129+
return new self($digest, $algorithm);
130+
}
131+
132+
133+
/**
134+
* Convert this X509Digest element to XML.
135+
*
136+
* @param \DOMElement|null $parent The element we should append this X509Digest element to.
137+
* @return \DOMElement
138+
*/
139+
public function toXML(DOMElement $parent = null): DOMElement
140+
{
141+
$e = $this->instantiateParentElement($parent);
142+
143+
$e->textContent = $this->digest;
144+
$e->setAttribute('Algorithm', $this->algorithm);
145+
146+
return $e;
147+
}
148+
}

tests/XML/ds/X509DataTest.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
use SimpleSAML\Test\XML\SerializableXMLTestTrait;
1111
use SimpleSAML\XML\Chunk;
1212
use SimpleSAML\XML\DOMDocumentFactory;
13+
use SimpleSAML\XMLSecurity\Constants;
14+
use SimpleSAML\XMLSecurity\Key;
1315
use SimpleSAML\XMLSecurity\XML\ds\X509Certificate;
1416
use SimpleSAML\XMLSecurity\XML\ds\X509Data;
17+
use SimpleSAML\XMLSecurity\XML\ds\X509Digest;
1518
use SimpleSAML\XMLSecurity\XML\ds\X509SubjectName;
1619
use SimpleSAML\XMLSecurity\TestUtils\PEMCertificatesMock;
1720
use SimpleSAML\XMLSecurity\XMLSecurityDSig;
@@ -34,6 +37,12 @@ final class X509DataTest extends TestCase
3437
/** @var string[] */
3538
private array $certData;
3639

40+
/** @var \SimpleSAML\XMLSecurity\Key\X509Certificate */
41+
private Key\X509Certificate $key;
42+
43+
/** @var string */
44+
private string $digest;
45+
3746

3847
/**
3948
*/
@@ -45,6 +54,12 @@ public function setUp(): void
4554
dirname(dirname(dirname(dirname(__FILE__)))) . '/tests/resources/xml/ds_X509Data.xml'
4655
);
4756

57+
$this->key = new Key\X509Certificate(
58+
PEMCertificatesMock::getPlainPublicKey()
59+
);
60+
61+
$this->digest = base64_encode(hex2bin($this->key->getRawThumbprint(Constants::DIGEST_SHA256)));
62+
4863
$this->certificate = str_replace(
4964
[
5065
'-----BEGIN CERTIFICATE-----',
@@ -81,6 +96,7 @@ public function testMarshalling(): void
8196
DOMDocumentFactory::fromString('<ds:X509UnknownTag>somevalue</ds:X509UnknownTag>')->documentElement
8297
),
8398
new X509Certificate($this->certificate),
99+
new X509Digest($this->digest, Constants::DIGEST_SHA256),
84100
new X509SubjectName($this->certData['name']),
85101
new Chunk(DOMDocumentFactory::fromString('<some>Chunk</some>')->documentElement)
86102
]
@@ -90,8 +106,9 @@ public function testMarshalling(): void
90106

91107
$this->assertInstanceOf(Chunk::class, $data[0]);
92108
$this->assertInstanceOf(X509Certificate::class, $data[1]);
93-
$this->assertInstanceOf(X509SubjectName::class, $data[2]);
94-
$this->assertInstanceOf(Chunk::class, $data[3]);
109+
$this->assertInstanceOf(X509Digest::class, $data[2]);
110+
$this->assertInstanceOf(X509SubjectName::class, $data[3]);
111+
$this->assertInstanceOf(Chunk::class, $data[4]);
95112

96113
$this->assertEquals($this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), strval($X509data));
97114
}
@@ -106,7 +123,8 @@ public function testUnmarshalling(): void
106123
$data = $X509data->getData();
107124
$this->assertInstanceOf(Chunk::class, $data[0]);
108125
$this->assertInstanceOf(X509Certificate::class, $data[1]);
109-
$this->assertInstanceOf(X509SubjectName::class, $data[2]);
110-
$this->assertInstanceOf(Chunk::class, $data[3]);
126+
$this->assertInstanceOf(X509Digest::class, $data[2]);
127+
$this->assertInstanceOf(X509SubjectName::class, $data[3]);
128+
$this->assertInstanceOf(Chunk::class, $data[4]);
111129
}
112130
}

tests/XML/ds/X509DigestTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\XMLSecurity\Test\XML\ds;
6+
7+
use DOMDocument;
8+
use PHPUnit\Framework\TestCase;
9+
use SimpleSAML\XML\DOMDocumentFactory;
10+
use SimpleSAML\XMLSecurity\Constants;
11+
use SimpleSAML\XMLSecurity\Key;
12+
use SimpleSAML\XMLSecurity\TestUtils\PEMCertificatesMock;
13+
use SimpleSAML\XMLSecurity\XML\ds\X509Digest;
14+
15+
/**
16+
* Class \SimpleSAML\XMLSecurity\XML\ds\X509DigestTest
17+
*
18+
* @covers \SimpleSAML\XMLSecurity\XML\ds\AbstractDsElement
19+
* @covers \SimpleSAML\XMLSecurity\XML\ds\X509Digest
20+
*
21+
* @package simplesamlphp/xml-security
22+
*/
23+
final class X509DigestTest extends TestCase
24+
{
25+
/** @var \DOMDocument */
26+
private DOMDocument $document;
27+
28+
/** @var \SimpleSAML\XMLSecurity\Key\X509Certificate */
29+
private Key\X509Certificate $key;
30+
31+
/** @var string */
32+
private string $digest;
33+
34+
35+
/**
36+
*/
37+
public function setUp(): void
38+
{
39+
$this->key = new Key\X509Certificate(
40+
PEMCertificatesMock::getPlainPublicKey()
41+
);
42+
$this->digest = base64_encode(hex2bin($this->key->getRawThumbprint(Constants::DIGEST_SHA256)));
43+
44+
$this->document = DOMDocumentFactory::fromFile(
45+
dirname(dirname(dirname(dirname(__FILE__)))) . '/tests/resources/xml/ds_X509Digest.xml'
46+
);
47+
}
48+
49+
50+
/**
51+
*/
52+
public function testMarshalling(): void
53+
{
54+
$X509digest = new X509Digest($this->digest, Constants::DIGEST_SHA256);
55+
56+
$this->assertEquals($this->digest, $X509digest->getDigest());
57+
$this->assertEquals(Constants::DIGEST_SHA256, $X509digest->getAlgorithm());
58+
59+
$this->assertEquals($this->document->saveXML($this->document->documentElement), strval($X509digest));
60+
}
61+
62+
63+
/**
64+
*/
65+
public function testUnmarshalling(): void
66+
{
67+
$X509digest = X509Digest::fromXML($this->document->documentElement);
68+
69+
$this->assertEquals($this->digest, $X509digest->getDigest());
70+
$this->assertEquals(Constants::DIGEST_SHA256, $X509digest->getAlgorithm());
71+
}
72+
73+
74+
/**
75+
* Test serialization / unserialization
76+
*/
77+
public function testSerialization(): void
78+
{
79+
$this->assertEquals(
80+
$this->document->saveXML($this->document->documentElement),
81+
strval(unserialize(serialize(X509Digest::fromXML($this->document->documentElement))))
82+
);
83+
}
84+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
22
<ds:X509UnknownTag>somevalue</ds:X509UnknownTag>
33
<ds:X509Certificate>MIICxDCCAi2gAwIBAgIUCJ8EYI/BrvQnHt9QFGfdqgaxDmowDQYJKoZIhvcNAQELBQAwczElMCMGA1UEAwwcc2VsZnNpZ25lZC5zaW1wbGVzYW1scGhwLm9yZzEZMBcGA1UECgwQU2ltcGxlU0FNTHBocCBIUTERMA8GA1UEBwwISG9ub2x1bHUxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UEBhMCVVMwIBcNMjAwNjIzMjEwMTEyWhgPMjEyMDA1MzAyMTAxMTJaMHMxJTAjBgNVBAMMHHNlbGZzaWduZWQuc2ltcGxlc2FtbHBocC5vcmcxGTAXBgNVBAoMEFNpbXBsZVNBTUxwaHAgSFExETAPBgNVBAcMCEhvbm9sdWx1MQ8wDQYDVQQIDAZIYXdhaWkxCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZ3ztmRqrJm6zSJ2jNUjbxdlX3WLadthHvDkdfQDWuFIUjMkCfO8+OWYdFP4cQWKBVnWyO+/LXFl9p9fwcSJmhmkgz5blUw0cjv8jccHw+yfVnGxGCK2Up/Si4HE/WStgjTKr3ivAqyc0Kc/97EoIXmb96SEEb4Lat9xt6mFSbWQIDAQABo1MwUTAdBgNVHQ4EFgQUHhB/tFwoDOT8V1nLi5XY4CJgTTQwHwYDVR0jBBgwFoAUHhB/tFwoDOT8V1nLi5XY4CJgTTQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBVqkGuiwyYMx0FH3j/qirZ2nKrjUU+p0ganzEA7kVZ97RivW5SFLHkRjcfqDXVyqOzmr4iR8rfxI2rUjYQI1SsKacPhitiArMk2IQ+uvxZ5eNtU2yFzW4arQ+a2ttlZI/GF9nLvgUNFIUwaDc+PQD2rOk+ACklbAS3jkf+L3DpQg==</ds:X509Certificate>
4+
<ds:X509Digest Algorithm="http://www.w3.org/2001/04/xmlenc#sha256">NllYUDA9XclmzMa84vOTdA3aunzwxBIqYjktnHBPcso=</ds:X509Digest>
45
<ds:X509SubjectName>/CN=selfsigned.simplesamlphp.org/O=SimpleSAMLphp HQ/L=Honolulu/ST=Hawaii/C=US</ds:X509SubjectName>
56
<some>Chunk</some>
67
</ds:X509Data>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<ds:X509Digest xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256">NllYUDA9XclmzMa84vOTdA3aunzwxBIqYjktnHBPcso=</ds:X509Digest>

0 commit comments

Comments
 (0)