如何使用 PHP 从 PDF 中检索数字签名信息?
How to retrieve digital signature information from PDF with PHP?
我的应用程序需要从 PDF 文件的数字签名 "attached" 中检索一些数据(签名者姓名)。
我只在 Java 和 C# 中找到了使用 iText class AcroFields 方法 GetSignatureNames
的示例
编辑: 我试过 pdftk 与 dump_data_fields 和 generate_fpdf 结果是 (不幸的是):
/Fields [
<<
/V /dftk.com.lowagie.text.pdf.PdfDictionary@3048918
/T (Signature1)
>>]
和
FieldType: Signature
FieldName: Signature1
FieldFlags: 0
FieldJustification: Left
提前致谢!
好吧,仅通过 PHP.
就可以做到这一点很复杂(我什至可以说是不可能的,但谁知道呢)
首先,请阅读article about digital signature in Adobe PDF
其次,阅读本文后你会知道签名存储在b和c字节之间根据/ByteRange[a b c d]指标
第三,我们可以从文档中提取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 question, and this topic
编辑于 2017-10-09
我故意建议你看 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;
我已经帮你调整好了,剩下的请你自己解决。
我用过iText,发现它非常可靠,强烈推荐。
您始终可以从 PHP.
调用 java 代码作为 "microservice"
这是我在 PHP7 中的工作代码:
<?php
require_once('vendor/autoload.php');
use Sop\ASN1\Type\Constructed\Sequence;
use Sop\ASN1\Element;
use Sop\X509\Certificate\Certificate;
$currentFile = "./upload/test2.pdf";
$content = file_get_contents($currentFile);
$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($currentFile, 'rb')) {
$signature = stream_get_contents($stream, $end - $start - 2, $start + 1); // because we need to exclude < and > from start and end
fclose($stream);
}
$binaryData = hex2bin($signature);
$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;
}
我的应用程序需要从 PDF 文件的数字签名 "attached" 中检索一些数据(签名者姓名)。
我只在 Java 和 C# 中找到了使用 iText class AcroFields 方法 GetSignatureNames
的示例编辑: 我试过 pdftk 与 dump_data_fields 和 generate_fpdf 结果是 (不幸的是):
/Fields [
<<
/V /dftk.com.lowagie.text.pdf.PdfDictionary@3048918
/T (Signature1)
>>]
和
FieldType: Signature
FieldName: Signature1
FieldFlags: 0
FieldJustification: Left
提前致谢!
好吧,仅通过 PHP.
就可以做到这一点很复杂(我什至可以说是不可能的,但谁知道呢)首先,请阅读article about digital signature in Adobe PDF
其次,阅读本文后你会知道签名存储在b和c字节之间根据/ByteRange[a b c d]指标
第三,我们可以从文档中提取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 question,
编辑于 2017-10-09 我故意建议你看 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;
我已经帮你调整好了,剩下的请你自己解决。
我用过iText,发现它非常可靠,强烈推荐。 您始终可以从 PHP.
调用 java 代码作为 "microservice"这是我在 PHP7 中的工作代码:
<?php
require_once('vendor/autoload.php');
use Sop\ASN1\Type\Constructed\Sequence;
use Sop\ASN1\Element;
use Sop\X509\Certificate\Certificate;
$currentFile = "./upload/test2.pdf";
$content = file_get_contents($currentFile);
$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($currentFile, 'rb')) {
$signature = stream_get_contents($stream, $end - $start - 2, $start + 1); // because we need to exclude < and > from start and end
fclose($stream);
}
$binaryData = hex2bin($signature);
$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;
}