Skip to content

Commit

Permalink
Added CRL support
Browse files Browse the repository at this point in the history
Added ExtendedKeyUsage for x509 cert
  • Loading branch information
adapik committed Mar 26, 2018
1 parent debc654 commit 49ed090
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/vendor/
.idea/
composer.lock
18 changes: 15 additions & 3 deletions example/parse_cert.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
$certificate = \Adapik\CMS\Certificate::createFromContent(file_get_contents('php://stdin'));
$t2 = microtime(true);

echo 'Parsed in:' . ($t2 - $t1) . 's';
echo 'Parsed in: ' . ($t2 - $t1) . 's';
echo "\n";

echo 'Issuer: ' . (string) $certificate->getIssuer();
Expand All @@ -25,9 +25,21 @@
echo "\n";
echo 'CA: ' . ($certificate->isCa() ? 'true' : 'false');
echo "\n";
echo 'Certificate Policies:';
echo "\n";
foreach ($certificate->getCertPolicies() as $certPolicy) {
echo "\t- ".$certPolicy;
echo "\n";
}
echo 'Extended Key Usage:';
echo "\n";
foreach ($certificate->getExtendedKeyUsage() as $keyUsage) {
echo "\t- ".$keyUsage;
echo "\n";
}

$t3 = microtime(true);
echo 'Total:' . ($t3 - $t1) . 's';
echo 'Total: ' . ($t3 - $t1) . 's';
echo "\n";
echo 'Memory Usage:' . memory_get_peak_usage(true) . ' bytes';
echo 'Memory Usage: ' . memory_get_peak_usage(true) . ' bytes';
echo "\n";
27 changes: 27 additions & 0 deletions example/parse_crl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

$t1 = microtime(true);
$crl = \Adapik\CMS\CertificateRevocationList::createFromContent(file_get_contents('php://stdin'));
$t2 = microtime(true);

echo 'Parsed in: ' . ($t2 - $t1) . 's';
echo "\n";

echo 'Issuer: ' . (string) $crl->getIssuer();
echo "\n";
echo 'This Update: ' . (string) $crl->getThisUpdate();
echo "\n";
echo 'Next Update: ' . (string) $crl->getNextUpdate();
echo "\n";
echo 'Revoked Certificates Count: ' . count($crl->getSerialNumbers());
echo "\n";

$t3 = microtime(true);
echo 'Total: ' . ($t3 - $t1) . 's';
echo "\n";
echo 'Memory Usage: ' . memory_get_peak_usage(true) . ' bytes';
echo "\n";
1 change: 0 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
>
<php>
<ini name="error_reporting" value="-1" />
<server name="KERNEL_DIR" value="app/" />
</php>

<testsuites>
Expand Down
20 changes: 20 additions & 0 deletions src/CMS/Certificate.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Adapik\CMS\Exception\FormatException;
use FG\ASN1;
use FG\ASN1\Mapper\Mapper;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\Sequence;

/**
Expand All @@ -18,6 +19,8 @@ class Certificate
const OID_EXTENSION_AUTHORITY_INFO_ACCESS = '1.3.6.1.5.5.7.1.1';
const OID_OCSP_URI = '1.3.6.1.5.5.7.48.1';
const OID_EXTENSION_CERT_POLICIES = '2.5.29.32';
const OID_EXTENSION_KEY_USAGE = '2.5.29.15';
const OID_EXTENSION_EXTENDED_KEY_USAGE = '2.5.29.37';

/**
* @var Sequence
Expand Down Expand Up @@ -170,6 +173,9 @@ public function getSubject()
return new Name($this->getTBSCertificate()->getChildren()[5]);
}

/**
* @return string[]
*/
public function getCertPolicies()
{
$policies = $this->getExtension(self::OID_EXTENSION_CERT_POLICIES);
Expand All @@ -185,6 +191,20 @@ public function getCertPolicies()
return $oids;
}

/**
* @return string[]
*/
public function getExtendedKeyUsage()
{
$extKeyUsageSyntax = $this->getExtension(self::OID_EXTENSION_EXTENDED_KEY_USAGE);

if ($extKeyUsageSyntax === null) {
return [];
}

return array_map('strval', $extKeyUsageSyntax->getChildren());
}

/**
* @return bool
*/
Expand Down
104 changes: 104 additions & 0 deletions src/CMS/CertificateRevocationList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

declare(strict_types=1);

namespace Adapik\CMS;

use Adapik\CMS\Exception\FormatException;
use FG\ASN1;
use FG\ASN1\Mapper\Mapper;
use FG\ASN1\Universal\Sequence;

