0 幾個重要概念
全局加密密鑰,簡稱爲全局密鑰,該全局密鑰由加密字典對象和你輸入的口令生成,用來生成對象加密密鑰。
對象加密密鑰,簡稱爲對象密鑰,是用來實際加密PDF文件內容的密鑰,不同於上篇PDF加密仿真裏的全部的對象共用一個密鑰不同,PDF文件中每個對象的加密密鑰都不一樣。
User Password 用戶口令。
Owner password 權限口令。
1 PDF文件的加密流程
上一篇文章大致介紹了關於ACROBAT對PDF加密操作和加密後生成的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規範。這上面的加密字典條目中除了條目O和U之外其它的都可以直接生成。那麼接下來我們就來看這兩個條目,全局加密密鑰,和對象加密密鑰的生成過程。
如果你去看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數組中的第一個32位ID字串傳入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 用RC4和AES對數據進行加密算法:
1.獲得字符串對象或流對象的對象號(object number)和產生號(generation numer), 如果字符串對象是一個直接對象,則利用其包含該對象的標識符。
2.將對象號和產生號作2進制整數對待,將原始的N字節長的密匙擴展到n+5字節,即將對象號的低3個字節和產生號的低2個字節依次接在前面N字節長的加密K密鑰上, 順序爲低字節靠前。(如果密鑰的長度爲40那麼n爲5,如果V的值大於1, n的值就爲Length除以8。)
3.初始化MD5哈希函數,然後將步驟2產生的字符串輸入到MD5中。
4.用前(n+5)個字節,如果N+5>16那麼截取前面的16個字節,將這個產生的hash結果作爲RC4和AES對稱加密算法的密匙來對該字符串或流對象進行加密。
如果是用到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規範。整篇文章採用多種方式來表達,目的只有一個,就是讓整個內容顯得非常明白容易理解。 在兩個比較難懂的地方我用了僞碼,我想這也會對你的理解有所幫助。
當然,錯誤是在所難免的,如果你對文章有什麼意見,或者有什麼知道,或者你認爲怎樣會更好,那麼我非常希望你能給我來信或者給我評論和留言。
非常感謝!