当前位置: 首页 > 工具软件 > PDFium > 使用案例 >

关于PDF加密的算法【PDFium笔记】

许阿苏
2023-12-01

下面代码是居于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一下,具体解释有空再填。

 类似资料: