下面代码是居于PDFium源码改动调试(源码也有类似的实现),自己实现一遍,加深理解一下
这个是Revision<=4的写法(目前MAC OS X10.15下的Preview.app还是是使用这种方法导出的),Revision>=5是AES256计算,不在这里讨论。
1.先计算O值,O值计算是对应Algorithm 3.3
//计算O值
ByteString CPDF_SecurityHandler::CalOwnerValue(const ByteString& owner_password) const {
//参考OnCreateInternal函数
size_t okeylen = 32;
uint8_t passcode[32];
GetPassCode(owner_password, passcode);
uint8_t digest[16];
CRYPT_MD5Generate(passcode, digest);
if (m_Revision >= 3) {
for (uint32_t i = 0; i < 50; i++)
CRYPT_MD5Generate(digest, digest);
}
uint8_t enckey[32] = {};
size_t copy_len = std::min(m_KeyLen, sizeof(digest));
memcpy(enckey, digest, copy_len);
//printf("m_KeyLen=%d\n",(int)m_KeyLen);
uint8_t tt[32] = {};
memcpy(tt, passcode, okeylen);
pdfium::span<uint8_t> test_span(tt, okeylen);
for (size_t i=0; i<=19; i++)
{
uint8_t tempkey[32] = {};
for (size_t j = 0; j < m_KeyLen; j++)
tempkey[j] = enckey[j] ^ static_cast<uint8_t>(i);
CRYPT_ArcFourCryptBlock(test_span, {tempkey, m_KeyLen});
}
return ByteString(tt, okeylen);
}
2.计算U值(CalcEncryptKey源码里有,作用是计算秘钥,对应描述在PDF Reference 1.7中Algorithm 3.2 Computing an encryption key)U值计算是对应Algorithm 3.4+3.5
//注意:pEncrypt需要先调好O值
ByteString CPDF_SecurityHandler::CalUserValue(const CPDF_Dictionary* pEncrypt,const ByteString& password,bool bIgnoreEncryptMeta,ByteString fileId) const
{
//参考OnCreateInternal函数
//1. generate key
uint8_t key[32]={0};
CalcEncryptKey(pEncrypt, password, key, m_KeyLen,bIgnoreEncryptMeta, fileId);
/*
printf("key--------");
for (int i=0; i<(int)m_KeyLen; i++) {
printf("%02x",key[i]&0xFF);
}
printf("--------\n");
*/
if (m_Revision == 2) {
uint8_t ukeybuf[32];
memcpy(ukeybuf, kDefaultPasscode, sizeof(kDefaultPasscode));
CRYPT_ArcFourCryptBlock(ukeybuf, {key, m_KeyLen});
return ByteString(ukeybuf,32);
}
//2.Initialize the MD5 hash function and pass the 32-byte padding string shown in step 1 of Algorithm 3.2 as input to this function.(原始的样式,不需要用密码填充)
uint8_t passcode[32]={0};
//GetPassCode(password, passcode);
memcpy(passcode, kDefaultPasscode, sizeof(kDefaultPasscode));
CRYPT_md5_context md5 = CRYPT_MD5Start();
CRYPT_MD5Update(&md5, passcode);
//3.Pass the first element of the file’s file identifier array (the value of the ID entry in the document’s trailer dictionary; see Table 3.13 on page 97) to the hash function and finish the hash. (See implementation note 26 in Appendix H.)
/*
trailer
<< /Size 107 /Root 105 0 R /Encrypt 106 0 R /Info 1 0 R /ID [ <26ff8d7146e2e2b3f8b052643eaa192c>
<26ff8d7146e2e2b3f8b052643eaa192c> ] >>
应该是<26ff8d7146e2e2b3f8b052643eaa192c>
*/
if (!fileId.IsEmpty())
{
CRYPT_MD5Update(&md5, fileId.raw_span());
}
uint8_t digest[16];
CRYPT_MD5Finish(&md5, digest);
//pdfium::span<uint8_t> test_span(passcode, sizeof(kDefaultPasscode));
//CRYPT_ArcFourCryptBlock(digest, {key, m_KeyLen});
/*
4. Encrypt the 16-byte result of the hash, using an RC4 encryption function with the encryption key from step 1. (i=0)
5. Do the following 19 times: Take the output from the previous invocation of the RC4 function and pass it as input to a new invocation of the function; use an en- cryption key generated by taking each byte of the original encryption key (ob- tained in step 1) and performing an XOR (exclusive or) operation between that byte and the single-byte value of the iteration counter (from 1 to 19). (i=1->19)
*/
uint8_t tt[32] = {0};
memcpy(tt, digest, m_KeyLen);
pdfium::span<uint8_t> test_span(tt, m_KeyLen);
for (size_t i=0; i<=19; i++)
{
uint8_t tempkey[32] = {};
for (size_t j = 0; j < m_KeyLen; j++)
tempkey[j] = key[j] ^ static_cast<uint8_t>(i);
CRYPT_ArcFourCryptBlock(test_span, {tempkey, m_KeyLen});
}
return ByteString(tt, 32);
}
先mark一下,具体解释有空再填。