如何使用PHP从PDF检索数字签名信息?

cel*_*owm 21 php pdf digital-signature pkcs#7

我有app需要从PDF文件上的"附加"数字签名中检索一些数据(签名者名称).

我在Java和C#中只找到了使用iText类AcroFields方法GetSignatureNames的示例

编辑:我用dump_data_fields和generate_fpdf 尝试了pdftk,结果是(不幸的):

/Fields [
<<
/V /dftk.com.lowagie.text.pdf.PdfDictionary@3048918
/T (Signature1)
>>]
Run Code Online (Sandbox Code Playgroud)

FieldType: Signature
FieldName: Signature1
FieldFlags: 0
FieldJustification: Left
Run Code Online (Sandbox Code Playgroud)

提前致谢 !

Den*_*mov 16

嗯,这很复杂(我会说甚至不可能,但谁知道)只能用PHP实现这一点.

首先,请在Adobe PDF中阅读关于数字签名的文章

其次,阅读本文后,您将知道签名存储在b和c字节之间,符合/ ByteRange [abcd]指标

第三,我们可以从文档中提取b和c然后提取签名本身(指南说它将是十六进制编码的PKCS7#对象).

<?php

 $content = file_get_contents('test.pdf');

 $regexp = '#ByteRange\[\s*(\d+) (\d+) (\d+)#'; // subexpressions are used to extract b and c

 $result = [];
 preg_match_all($regexp, $content, $result);

 // $result[2][0] and $result[3][0] are b and c
 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('test.pdf', 'rb')) {
         $signature = stream_get_contents($stream, $end - $start - 2, $start + 1); // because we need to exclude < and > from start and end

         fclose($stream);
     }

     file_put_contents('signature.pkcs7', hex2bin($signature));
}
Run Code Online (Sandbox Code Playgroud)

第四步,我们在文件signature.pkcs7中有PKCS#7对象.不幸的是,我不知道使用PHP从签名中提取信息的方法.因此,您必须能够运行shell命令才能使用openssl

openssl pkcs7 -in signature.pkcs7 -inform DER -print_certs > info.txt
Run Code Online (Sandbox Code Playgroud)

在文件info.txt中运行此命令后,您将拥有一系列证书.最后一个是你需要的.您可以看到文件的结构并解析所需的数据.

请同时参考这个问题,这个问题这个主题

编辑于2017-10-09 我故意建议你看看这个问题 有一个代码,你可以根据自己的需要进行调整.

use ASN1\Type\Constructed\Sequence;
use ASN1\Element;
use X509\Certificate\Certificate;       

$seq = Sequence::fromDER($binaryData);
$signed_data = $seq->getTagged(0)->asExplicit()->asSequence();
// ExtendedCertificatesAndCertificates: https://tools.ietf.org/html/rfc2315#section-6.6
$ecac = $signed_data->getTagged(0)->asImplicit(Element::TYPE_SET)->asSet();
// ExtendedCertificateOrCertificate: https://tools.ietf.org/html/rfc2315#section-6.5
$ecoc = $ecac->at($ecac->count() - 1);
$cert = Certificate::fromASN1($ecoc->asSequence());
$commonNameValue = $cert->tbsCertificate()->subject()->toString();
echo $commonNameValue;
Run Code Online (Sandbox Code Playgroud)

我已经为你调整过了,但请你自己做好准备.

  • @celsown 为什么你懒得看一下你的 test.pdf 文件的内容?你的ByteRange指标有点不同,你只需要稍微改变`$regexp = '#ByteRange\s*\[(\d+) (\d+) (\d+)#';` (2认同)