diff --git a/src/Cryptography/CryptographyService.php b/src/Cryptography/CryptographyService.php index 90c6993..9ddc841 100644 --- a/src/Cryptography/CryptographyService.php +++ b/src/Cryptography/CryptographyService.php @@ -16,6 +16,12 @@ class CryptographyService public function __construct(string $privateKeyFile, string $publicKeyFile, string $privateKeyPassword = '') { + if (!file_exists($privateKeyFile)) { + throw new PrivateKeyFileNotFoundException($privateKeyFile); + } + if (!file_exists($publicKeyFile)) { + throw new PublicKeyFileNotFoundException($publicKeyFile); + } $this->privateKeyFile = $privateKeyFile; $this->publicKeyFile = $publicKeyFile; $this->privateKeyPassword = $privateKeyPassword; @@ -59,6 +65,7 @@ public function getBkpCode(string $pkpCode): string public function addWSESignature(string $request): string { + $this->tryLoadPublicKey(); $securityKey = new \RobRichards\XMLSecLibs\XMLSecurityKey(\RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA256, ['type' => 'private']); $document = new \DOMDocument('1.0'); $document->loadXML($request); @@ -73,4 +80,13 @@ public function addWSESignature(string $request): string return $wse->saveXML(); } + private function tryLoadPublicKey() + { + $publicKeyResource = openssl_get_publickey(file_get_contents($this->publicKeyFile)); + if ($publicKeyResource === false) { + throw new PublicKeyFileException($this->publicKeyFile); + } + openssl_free_key($publicKeyResource); + } + } diff --git a/src/Cryptography/PrivateKeyFileNotFoundException.php b/src/Cryptography/PrivateKeyFileNotFoundException.php new file mode 100644 index 0000000..7cd5bc3 --- /dev/null +++ b/src/Cryptography/PrivateKeyFileNotFoundException.php @@ -0,0 +1,8 @@ +publicKeyFile = $publicKeyFile; + } + + public function getPublicKeyFile(): string + { + return $this->publicKeyFile; + } + +} diff --git a/src/Cryptography/PublicKeyFileNotFoundException.php b/src/Cryptography/PublicKeyFileNotFoundException.php new file mode 100644 index 0000000..b0404a6 --- /dev/null +++ b/src/Cryptography/PublicKeyFileNotFoundException.php @@ -0,0 +1,8 @@ +getReceiptData(); - $crypto = new CryptographyService(__DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.key', __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.pub'); + $crypto = $this->createCryptographyServiceWithoutPassword(); $expectedPkp = base64_decode(self::EXPECTED_PKP); $pkpCode = $crypto->getPkpCode($data); @@ -21,11 +25,37 @@ public function testGetCodes() self::assertSame(self::EXPECTED_BKP, $crypto->getBkpCode($pkpCode)); } - public function testExceptions() + /** + * @dataProvider provideInvalidKeyPaths + */ + public function testInvalidKeyPaths(string $privateKeyPath, string $publicKeyPath, string $expectedExceptionType) + { + try { + new CryptographyService($privateKeyPath, $publicKeyPath); + $this->fail('Exception ' . $expectedExceptionType . ' expected'); + } catch (\PHPUnit\Framework\AssertionFailedError $exception) { + throw $exception; + } catch (\Throwable $exception) { + $this->assertInstanceOf($expectedExceptionType, $exception); + } + } + + /** + * @return array[] + */ + public function provideInvalidKeyPaths(): array + { + return [ + [self::PRIVATE_KEY_WITHOUT_PASSWORD_PATH, './foo/path', PublicKeyFileNotFoundException::class], + ['./foo/path', self::PUBLIC_KEY_PATH, PrivateKeyFileNotFoundException::class], + ]; + } + + public function testInvalidPrivateKeyInPkpCalculation() { $cryptoService = new CryptographyService( - __DIR__ . '/invalid-certificate.pem', - __DIR__ . '/invalid-certificate.pem' + self::INVALID_KEY_PATH, + self::PUBLIC_KEY_PATH ); try { @@ -33,7 +63,7 @@ public function testExceptions() $this->fail(); } catch (PrivateKeyFileException $e) { - $this->assertSame(__DIR__ . '/invalid-certificate.pem', $e->getPrivateKeyFile()); + $this->assertSame(self::INVALID_KEY_PATH, $e->getPrivateKeyFile()); } } @@ -44,10 +74,7 @@ public function testExceptions2() { include __DIR__ . '/OpenSslFunctionsMock.php'; - $cryptoService = new CryptographyService( - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.key', - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.pub' - ); + $cryptoService = $cryptoService = $this->createCryptographyServiceWithoutPassword(); try { $cryptoService->getPkpCode($this->getReceiptData()); @@ -61,10 +88,7 @@ public function testExceptions2() public function testWSESignatureWithoutPrivateKeyPassword() { $request = $this->getRequestData(); - $crypto = new CryptographyService( - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.key', - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.pub' - ); + $crypto = $this->createCryptographyServiceWithoutPassword(); $this->assertNotEmpty($crypto->addWSESignature($request)); } @@ -72,11 +96,7 @@ public function testWSESignatureWithoutPrivateKeyPassword() public function testWSESignatureWithPrivateKeyPassword() { $request = $this->getRequestData(); - $crypto = new CryptographyService( - __DIR__ . '/../../../cert/EET_CA1_Playground_With_Password-CZ00000019.key', - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.pub', - 'eet' - ); + $crypto = $this->createCryptographyServiceWithPassword('eet'); $this->assertNotEmpty($crypto->addWSESignature($request)); } @@ -84,17 +104,29 @@ public function testWSESignatureWithPrivateKeyPassword() public function testWSESignatureWithInvalidPrivateKeyPassword() { $request = $this->getRequestData(); - $crypto = new CryptographyService( - __DIR__ . '/../../../cert/EET_CA1_Playground_With_Password-CZ00000019.key', - __DIR__ . '/../../../cert/EET_CA1_Playground-CZ00000019.pub', - 'invalid' - ); + $crypto = $this->createCryptographyServiceWithPassword('invalid'); $this->expectException(\PHPUnit\Framework\Error\Error::class); $this->expectExceptionMessage('openssl_sign(): supplied key param cannot be coerced into a private key'); $crypto->addWSESignature($request); } + public function testWSESignatureWithInvalidPublicKey() + { + $request = $this->getRequestData(); + $crypto = new CryptographyService( + self::PRIVATE_KEY_WITHOUT_PASSWORD_PATH, + self::INVALID_KEY_PATH + ); + + try { + $crypto->addWSESignature($request); + $this->fail('Exception ' . PublicKeyFileException::class . ' expected'); + } catch (PublicKeyFileException $e) { + $this->assertSame(self::INVALID_KEY_PATH, $e->getPublicKeyFile()); + } + } + private function getReceiptData(): array { return [ @@ -127,4 +159,18 @@ private function getRequestData(): string return $request; } + private function createCryptographyServiceWithoutPassword(): CryptographyService + { + return new CryptographyService(self::PRIVATE_KEY_WITHOUT_PASSWORD_PATH, self::PUBLIC_KEY_PATH); + } + + private function createCryptographyServiceWithPassword(string $password): CryptographyService + { + return new CryptographyService( + self::PRIVATE_KEY_WITH_PASSWORD_PATH, + self::PUBLIC_KEY_PATH, + $password + ); + } + }