PDF加密、解密內幕(二)- PDF檔案口令加密內幕
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(異或)運算得到的。
charTest[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(異或)運算得到的。
charTest[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規範。整篇文章採用多種方式來表達,目的只有一個,就是讓整個內容顯得非常明白容易理解。在兩個比較難懂的地方我用了偽碼,我想這也會對你的理解有所幫助。
當然,錯誤是在所難免的,如果你對文章有什麼意見,或者有什麼知道,或者你認為怎樣會更好,那麼我非常希望你能給我來信或者給我評論和留言。
非常感謝!