/**
* Certificate Revocation List (CRL)
*/
class CertificateRevocationList
{
/**
* @var Sequence
*/
private $sequence;

/**
* Certificate constructor.
*
* @param Sequence $object
*/
public function __construct(Sequence $object)
{
$this->sequence = $object;
}

private function getTBSCertList()
{
return $this->sequence->getChildren()[0];
}

public function getSignatureAlgorithm()
{
return (string) $this->sequence->getChildren()[1]->getChildren()[0];
}

public function getSignatureValue()
{
return (string) $this->sequence->getChildren()[2];
}

public function getIssuer()
{
$name = $this->getTBSCertList()->findChildrenByType(\FG\ASN1\Universal\Sequence::class)[1];

return new Name($name);
}

public function getThisUpdate()
{
$time = $this->getTBSCertList()->findChildrenByType(\FG\ASN1\AbstractTime::class)[0];

return (string) $time;
}

public function getNextUpdate()
{
$time = $this->getTBSCertList()->findChildrenByType(\FG\ASN1\AbstractTime::class)[1];

return (string) $time;
}

public function getSerialNumbers()
{
$revokedCerts = $this->getTBSCertList()->findChildrenByType(\FG\ASN1\Universal\Sequence::class)[2];

$numbers = array_map(function(Sequence $revokedCert) {
return gmp_strval((string) $revokedCert->getChildren()[0], 16);
}, $revokedCerts->getChildren());

return $numbers;
}

/**
* Constructor
*
* @param $content
*
* @return self
*
* @throws FormatException
*/
public static function createFromContent($content)
{
$sequence = ASN1\ASN1Object::fromFile($content);

if (!$sequence instanceof Sequence) {
throw new FormatException('CRL must be type of Sequence');
}

$map = (new Mapper())->map($sequence, Maps\CertificateList::MAP);

if ($map === null) {
throw new FormatException('CRL invalid format');
}

return new self($sequence);
}
}
62 changes: 62 additions & 0 deletions tests/CMS/CertificateRevocationListTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace CMS;

use Adapik\CMS\CertificateRevocationList;
use PHPUnit\Framework\TestCase;

/**
* Test CertificateRevocationList class
*/
class CertificateRevocationListTest extends TestCase
{
/**
* @var string
*/
private $crl;

protected function setUp()
{
$this->crl = base64_decode(file_get_contents(__DIR__ . '/../fixtures/crl.crl'));
}

protected function tearDown()
{
$this->crl = null;
}

public function testCreateFromContent()
{
$crl = CertificateRevocationList::createFromContent($this->crl);
$this->assertInstanceOf(CertificateRevocationList::class, $crl);
}

public function testGetIssuer()
{
$crl = CertificateRevocationList::createFromContent($this->crl);
$this->assertSame(
'1.2.840.113549.1.9.1: [email protected]; 2.5.4.6: RU; 2.5.4.8: 77 г. Москва; 2.5.4.7: Москва; 2.5.4.9: 125375 г. Москва, ул. Тверская, д. 7; 2.5.4.10: Минкомсвязь России; 1.2.643.100.1: 1047702026701; 1.2.643.3.131.1.1: 007710474375; 2.5.4.3: Головной удостоверяющий центр',
(string) $crl->getIssuer()
);
}

public function testGetNextUpdate()
{
$crl = CertificateRevocationList::createFromContent($this->crl);
$this->assertSame('2018-04-06T08:33:56+00:00', $crl->getNextUpdate());
}

public function testGetThisUpdate()
{
$crl = CertificateRevocationList::createFromContent($this->crl);
$this->assertSame('2018-03-07T08:33:56+00:00', $crl->getThisUpdate());
}

public function testGetSerialNumbers()
{
$crl = CertificateRevocationList::createFromContent($this->crl);
$this->assertCount(27, $crl->getSerialNumbers());
}
}
19 changes: 17 additions & 2 deletions tests/CMS/CertificateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@ class CertificateTest extends TestCase
*/
private $caCert;

public function setUp()
protected function setUp()
{
$this->userCert = base64_decode(file_get_contents(__DIR__ . '/../fixtures/cert_user.crt'));
$this->caCert = base64_decode(file_get_contents(__DIR__ . '/../fixtures/cert_ca.crt'));
}

protected function tearDown()
{
$this->userCert = null;
$this->caCert = null;
}

public function testCreate()
{
$signedData = Certificate::createFromContent($this->userCert);
Expand Down Expand Up @@ -119,10 +125,19 @@ public function testGetPolicies()
$cert = Certificate::createFromContent($this->caCert);
$this->assertSame([
'1.3.6.1.4.1.6449.1.2.2.26',
'2.23.140.1.2.1'
'2.23.140.1.2.1',
], $cert->getCertPolicies());
}

public function testGetExtendedKeyUsage()
{
$cert = Certificate::createFromContent($this->caCert);
$this->assertSame([
'1.3.6.1.5.5.7.3.1',
'1.3.6.1.5.5.7.3.2',
], $cert->getExtendedKeyUsage());
}

public function testGetBinary()
{
$cert = Certificate::createFromContent($this->caCert);
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/crl.crl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
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
Expand Down

0 comments on commit 49ed090

Please sign in to comment.