我想在PHP中检测签名的PDF,并验证签名是否有效。从这个document开始,我在下面编写了此PHP代码。
它的作用是:
最后,我有一个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
受this的启发,我做了以下工作:
#!/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
不幸的是,两个哈希值不匹配...
我想念什么吗?
答案 0 :(得分:1)
灵感来自的blog 显示以下图形来说明PKCS#7签名容器的结构
实际上,这仅表示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 section 9.2 "SignerInfo type")
尤其是在上图中没有找到可选的authenticatedAttributes
。但是,在任何当前要认真对待的签名配置文件中,实际上都需要这些authenticatedAttributes
(又称签名属性)!
此外,如果PKCS#7签名容器签名者信息对象中有authenticatedAttributes
,则加密摘要不是文档数据摘要,而是{{ 1}}结构。在这种情况下,文档数据的摘要存储为特定签名属性(“ messageDigest”属性)的值。因此,在这种情况下,您尝试提取错误的值来与文档摘要进行比较。
例如,如果您在follow-up question中共享的示例文档中有authenticatedAttributes
,那么鼓舞人心的博客会让您误入歧途。