问题描述
我使用 signtool
生成了一个 PKCS#7 包,其中包含:
- 留言
- 数字签名
- 签署者证书。
构建此 PKCS#7 文件的命令如下:
$signtool sign /f signer_certificate.pfx /p ∙∙∙ /fd sha512 /p7 . /p7ce Embedded /p7co 0 my_file
这会以 DER 格式 (ASN.1) 输出名为 my_file.p7
的文件。
现在我编写了一个 C++ 程序来验证我们可以从包中提取证书和消息。为此,我调用 CryptVerifyMessageSignature
:
#include <windows.h>
#include <wincrypt.h>
#include <vector>
std::vector<BYTE> InputPkcs7Data;
// read input file into InputPkcs7Data (CreateFile,GetFileSizeEx,ReadFile).
// out of scope.
CRYPT_VERIFY_MESSAGE_Para Parameters = {};
Parameters.cbSize = sizeof(Parameters);
Parameters.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
Parameters.hCryptProv = NULL;
Parameters.pfnGetSignerCertificate = NULL;
Parameters.pvGetArg = NULL;
DWORD DecodedMessageLength = 0;
PCCERT_CONTEXT SignerCertificate = NULL;
BOOL Result = CryptVerifyMessageSignature(&Parameters,InputPkcs7Data.data(),InputPkcs7Data.size(),NULL,&DecodedMessageLength,&SignerCertificate);
std::vector<BYTE> DecodedMessage;
DecodedMessage.assign(DecodedMessageLength,0);
Result = CryptVerifyMessageSignature(&Parameters,DecodedMessage.data(),&SignerCertificate);
if (Result == FALSE) {
wprintf(L"Error: %lx\n",GetLastError());
}
else
{
// inspect Memory
CertFreeCertificateContext(SignerCertificate);
}
我的问题是,即使 CryptVerifyMessageSignature
成功,我进入 DecodedMessage
的内容也不是原始消息。总是有几个前导字节起初看起来像垃圾,但包含有关消息长度的信息。我设法理解 DecodedMessage
的顺序如下,具体取决于原始消息的长度:
- OriginalMessageLength ⩽ 127 字节:
- 0x04
- 表示消息长度的一个字节
- 消息
- 128 字节 ⩽ OriginalMessageLength ⩽ 255 字节:
- 0x04
- 0x81
- 表示消息长度的一个字节
- 消息
- 256 字节 ⩽ OriginalMessageLength ⩽ 65535 字节:
- 0x04
- 0x82
- 两个字节表示消息的长度,以 Big Endian 表示
- 消息
- 65536 字节 ⩽ OriginalMessageLength ⩽ 16777215 字节:
- 0x04
- 0x83
- 三个字节表示消息的长度,以 Big Endian 表示
- 消息
- OriginalMessageLength ⩾ 16777216 字节:
- 0x04
- 0x84
- 四个字节,以 Big Endian 表示消息的长度
- 消息。 (我没有尝试处理大于 4GiB 的消息。
这真的出乎我的意料。这个“头信息”从何而来,有什么方法可以避免在调用 CryptVerifyCertificate 时得到它?
是否可能 signtool
不是生成签名消息的正确工具?
解决方法
解码后的消息似乎是一个 OCTET STRING
,根据 https://en.wikipedia.org/wiki/X.690#DER_encoding 使用可分辨编码规则进行编码。 04 是 OCTET STRING 的标签号。