1. 程式人生 > >PDF加密、解密內幕(二)- PDF檔案口令加密內幕

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(異或)運算得到的。

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陣列中的第一個32ID字串傳入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.1RC4AES對資料進行加密演算法:

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規範。整篇文章採用多種方式來表達,目的只有一個,就是讓整個內容顯得非常明白容易理解。在兩個比較難懂的地方我用了偽碼,我想這也會對你的理解有所幫助。

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

非常感謝!