Skip to content

Commit

Permalink
Support specify custom keyring details for Electronic Signature.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew-svirin committed Dec 10, 2024
1 parent 49f23d9 commit 133a570
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 12 deletions.
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
trim_trailing_whitespace = false

[*.{key,pwd,crt,csr}]
insert_final_newline = false
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 2.3

* Support specify custom keyring details for Electronic Signature.
* Support switching A005/A006
* Change signature for KeyringManagerInterface. Attributes `USER.A.Version` moved from Bank to Keyring.
* Add `withES` param for downloading order types. Default `withES=false`.
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ ifdef WIN_ETH_DRIVER
WIN_ETH_IP := $(shell ipconfig.exe | grep ${WIN_ETH_DRIVER} -A3 | cut -d':' -f 2 | tail -n1 | sed -e 's/\s*//g')
endif

docker-up u:
docker-up u start:
cd docker && docker-compose -p ebics-client-php up -d;
@if [ "$(WIN_ETH_IP)" ]; then cd docker && docker-compose -p ebics-client-php exec php-cli-ebics-client-php sh -c "echo '$(WIN_ETH_IP) host.docker.internal' >> /etc/hosts"; fi

docker-down d:
docker-down d stop:
cd docker && docker-compose -p ebics-client-php down

docker-build:
docker-build build:
cd docker && docker-compose -p ebics-client-php build --no-cache

docker-php php:
Expand Down
5 changes: 4 additions & 1 deletion src/Contracts/EbicsClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ interface EbicsClientInterface

/**
* Create user signatures A, E and X on first launch.
* @param string $aVersion Version of Electronic Signature (A005|A006)
* @param array|null $aDetails Custom Certificate, private_key and
* password details for Electronic Signature.
*/
public function createUserSignatures(string $aVersion): void;
public function createUserSignatures(string $aVersion, array $aDetails = null): void;

/**
* Download supported protocol versions for the Bank.
Expand Down
28 changes: 22 additions & 6 deletions src/EbicsClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
use AndrewSvirin\Ebics\Models\UploadOrderResult;
use AndrewSvirin\Ebics\Models\UploadTransaction;
use AndrewSvirin\Ebics\Models\User;
use AndrewSvirin\Ebics\Models\X509\ContentX509Generator;
use AndrewSvirin\Ebics\Services\CryptService;
use AndrewSvirin\Ebics\Services\CurlHttpClient;
use AndrewSvirin\Ebics\Services\XmlService;
Expand Down Expand Up @@ -125,9 +126,11 @@ public function __construct(Bank $bank, User $user, Keyring $keyring)
* @inheritDoc
* @throws EbicsException
*/
public function createUserSignatures(string $aVersion = SignatureInterface::A_VERSION6): void
{
$signatureA = $this->createUserSignature(SignatureInterface::TYPE_A);
public function createUserSignatures(
string $aVersion = SignatureInterface::A_VERSION6,
array $aDetails = null
): void {
$signatureA = $this->createUserSignature(SignatureInterface::TYPE_A, $aDetails);
$this->keyring->setUserSignatureAVersion($aVersion);
$this->keyring->setUserSignatureA($signatureA);

Expand Down Expand Up @@ -1310,14 +1313,27 @@ private function getUserSignature(string $type): SignatureInterface
* @return SignatureInterface
* @throws EbicsException
*/
private function createUserSignature(string $type): SignatureInterface
private function createUserSignature(string $type, array $details = null): SignatureInterface
{
switch ($type) {
case SignatureInterface::TYPE_A:
if (null === $details) {
$keys = $this->cryptService->generateKeys($this->keyring->getPassword());
$certificateGenerator = $this->keyring->getCertificateGenerator();
} else {
$keys = $this->cryptService->changePrivateKeyPassword(
$details['privatekey'],
$details['password'],
$this->keyring->getPassword()
);
$certificateGenerator = new ContentX509Generator();
$certificateGenerator->setAContent($details['certificate']);
}

$signature = $this->signatureFactory->createSignatureAFromKeys(
$this->cryptService->generateKeys($this->keyring->getPassword()),
$keys,
$this->keyring->getPassword(),
$this->keyring->getCertificateGenerator()
$certificateGenerator
);
break;
case SignatureInterface::TYPE_E:
Expand Down
1 change: 0 additions & 1 deletion src/Models/X509/BankX509Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace AndrewSvirin\Ebics\Models\X509;

use AndrewSvirin\Ebics\Contracts\EbicsClientInterface;
use AndrewSvirin\Ebics\Models\Bank;
use LogicException;

Expand Down
65 changes: 65 additions & 0 deletions src/Models/X509/ContentX509Generator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace AndrewSvirin\Ebics\Models\X509;

use AndrewSvirin\Ebics\Contracts\Crypt\RSAInterface;
use AndrewSvirin\Ebics\Contracts\Crypt\X509Interface;
use AndrewSvirin\Ebics\Contracts\X509GeneratorInterface;
use AndrewSvirin\Ebics\Models\Crypt\X509;

/**
* Generator simulation for already created certificates and loaded from content.
*
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @author Andrew Svirin
*/
final class ContentX509Generator implements X509GeneratorInterface
{
private string $aContent;

private string $eContent;

private string $xContent;

public function setAContent(string $content): void
{
$this->aContent = $content;
}

public function setEContent(string $content): void
{
$this->eContent = $content;
}

public function setXContent(string $content): void
{
$this->xContent = $content;
}

public function generateAX509(RSAInterface $privateKey, RSAInterface $publicKey): X509Interface
{
$cert = new X509();

$cert->loadX509($this->aContent);

return $cert;
}

public function generateEX509(RSAInterface $privateKey, RSAInterface $publicKey): X509Interface
{
$cert = new X509();

$cert->loadX509($this->eContent);

return $cert;
}

public function generateXX509(RSAInterface $privateKey, RSAInterface $publicKey): X509Interface
{
$cert = new X509();

$cert->loadX509($this->xContent);

return $cert;
}
}
37 changes: 37 additions & 0 deletions tests/EbicsClientV30Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,43 @@ public function testChangeKeyringPassword(int $credentialsId, array $codes)
$this->assertResponseOk($code, $reportText);
}

/**
* @dataProvider serversDataProvider
*
* @group INI-CUSTOM
* @group INI-V30-CUSTOM
*
* @param int $credentialsId
* @param array $codes
*
* @covers
*/
public function testINIWithCustomCrt(int $credentialsId, array $codes)
{
$client = $this->setupClientV30($credentialsId, $codes['INI']['fake']);

$client->createUserSignatures('A005', [
'privatekey' => file_get_contents($this->data.'/electronic_signature/user.key'),
'certificate' => file_get_contents($this->data.'/electronic_signature/user.crt'),
'password' => file_get_contents($this->data.'/electronic_signature/passphrase.txt'),
]);

// Check that keyring is empty and or wait on success or wait on exception.
$userExists = $client->getKeyring()->getUserSignatureA();
if ($userExists) {
$this->expectException(InvalidUserOrUserStateException::class);
$this->expectExceptionCode(91002);
}
$ini = $client->INI();
if (!$userExists) {
$responseHandler = $client->getResponseHandler();
$this->saveKeyring($credentialsId, $client->getKeyring());
$code = $responseHandler->retrieveH00XReturnCode($ini);
$reportText = $responseHandler->retrieveH00XReportText($ini);
$this->assertResponseOk($code, $reportText);
}
}

/**
* @dataProvider serversDataProvider
*
Expand Down
Binary file modified tests/_data.zip
Binary file not shown.

0 comments on commit 133a570

Please sign in to comment.