Skip to content

Commit

Permalink
Merge pull request #35 from andrew-svirin/dev
Browse files Browse the repository at this point in the history
Makes possible to have users certificates.
  • Loading branch information
Andrew Svirin committed Mar 22, 2020
2 parents ebc8db8 + 96f7d0f commit 45fe2f6
Show file tree
Hide file tree
Showing 16 changed files with 846 additions and 196 deletions.
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,55 @@ andrew-svirin/ebics-client-php is licensed under the MIT License, see the LICENS
echo $code . '-' . $reportText . "\n";
```

More methods you can find in `tests/Unit/EbicsTest`

### French Bank
When using french bank, you will need to create a X509 certificate. Create a class which extends the `AbstractX509Generator` (or implements the `X509GeneratorInterface` if you want a total control about the generation)
```php
<?php

namespace App\Factories\X509;

use AndrewSvirin\Ebics\Factories\X509\AbstractX509Generator;

class MyCompanyX509Generator extends AbstractX509Generator
{
public function getCertificateOptions(array $options = []) : array{
return [
'subject' => [
'DN' => [
'id-at-countryName' => 'FR',
'id-at-stateOrProvinceName' => 'State',
'id-at-localityName' => 'City',
'id-at-organizationName' => 'Your company',
'id-at-commonName' => 'yourwebsite.tld',
]
],
'extensions' => [
'id-ce-subjectAltName' => [
'value' => [
[
'dNSName' => '*.yourwebsite.tld',
],
]
],
],
];
}
}
```
You can see more values in the `LegacyX509Generator` class.

Then call the `X509GeneratorFactory::setGeneratorClass()` method :
```php
X509GeneratorFactory::setGeneratorClass(MyCompanyX509Generator::class);
//...
$client->INI();
```




More methods you can find in `tests/EbicsTest`

### Statistic
[![Build Status](https://travis-ci.org/andrew-svirin/ebics-client-php.svg?branch=master)](https://travis-ci.org/andrew-svirin/ebics-client-php)
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
"php": "^7.2",
"ext-dom": "*",
"ext-openssl": "*",
"ext-zlib": "*",
"ext-json": "*",
"andrew-svirin/mt942-php": "~1.0",
"comodojo/httprequest": "~1.0",
"phpseclib/phpseclib": "~2.0",
"ext-zlib": "*",
"ext-json": "*",
"symfony/http-client": "^4.3||^5.0"
},
"require-dev": {
Expand Down
23 changes: 23 additions & 0 deletions src/Contracts/X509GeneratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace AndrewSvirin\Ebics\Contracts;

use phpseclib\Crypt\RSA;

/**
* X509 Factory Interface representation.
*
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @author Guillaume Sainthillier
*/
interface X509GeneratorInterface
{
/**
* Generate a X509 certificate and returns its content
* @param RSA $privateKey the private key
* @param RSA $publicKey the public key
* @param array $options optional generation options (may be empty)
* @return string the X509 content
*/
public function generateX509(RSA $privateKey, RSA $publicKey, array $options = []): string;
}
269 changes: 76 additions & 193 deletions src/Factories/CertificateFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,208 +2,91 @@

namespace AndrewSvirin\Ebics\Factories;

use AndrewSvirin\Ebics\Factories\X509\X509GeneratorFactory;
use AndrewSvirin\Ebics\Models\Certificate;
use DateTime;
use phpseclib\Crypt\RSA;
use phpseclib\File\X509;
use phpseclib\Math\BigInteger;

/**
* Class CertificateFactory represents producers for the @see Certificate.
*
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @author Andrew Svirin
* @author Andrew Svirin, Guillaume Sainthillier
*/
class CertificateFactory
{

public static function buildCertificateA(string $publicKey, string $privateKey, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_A, $publicKey, $privateKey, $content);
}

public static function buildCertificateE(string $publicKey, string $privateKey = null, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_E, $publicKey, $privateKey, $content);
}

public static function buildCertificateX(string $publicKey, string $privateKey = null, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_X, $publicKey, $privateKey, $content);
}

public static function generateCertificateAFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_A, $isCertified);
}

public static function generateCertificateEFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_E, $isCertified);
}

public static function generateCertificateXFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_X, $isCertified);
}

private static function generateCertificateFromKeys(array $keys, string $type, bool $isCertified): Certificate
{
if ($isCertified)
{
$certificateContent = self::generateCertificateContent($keys);
}
$certificate = new Certificate($type, $keys['publickey'], $keys['privatekey'], $certificateContent ?? null);
return $certificate;
}

private static function generateCertificateContent(array $keys): string
{
// TODO: Move options to separate settings.
$privateKey = new RSA();
$privateKey->loadKey($keys['privatekey']);

$publicKey = new RSA();
$publicKey->loadKey($keys['publickey']);
$publicKey->setPublicKey();

$subject = new X509();
$subject->setPublicKey($publicKey); // $pubKey is Crypt_RSA object
$subject->setDN([
'id-at-countryName' => 'FR',
'id-at-stateOrProvinceName' => 'Seine-et-Marne',
'id-at-localityName' => 'Melun',
'id-at-organizationName' => 'Elcimai Informatique',
'id-at-commonName' => '*.webank.fr',
]);
$subject->setKeyIdentifier($subject->computeKeyIdentifier($publicKey)); // id-ce-subjectKeyIdentifier

$issuer = new X509();
$issuer->setPrivateKey($privateKey); // $privKey is Crypt_RSA object
$issuer->setDN([
'id-at-countryName' => 'US',
'id-at-organizationName' => 'GeoTrust Inc.',
'id-at-commonName' => 'GeoTrust SSL CA - G3',
]);
$issuer->setKeyIdentifier($subject->computeKeyIdentifier($publicKey)); // id-ce-authorityKeyIdentifier

$today = DateTime::createFromFormat('U', time());
$x509 = new X509();

$x509->startDate = $today->modify('-1 day')->format('YmdHis');
$x509->endDate = $today->modify('+1 year')->format('YmdHis');
$x509->serialNumber = self::generateSerialNumber();
$result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption');
$x509->loadX509($result);
$x509->setExtension('id-ce-subjectAltName', [
[
'dNSName' => '*.webank.fr',
],
[
'dNSName' => 'webank.fr',
],
]);
$x509->setExtension('id-ce-basicConstraints', [
'cA' => false,
]);
$x509->setExtension('id-ce-keyUsage', ['keyEncipherment', 'digitalSignature'], true);
$x509->setExtension('id-ce-cRLDistributionPoints', [
[
'distributionPoint' =>
[
'fullName' =>
[
[
'uniformResourceIdentifier' => 'http://gn.symcb.com/gn.crl',
],
],
],
]]);
$x509->setExtension('id-ce-certificatePolicies', [
[
'policyIdentifier' => '2.23.140.1.2.2',
'policyQualifiers' =>
[
[
'policyQualifierId' => 'id-qt-cps',
'qualifier' =>
[
'ia5String' => 'https://www.geotrust.com/resources/repository/legal',
],
],
[
'policyQualifierId' => 'id-qt-unotice',
'qualifier' =>
[
'explicitText' =>
[
'utf8String' => 'https://www.geotrust.com/resources/repository/legal',
],
],
],
],
],
]);
$x509->setExtension('id-ce-extKeyUsage', ['id-kp-serverAuth', 'id-kp-clientAuth']);
$x509->setExtension('id-pe-authorityInfoAccess', [
[
'accessMethod' => 'id-ad-ocsp',
'accessLocation' =>
[
'uniformResourceIdentifier' => 'http://gn.symcd.com',
],
],
[
'accessMethod' => 'id-ad-caIssuers',
'accessLocation' =>
[
'uniformResourceIdentifier' => 'http://gn.symcb.com/gn.crt',
],
],
]);
$x509->setExtension('1.3.6.1.4.1.11129.2.4.2',
'BIIBbAFqAHcA3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswAAAFdCJcynQAABAMASDBGAiEAgJgQE9466xkMy6olq+1xvTGt9ROXcgmdUIht4EE4g14CIQDZNjYcKbVU6taN/unn2WHlsDgphMgQXzALHt7vrI/bIgB2AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABXQiXMtAAAAQDAEcwRQIgTx+2uvI9ReTYiO9Ii85qoet1dc+y58RT4wAO9C4OCakCIQCRhO2kJWxeSfP1L2/Q24I3MGLMn//mwhdJ43mu4e9n8gB3AO5Lvbd1zmC64UJpH6vhnmajD35fsHLYgwDEe4l6qP3LAAABXQiXNJcAAAQDAEgwRgIhAM+dK3OLBL5nGzp/PSt3yRab85AD3jz69g5TqGdrMuhkAiEAnDMu/ZiqyBWO3+li3L9/hi3BcHX74rAmA3OX1jNxIKE='
);
$result = $x509->sign($issuer, $x509, 'sha256WithRSAEncryption');
$certificateContent = $x509->saveX509($result);
return $certificateContent;
}

/**
* Generate 74 digits serial number represented in the string.
* @return string
*/
private static function generateSerialNumber(): string
{
// prevent the first number from being 0
$result = rand(1, 9);
for ($i = 0; $i < 74; $i++)
{
$result .= rand(0, 9);
}
return $result;
}

public static function buildCertificateEFromDetails(string $exponent, string $modulus, string $content = null): Certificate
{
return self::buildCertificateFromDetails(Certificate::TYPE_E, $exponent, $modulus, $content);
}

public static function buildCertificateXFromDetails(string $exponent, string $modulus, string $content = null): Certificate
{
return self::buildCertificateFromDetails(Certificate::TYPE_X, $exponent, $modulus, $content);
}

private static function buildCertificateFromDetails(string $type, string $exponent, string $modulus, string $content = null): Certificate
{
$rsa = new RSA();
$rsa->loadKey([
'n' => new BigInteger($modulus, 256),
'e' => new BigInteger($exponent, 256),
]);
$publicKey = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
$privateKey = $rsa->getPrivateKey(RSA::PUBLIC_FORMAT_PKCS1);
$certificate = new Certificate($type, $publicKey, $privateKey, $content);
return $certificate;
}
public static function buildCertificateA(string $publicKey, string $privateKey, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_A, $publicKey, $privateKey, $content);
}

public static function buildCertificateE(string $publicKey, string $privateKey = null, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_E, $publicKey, $privateKey, $content);
}

public static function buildCertificateX(string $publicKey, string $privateKey = null, string $content = null): Certificate
{
return new Certificate(Certificate::TYPE_X, $publicKey, $privateKey, $content);
}

public static function generateCertificateAFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_A, $isCertified);
}

public static function generateCertificateEFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_E, $isCertified);
}

public static function generateCertificateXFromKeys(array $keys, bool $isCertified): Certificate
{
return self::generateCertificateFromKeys($keys, Certificate::TYPE_X, $isCertified);
}

public static function buildCertificateEFromDetails(string $exponent, string $modulus, string $content = null): Certificate
{
return self::buildCertificateFromDetails(Certificate::TYPE_E, $exponent, $modulus, $content);
}

public static function buildCertificateXFromDetails(string $exponent, string $modulus, string $content = null): Certificate
{
return self::buildCertificateFromDetails(Certificate::TYPE_X, $exponent, $modulus, $content);
}

private static function generateCertificateFromKeys(array $keys, string $type, bool $isCertified): Certificate
{
if ($isCertified) {
$certificateContent = self::generateCertificateContent($keys, $type);
}
return new Certificate($type, $keys['publickey'], $keys['privatekey'], $certificateContent ?? null);
}

private static function generateCertificateContent(array $keys, string $type): string
{
$privateKey = new RSA();
$privateKey->loadKey($keys['privatekey']);

$publicKey = new RSA();
$publicKey->loadKey($keys['publickey']);
$publicKey->setPublicKey();

$generator = X509GeneratorFactory::create();
return $generator->generateX509($privateKey, $publicKey, [
'type' => $type,
]);
}

private static function buildCertificateFromDetails(string $type, string $exponent, string $modulus, string $content = null): Certificate
{
$rsa = new RSA();
$rsa->loadKey([
'n' => new BigInteger($modulus, 256),
'e' => new BigInteger($exponent, 256),
]);
$publicKey = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1);
$privateKey = $rsa->getPrivateKey(RSA::PUBLIC_FORMAT_PKCS1);
return new Certificate($type, $publicKey, $privateKey, $content);
}
}
Loading

0 comments on commit 45fe2f6

Please sign in to comment.