From 16e3bd37f56ac40f106f95d18ff884fd41f69c59 Mon Sep 17 00:00:00 2001 From: Maks3w Date: Tue, 5 Nov 2013 17:17:45 +0100 Subject: [PATCH] Improve public key extractor. Add a way for extract public key from DOM nodes. --- src/FR3D/XmlDSig/Adapter/AdapterInterface.php | 12 +++- .../XmlDSig/Adapter/XmlseclibsAdapter.php | 59 +++++++++++++++++-- .../XmlDSigTest/Adapter/CommonTestCase.php | 42 ++++++++++++- .../Adapter/XmlseclibsAdapterTest.php | 5 ++ 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/src/FR3D/XmlDSig/Adapter/AdapterInterface.php b/src/FR3D/XmlDSig/Adapter/AdapterInterface.php index cff7578..c683bd6 100644 --- a/src/FR3D/XmlDSig/Adapter/AdapterInterface.php +++ b/src/FR3D/XmlDSig/Adapter/AdapterInterface.php @@ -3,6 +3,7 @@ namespace FR3D\XmlDSig\Adapter; use DOMDocument; +use DOMNode; use RuntimeException; /** @@ -56,9 +57,18 @@ public function setPrivateKey($privateKey, $algorithmType = self::RSA_SHA1); public function setPublicKey($publicKey); /** + * Returns the public key from various sources + * + * Try to get the public key from the following sources (index means priority): + * + * 1) From $dom param of this method + * 2) From a previous publickey set by setPublicKey + * 3) From private key set by setPrivateKey + * + * @param null|DOMNode $dom DOM node where to search a publicKey * @return string|null Public key in PEM format */ - public function getPublicKey(); + public function getPublicKey(DOMNode $dom = null); /** * Public/Private key signature algorithm diff --git a/src/FR3D/XmlDSig/Adapter/XmlseclibsAdapter.php b/src/FR3D/XmlDSig/Adapter/XmlseclibsAdapter.php index bbee125..b994287 100644 --- a/src/FR3D/XmlDSig/Adapter/XmlseclibsAdapter.php +++ b/src/FR3D/XmlDSig/Adapter/XmlseclibsAdapter.php @@ -3,6 +3,7 @@ namespace FR3D\XmlDSig\Adapter; use DOMDocument; +use DOMNode; use XMLSecEnc; use RuntimeException; use XMLSecurityKey; @@ -77,14 +78,14 @@ public function setPublicKey($publicKey) return $this; } - public function getPublicKey() + public function getPublicKey(DOMNode $dom = null) { + if ($dom) { + $this->setPublicKeyFromNode($dom); + } + if (!$this->publicKey && $this->privateKey) { - // Extract public key from private key - openssl_pkey_export( - openssl_pkey_get_public($this->privateKey), - $this->publicKey - ); + $this->setPublicKeyFromPrivateKey($this->privateKey); } return $this->publicKey; @@ -191,4 +192,50 @@ public function verify(DOMDocument $data) return true; } + + /** + * Try to extract the public key from DOM node + * + * Sets publicKey and keyAlgorithm properties if success. + * + * @see publicKey + * @see keyAlgorithm + * @param DOMNode $dom + * @return bool `true` If public key was extracted or `false` if cannot be possible + */ + protected function setPublicKeyFromNode(DOMNode $dom) + { + // try to get the public key from the certificate + $objXMLSecDSig = new XMLSecurityDSig(); + $objDSig = $objXMLSecDSig->locateSignature($dom); + if (!$objDSig) { + return false; + } + + $objKey = $objXMLSecDSig->locateKey(); + if (!$objKey) { + return false; + } + + XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig); + $this->publicKey = $objKey->getX509Certificate(); + $this->keyAlgorithm = $objKey->getAlgorith(); + + return true; + } + + /** + * Try to extract the public key from private key + * + * @see publicKey + * @param string $privateKey + * @return bool `true` If public key was extracted or `false` if cannot be possible + */ + protected function setPublicKeyFromPrivateKey($privateKey) + { + return openssl_pkey_export( + openssl_pkey_get_public($privateKey), + $this->publicKey + ); + } } diff --git a/test/FR3D/XmlDSigTest/Adapter/CommonTestCase.php b/test/FR3D/XmlDSigTest/Adapter/CommonTestCase.php index baf3c5e..c35f282 100644 --- a/test/FR3D/XmlDSigTest/Adapter/CommonTestCase.php +++ b/test/FR3D/XmlDSigTest/Adapter/CommonTestCase.php @@ -26,6 +26,34 @@ class CommonTestCase extends \PHPUnit_Framework_TestCase */ protected $publicKey = '../_files/pubkey.pem'; + public function testGetPublicKeyFromSetter() + { + $publicKey = $this->getPublicKey(); + $this->assertNotEquals($publicKey, $this->adapter->getPublicKey()); + + $this->adapter->setPublicKey($publicKey); + $this->assertEquals($publicKey, $this->adapter->getPublicKey()); + } + + public function testGetPublicKeyFromPrivateKey() + { + $publicKey = $this->getPublicKey(); + $this->assertNotEquals($publicKey, $this->adapter->getPublicKey()); + + $this->adapter->setPrivateKey($this->getPrivateKey()); + $this->assertEquals($publicKey, $this->adapter->getPublicKey()); + } + + public function testGetPublicKeyFromNode() + { + $publicKey = $this->getPublicKey(); + $this->assertNotEquals($publicKey, $this->adapter->getPublicKey()); + + $data = new DOMDocument(); + $data->load(__DIR__ . '/_files/basic-doc-signed.xml'); + $this->assertEquals($publicKey, $this->adapter->getPublicKey($data)); + } + public function testSignWithoutPrivateKeys() { $this->setExpectedException( @@ -41,8 +69,8 @@ public function testSign() $data->load(__DIR__ . '/_files/basic-doc.xml'); $this->adapter - ->setPrivateKey(file_get_contents(__DIR__ . '/' . $this->privateKey)) - ->setPublicKey(file_get_contents(__DIR__ . '/' . $this->publicKey)) + ->setPrivateKey($this->getPrivateKey()) + ->setPublicKey($this->getPublicKey()) ->addTransform(AdapterInterface::ENVELOPED) ->setCanonicalMethod('http://www.w3.org/2001/10/xml-exc-c14n#') ->sign($data); @@ -84,4 +112,14 @@ public function testManipulatedSignature() $this->assertFalse($this->adapter->verify($data)); } + + protected function getPrivateKey() + { + return file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . $this->privateKey); + } + + protected function getPublicKey() + { + return file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . $this->publicKey); + } } diff --git a/test/FR3D/XmlDSigTest/Adapter/XmlseclibsAdapterTest.php b/test/FR3D/XmlDSigTest/Adapter/XmlseclibsAdapterTest.php index a6ed728..337338e 100644 --- a/test/FR3D/XmlDSigTest/Adapter/XmlseclibsAdapterTest.php +++ b/test/FR3D/XmlDSigTest/Adapter/XmlseclibsAdapterTest.php @@ -13,4 +13,9 @@ protected function setUp() { $this->adapter = new XmlseclibsAdapter(); } + + public function testGetPublicKeyFromPrivateKey() + { + $this->markTestIncomplete('PHP OpenSSL extension does not extract public key from private key'); + } }