vakata/certificate

View on GitHub
src/CRL.php

Summary

Maintainability
B
6 hrs
Test Coverage
F
0%
<?php

namespace vakata\certificate;

use vakata\asn1\ASN1;
use vakata\asn1\Decoder;
use vakata\asn1\structures\CRL as Parser;

class CRL
{
    protected $data;

    protected static function base256toHex($inp) : string
    {
        $num = ASN1::fromBase256($inp);
        $hex = '';
        for ($i = strlen($num) - 4; $i >= 0; $i-=4) {
            $hex .= dechex(bindec(substr($num, $i, 4)));
        }
        return strrev($hex);
    }

    public function __construct(Parser $data)
    {
        $this->data = $data;
    }
    public static function fromString(string $data) : CRL
    {
        return new static(Parser::fromString($data));
    }
    public static function fromFile(string $path) : CRL
    {
        return new static(Parser::fromFile($path));
    }
    public function getAuthorityKeyIdentifier()
    {
        $temp = $this->data->toArray(false, true)['tbsCertList']['extensions'];
        if (!$temp) {
            return null;
        }
        foreach ($temp as $item) {
            if ($item['extnID'] === ASN1::TextToOID('authorityKeyIdentifier')) {
                $val = $item['extnValue'];
                while (!is_string($val) && isset($val[0])) {
                    $val = $val[0];
                }
                if (is_string($val)) {
                    return static::base256toHex($val);
                }
            }
        }
        return null;
    }
    public function isSignatureValid(array $ca) : bool
    {
        $keyID = $this->getAuthorityKeyIdentifier();
        $found = null;
        if ($keyID === null) {
            $found = array_values($ca)[0] ?? null;
        } else {
            foreach ($ca as $cert) {
                if ($cert->getSubjectKeyIdentifier() === $keyID) {
                    $found = $cert;
                    break;
                }
            }
        }
        if (!$found) {
            throw new CertificateException('CA not found');
        }
        $lazy = $this->data->toArray(false, true);
        $signed = $lazy->rawData()['tbsCertList'];
        $signatureValue = $lazy['signatureValue'];
        $signatureAlgorithm = $lazy['signatureAlgorithm']['algorithm'];
        return Signature::verify(
            $this->data->getReader()->chunk($signed['start'], $signed['length']),
            substr($signatureValue, 1),
            $found->getPublicKey(),
            $signatureAlgorithm
        );
    }
    public function revoked(bool $extensions = false)
    {
        $temp = $this->data->toArray(false, true)['tbsCertList'];
        if (isset($temp['revokedCertificates'])) {
            foreach ($temp['revokedCertificates']->rawData() as $v) {
                $cert = Decoder::fromString($this->data->getReader()->chunk($v['start'], $v['length']))
                    ->map(Parser::map()['children']['tbsCertList']['children']['revokedCertificates']['repeat']);
                $reason = 0;
                foreach ($cert['extensions'] ?? [] as $ext) {
                    if ($ext['extnID'] === '2.5.29.21') {
                        while (is_array($ext['extnValue'])) {
                            $ext['extnValue'] = array_values($ext['extnValue'])[0];
                        }
                        $reason = (int)$ext['extnValue'];
                    }
                }
                if (!$extensions) {
                    unset($cert['extensions']);
                }
                if ($reason !== 8) {
                    $cert['reason'] = $reason;
                    yield $cert;
                }
            }
        }
    }
    public function isRevoked(string $cert, int $time = null)
    {
        $time = $time ?? time();
        foreach ($this->revoked() as $c) {
            if (trim($c['userCertificate'], '0 ') === trim($cert, '0 ')) {
                return $c['revocationDate'] <= $time;
            }
        }
        return false;
    }
}