Skip to content

Instantly share code, notes, and snippets.

@kernusr
Created April 15, 2021 22:26
Show Gist options
  • Save kernusr/aded2c230a05776bdbd456320f6ae33f to your computer and use it in GitHub Desktop.
Save kernusr/aded2c230a05776bdbd456320f6ae33f to your computer and use it in GitHub Desktop.
Пример чтения подписи из файла
{
"require": {
"sop/asn1": "^4.1",
"webmasterskaya/x509": "dev-master"
},
"minimum-stability": "dev"
}
<?php
use Sop\ASN1\Element;
use Sop\ASN1\Type\Constructed\Sequence;
use Webmasterskaya\X509\Certificate\Certificate;
require_once __DIR__ . './vendor/autoload.php';
$content = file_get_contents(__DIR__ . '/Polozhenie.pdf');
$regexp
= '#ByteRange\[\s*(\d+) (\d+) (\d+)#'; // subexpressions are used to extract b and c
$result = [];
preg_match_all($regexp, $content, $result);
if (isset($result[2]) && isset($result[3]) && isset($result[2][0])
&& isset($result[3][0])
) {
$start = $result[2][0];
$end = $result[3][0];
if ($stream = fopen(__DIR__ . '/Polozhenie.pdf', 'rb')) {
$signature = stream_get_contents(
$stream, $end - $start - 2, $start + 1
); // because we need to exclude < and > from start and end
fclose($stream);
}
if (!empty($signature)) {
$binary = hex2bin($signature);
$seq = Sequence::fromDER($binary);
$signed_data = $seq->getTagged(0)->asExplicit()->asSequence();
$ecac = $signed_data->getTagged(0)->asImplicit(Element::TYPE_SET)
->asSet();
/** @var Sop\ASN1\Type\UnspecifiedType $ecoc */
$ecoc = $ecac->at($ecac->count() - 1);
$cert = Certificate::fromASN1($ecoc->asSequence());
foreach ($cert->tbsCertificate()->subject()->all() as $attr) {
/** @var Webmasterskaya\X501\ASN1\AttributeTypeAndValue $atv */
$atv = $attr->getIterator()->current();
echo $atv->type()->typeName() . ' : ' . $atv->value()->stringValue() . PHP_EOL;
}
}
}
@Leon2110
Copy link

Leon2110 commented Apr 16, 2021

Проверил несколько подписанных pdf, везде отработало отлично.
Для полного идеала еще бы получать дату подписи и серийный номер.
Снимок

@kernusr
Copy link
Author

kernusr commented Apr 16, 2021

$cert->tbsCertificate()->validity()->notBefore()->dateTime()->format('d-m-Y H:i:s'); // Дата начала
$cert->tbsCertificate()->validity()->notAfter()->dateTime()->format('d-m-Y H:i:s'); // Дата окончания

@kernusr
Copy link
Author

kernusr commented Apr 16, 2021

$cert->tbsCertificate()->serialNumber(); // Серийный номер

Как извлечь отпечаток - не знаю. Я его не нашёл в объекте подписи =(

@Leon2110
Copy link

Спасибо за даты. Отпечаток я тоже на нашел, но вместо него можно серийный номер. Серийный номер присутствует еще до преобразования в hex, если вардампить $cert->tbsCertificate() мы сверху видим _serialNumber, я думаю это оно, вот только для этого поля нужно сделать все в обратном направлении включая обратно преобразовать в bin2hex. Это пока догадки, я не знаю как обратить некоторые действия.

@kernusr
Copy link
Author

kernusr commented Apr 16, 2021

Не понял, в чем проблема с серийником.
Я метод выше дал. Он возвращает неверные данные? Или о чем речь?

@Leon2110
Copy link

Да если сравнивать с https://dss.cryptopro.ru/Verify/Verify/ серийник не совпадает, думаю над этим.

@kernusr
Copy link
Author

kernusr commented Apr 16, 2021

Теперь понял!
Они различаются чисто представлением. То что показывает криптопро - 16-битной представление, а то что возвращает класс - простой int
Можно конвертировать!

function dec2hex($number)
{
    $hexvalues = array('0','1','2','3','4','5','6','7',
               '8','9','A','B','C','D','E','F');
    $hexval = '';
     while($number != '0')
     {
        $hexval = $hexvalues[bcmod($number,'16')].$hexval;
        $number = bcdiv($number,'16',0);
    }
    return $hexval;
}

И потом просто сделать

echo dec2hex($cert->tbsCertificate()->serialNumber());

Вернёт 5E110D84305C855577E75FBFC50F92B71AF6ACF6

@Leon2110
Copy link

Класс, потребовалось включить расширение в php "bcmath", но это работает, то что нужно, имеем все необходимые поля.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment