PDF加密、解密內幕(二)- PDF文件口令加密內幕

0 幾個重要概念

全局加密密鑰,簡稱爲全局密鑰,該全局密鑰由加密字典對象和你輸入的口令生成,用來生成對象加密密鑰。

對象加密密鑰,簡稱爲對象密鑰,是用來實際加密PDF文件內容的密鑰,不同於上篇PDF加密仿真裏的全部的對象共用一個密鑰不同,PDF文件中每個對象的加密密鑰都不一樣。

User Password 用戶口令。

Owner password 權限口令。

1 PDF文件的加密流程

上一篇文章大致介紹了關於ACROBATPDF加密操作和加密後生成的PDF文件中的加密字典對象。同時我們提出了下面的四個問題:

1.         文件的內容是如何被加密的?

2.         加密密鑰是如何生成的?

3.         加密字典中的每一項是如何生成的?

4.         加密字典和加密密鑰的關係?

第一個問題是我們的最終目的,本文要最終要解決的就是這個問題。而第2,3,4個問題作爲第1個問題的分解,在下文中有個清晰地解答。

       下面是一個PDF文件加密的大致過程:

1.         選擇PDF兼容版本和輸入用戶口令,權限口令。

2.         根據輸入生成加密字典對象。

3.         由輸入和加密字典對象生成全局密鑰。

4.         由全局密鑰生成對象加密密鑰。

5.         用對象加密密鑰加密對象內容。

2 PDF加密解密流程詳解

加密字典對象一般有下面的條目:

/Length 密鑰的長度

/Filter 生成密鑰的方法,就是前面說的security handler

/O 是由用戶口令和權限口令得到的,用來生成密鑰和驗證輸入的權限口令

/P 訪問權限的標誌

/R 標準加密的(standard security handler)的版本

/U由用戶口令生成,用來驗證輸入的用戶口令或權限口令,是否要提醒用戶輸入密碼

/V  可選,用來指明加密的算法。

一般來說這上面的條目是必須的,其它條目具體見PDF規範。這上面的加密字典條目中除了條目OU之外其它的都可以直接生成。那麼接下來我們就來看這兩個條目,全局加密密鑰,和對象加密密鑰的生成過程。

如果你去看PDF規範,在有的步驟會有點迷茫的,相信下面的詳細介紹對大家會有所幫助,如果你要實現自己的加密程序,也將給你一個指引。

2.1 生成加密字典和加密密鑰

2.1.1 生成條目O

算法3.3

1.         補充或截取權限口令字符串爲32個字節。如果輸入的口令大於32個字節,那麼只保留前32個字節,如果少於32個字節,那麼就按下面的字符串補上所缺少的字節數:

<0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,

  0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,

  0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,

0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a>

如果沒有權限(主)口令,那麼就用用戶口令替代。

2.      初始化MD5函數並將步驟1產生的結果輸入MD5函數。

3.      (版本3或更高)連續做50次:此後輸出作爲輸入新的MD5哈希函數中。

4.      利用這個HASH數列的前面n位創建RC4密鑰,對於版本2來說,n始終爲5, 但對於版本3或更高版本來說,取決於加密字典中Length的值,length/8

5.      按照算法3.2中的第一步由用戶口令得到32字節字符串。

6.      將第5步中產生的32位字節字符串用第4步中產生的密鑰用RC4算法加密。

7.      (版本3或更高)19次:用前一次的輸出作爲下一次的輸入進行編碼;密鑰是由第1步產生的原始密鑰的單個字節和循環數和進行XOR(異或)運算得到的。

char Test[32] = 1步生成的32字符串。

char temKey = Test[32];

unsigned int keyLength = length/8;

for (i = 1; i <=19; i++)

{

     for (j = 0; j < keyLength; ++j)

     {

         tmpKey[j] = fileKey[j] ^ i;

    }

    rc4InitKey(tmpKey, keyLength, fState);

    fx = fy = 0;

    for (j = 0; j < 32; ++j)

     {

         test[j] = rc4EncryptByte(fState, &fx, &fy, test[j]);

}

8.      32字節字符串即爲加密字典對象條目O的值。

圖1

2.1.2 得到全局加密密鑰

算法2:

1 補充或截取口令字符串爲32個字節。如果輸入的口令大於32個字節,那麼只保留前32個字節,如果少於32個字節,那麼就按下面的字符串補上所缺少的字節數:

<0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,

  0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,

  0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,

0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a>

如果用戶口令爲空,那麼就意味着沒有用戶口令,用這32個字節完全填充。

2.初始化MD5函數並將步驟1產生的結果輸入MD5函數。

3.將加密字典O條目輸入MD5中。(算法3.3描述O值的產生)。

4.將P條目作爲無符號的4字節整數然後將該4字節輸入MD5函數。

5.將該PDF文檔的ID標識數組的第一個元素(即該PDF文檔中trailer字典中ID條目的第一個字符串)輸入到函數MD5中。(關於PDF文檔ID的介紹見文章“PDF文檔ID”)

6. (版本3或更高)如果該文檔的metadata不加密,將4字節的0xFFFFFFFF輸入哈希函數MD5中。

7.結束散列。

8(版本3或更高)將前面MD5產生的HASH序列的前n位,也就是加密字典中的Length/8位字串,輸入到新的MD5哈希函數中,此後輸出作爲輸入連續再做49次。

9.加密密鑰就是這個HASH數列的前面n位,對於版本2來說,n始終爲5,對於版本3或更高版本來說,就取決於加密字典中Length的值,n=length/8

這個算法,是根據用戶口令字符串,產生全局加密密鑰

圖2

2.1.3 生成條目U

算法4 版本2:

1 按照算法2的方法,基於用戶口令字符串生成加密密鑰。

2 用上面步驟產生的加密密鑰加密按照算法2步驟1生成的32位串。

3 步驟2的輸出即爲加密字典對象中條目U的值。

 

算法5 版本3

1按照算法2的方法,基於用戶口令字符串生成加密密鑰。

2 初始化MD5 HASH函數並將按照算法2步驟1生成的32位字符串輸入該函數。

3 將文件ID數組中的第一個32ID字串傳入MD5函數。

4 用第1步產生的密鑰通過RC4算法來加密由步驟3輸出的16位字符串。

9.      19次:用前一次的輸出作爲下一次的輸入進行解碼;密鑰是由第1步產生的原始密鑰的單個字節和循環數和進行XOR(異或)運算得到的。

char Test[32] = 1步生成的32字符串。

char temKey = Test[32];

unsigned int keyLength = length/8;

for (i = 1; i <=19; i++)

{

     for (j = 0; j < keyLength; ++j)

     {

         tmpKey[j] = fileKey[j] ^ i;

    }

    rc4InitKey(tmpKey, keyLength, fState);

    fx = fy = 0;

    for (j = 0; j < 32; ++j)

     {

         test[j] = rc4EncryptByte(fState, &fx, &fy, test[j]);

}

 

2.2 生成對象加密密鑰並將對象加密

算法3.1 RC4AES對數據進行加密算法:

1.獲得字符串對象或流對象的對象號(object number)和產生號(generation numer), 如果字符串對象是一個直接對象,則利用其包含該對象的標識符。

2.將對象號和產生號作2進制整數對待,將原始的N字節長的密匙擴展到n+5字節,即將對象號的低3個字節和產生號的低2個字節依次接在前面N字節長的加密K密鑰上, 順序爲低字節靠前。(如果密鑰的長度爲40那麼n5,如果V的值大於1 n的值就爲Length除以8。)

3.初始化MD5哈希函數,然後將步驟2產生的字符串輸入到MD5中。

4.用前(n+5)個字節,如果N+5>16那麼截取前面的16個字節,將這個產生的hash結果作爲RC4AES對稱加密算法的密匙來對該字符串或流對象進行加密。

如果是用到AES算法,用到CBC模式。

                                                                              圖 3

3 回答雲木的問題

雲木的問題是這樣,只有設置了權限口令的的PDF文件,是否可以直接從PDF文件中取出某個對象的流?

他想這樣的PDF文件應該是不加密的,因爲PDF文件的內容是可以看到的,只是設置了一個用來表示權限的串。這是我上篇文章《PDF文件加密仿真》中說的,而且還說了可以有辦法去掉這個串或者修改這個串,不過我也有在文章中用紅色字體說明PDF文件中內容實際上是加密了的,只是加密密鑰可以從加密字典中直接計算出來。

那麼這個密鑰如何如何計算得到呢?我想由算法3.2和算法3.1就得出答案了。因爲沒有打開口令,那麼直接由上面的32字節加密口令常量串填充,然後根據其它文件中的相關內容得到全局密鑰。進而生成對象加密密鑰。

4 是否可以直接修改權限設置串

有的人說可以直接修改加密字典中的條目P得到更大的權限,其實不然,因爲按照加密算法2,全局加密密鑰其中有個輸入就是P, 如果P修改了,也不能正確得到加密密鑰。你也可以實際試驗一下,用 Ultra Edit打開,修改P條目,然後再用PDF reader打開,就會提示你輸入口令,因爲根據該PDF文件本身是無法得到加密密鑰了。

5 本文總結

本文詳細地對PDF文件的口令加密做了一個說明,相信你可以利用它來對PDF文件進行口令加密了,當然在一些細節方面你需要查詢一下PDF規範。整篇文章採用多種方式來表達,目的只有一個,就是讓整個內容顯得非常明白容易理解。 在兩個比較難懂的地方我用了僞碼,我想這也會對你的理解有所幫助。

       當然,錯誤是在所難免的,如果你對文章有什麼意見,或者有什麼知道,或者你認爲怎樣會更好,那麼我非常希望你能給我來信或者給我評論和留言。

       非常感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章