Как извлечь и проверить подпись PDF (PKCS7) с помощью openssl? - PullRequest
0 голосов
/ 25 января 2019

Я хотел бы обнаружить подписанные PDF-файлы в PHP и проверить, действительна ли подпись.Из этого документа я написал этот код PHP ниже.

Что он делает:

  1. Извлечение кода PKCS7 (это работает, потому что я могу получить подробности из Openssl)
  2. Вычисление хэша SHA256 документа.

В конце у меня есть файл PKCS7 и SHA256.

Теперь я хотел бы проверить свою подпись на моем файле PKCS7.Как я могу это сделать?Сначала я посмотрел на digest_enc_alg / sha256WithRSAEncryption / enc_digest, но, похоже, это не то, что я ищу.

class VerifyPDF
{
    public static function getByteRange($filename)
    {
        $content = file_get_contents($filename);
        if (!preg_match_all('/ByteRange\[\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\]/',
            $content, $matches))
        {
            throw new \Exception('Unable to get certificate');
        }

        return [
            intval($matches[1][0]), // Offset of the first part (usually 0)
            intval($matches[2][0]), // Size of the first part
            intval($matches[3][0]), // Offset to the second part
            intval($matches[4][0])  // Size of the second part
        ];
    }

    public static function get_pkcs7($filename)
    {
        [$o1, $l1, $o2, $l2] = self::getByteRange($filename);

        if (!$fp = fopen($filename, 'rb')) {
            throw new \Exception("Unable to open $filename");
        }

        $signature = stream_get_contents($fp, $o2 - $l1 - 2, $l1 + 1);
        fclose($fp);

        file_put_contents('out.pkcs7', hex2bin($signature));
    }

    public static function compute_hash($filename)
    {
        [$o1, $l1, $o2, $l2] = self::getByteRange($filename);

        if (!$fp = fopen($filename, 'rb')) {
            throw new \Exception("Unable to open $filename");
        }

        $i = stream_get_contents($fp, $l1, $o1);
        $j = stream_get_contents($fp, $l2, $o2);

        if (strlen($i) != $l1 || strlen($j) != $l2) {
            throw new \Exception('Invalid chunks');
        }

        fclose($fp);

        return hash('sha256', $i . $j);
    }
}

Хэш, который я получаю:

5036ae43aba11ce626f6f9b1d5246ba0700e217655b9ff927e31fbefadfa2182

Так вдохновленный этим Я сделал следующее:

#!/bin/bash
PKCS7='out.pkcs7'

# Extract Digest (SHA256)
OFFSET=$(openssl asn1parse -inform der -in $PKCS7 | \
    perl -ne 'print $1 + $2 if /(\d+):d=\d\s+hl=(\d).*?256 prim.*HEX DUMP/m')
dd if=$PKCS7 of=signed-sha256.bin bs=1 skip=$OFFSET count=256

# Extract Public key 
openssl pkcs7 -print_certs -inform der -in $PKCS7 | \
    tac | sed '/-----BEGIN/q' | tac > client.pem
openssl x509 -in client.pem -pubkey -noout > client.pub.pem

# Verify the signature
openssl rsautl -verify -pubin -inkey client.pub.pem < signed-sha256.bin > verified.bin

# Get Hash and compare with the computed hash from the PDF
openssl asn1parse -inform der -in verified.bin | grep -Po '\[HEX DUMP\]:\K\w+$' | tr A-F a-f

Что даетмне это:

C8581962753927BB57B66B1D0D0F4B33A29EF3E03DA12D2329DB72763AC7EDB6

Так что к сожалению по двум хэшам не совпадают ...

Я что-то упустил?

1 Ответ

0 голосов
/ 28 января 2019

В блоге , на который вы вдохновились , показаны следующие рисунки, объясняющие структуру контейнера подписи PKCS # 7

screen shot

На самом деле это только самая простая структура, определенная PKCS # 7.Если вы посмотрите на SignerInfo спецификацию (content - signerInfos - SignerInfo), вы увидите

   SignerInfo ::= SEQUENCE {
     version Version,
     issuerAndSerialNumber IssuerAndSerialNumber,
     digestAlgorithm DigestAlgorithmIdentifier,
     authenticatedAttributes
       [0] IMPLICIT Attributes OPTIONAL,
     digestEncryptionAlgorithm
       DigestEncryptionAlgorithmIdentifier,
     encryptedDigest EncryptedDigest,
     unauthenticatedAttributes
       [1] IMPLICIT Attributes OPTIONAL }

( RFC 2315 раздел 9.2 "Тип SignerInfo" )

В частности, есть ДОПОЛНИТЕЛЬНАЯ authenticatedAttributes, которую вы не найдете на скриншоте выше.Но в любом текущем профиле подписи, к которому следует относиться серьезно, эти authenticatedAttributes (он же подписанные атрибуты) действительно необходимы!

Кроме того, если в информационном объекте подписавшего контейнера подписи PKCS # 7 есть authenticatedAttributes, зашифрованныйдайджестом является не дайджестом данных документа, а дайджестом структуры authenticatedAttributes.В этом случае дайджест данных документа сохраняется как значение определенного подписанного атрибута, атрибута «messageDigest».Таким образом, в этом случае вы пытаетесь извлечь неправильное значение, чтобы сравнить дайджест документа с.

Например, в случае примера документа, которым вы поделились в своем последующем вопросе , есть authenticatedAttributes, поэтому вдохновляющий блог привел вас в заблуждение.

...