【问题标题】:How to retrieve digital signature information from PDF with PHP?如何使用 PHP 从 PDF 中检索数字签名信息?
【发布时间】:2018-03-07 21:48:00
【问题描述】:

我的应用需要从“附加”在 PDF 文件上的数字签名中检索一些数据(签名者姓名)。

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

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

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

FieldType: Signature
FieldName: Signature1
FieldFlags: 0
FieldJustification: Left

提前致谢!

【问题讨论】:

  • 你看过 fdf 函数吗?
  • 如果@Dennis 发布的解决方案对您不起作用,请告诉我,我想试一试这个问题
  • 您是否考虑过将 c 代码添加为 php 的扩展?
  • 嗨@TarunLalwani,丹尼斯的答案几乎是完整的,但现在我在使用 PHPASN1 进行 DER 解码的 ASN1 树上导航有些困难
  • 分享你当前的代码,我会简短地介绍一下

标签: php pdf digital-signature pkcs#7


【解决方案1】:

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

首先请阅读article about digital signature in Adobe PDF

其次,读完你会知道根据/ByteRange[a b c d]指标,签名存储在b和c字节之间

第三,我们可以从文档中提取 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));
}

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

openssl pkcs7 -in signature.pkcs7 -inform DER -print_certs > info.txt

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

另请参考this questionthis questionthis topic

于 2017 年 10 月 9 日编辑 我故意建议你看exactly this question 您可以根据需要调整代码。

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;

我已经为你调整好了,其余的请你自己做。

【讨论】:

  • 不幸的是,preg_match_all 无法使用我的签名:docdroid.net/oVB1AW2
  • @celsown 为什么您不费心查看 test.pdf 文件的内容?你的 ByteRange 指标有点不同,你只需要稍微改变一下$regexp = '#ByteRange\s*\[(\d+) (\d+) (\d+)#';
  • 新的正则表达式有效,谢谢。现在我正在尝试使用 PHPASN1 来解码 DER,因为显然 PHP openssl 不适用于 DER
  • @TonyChiboucas 是的,谢谢。但我宁愿为此问题使用 3rd 方库实现 java 服务。并通过 shell 命令使用它
  • 如果你打算通过 shell 调用它,为什么不直接写一个 shell 脚本呢?
【解决方案2】:

我使用过 iText,发现它非常可靠,我强烈推荐它。 您始终可以将 Java 代码称为 PHP 中的“微服务”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 2015-01-25
    • 2022-01-08
    • 2019-12-27
    • 1970-01-01
    • 1970-01-01
    • 2016-08-04
    相关资源
    最近更新 更多