24位BMP位元影像檔案
要利用BMP位元影像進行加密首先需要瞭解BMP檔案的存放格式,24位真彩BMP位元影像檔案包括3
部分:
第一部分是BMP檔案頭:前2個位元組是“BM”,是用於識別BMP檔案的標誌;第3、4、5、6
位元組存放的是位元影像檔案的大小,以位元組為單位;第7、8、9、10位元組是保留的,必須為0
;第11、12、13、14位元組給出位元影像陣列相對於檔案頭的位移。
第二部分是點位元影像資訊:從第29個位元組開始,第29、30位元組描述的是像素的位元;第35
、36、37、38位元組確定映像位元組數的多少,但通常此項為空白。
第三部分是位元影像陣列:從第39個位元組開始,每3個位元組表示一個像素,這3個位元組依次表
示該像素的紅、綠、藍亮度分量值。要從位元影像檔案中“擠"出用來隱藏其他重要訊息的存
儲空間,就需要從這裡入手。
實現原理
根據亮度公式:I=0.3R+0.59G+0.11B可以知道人眼對於綠色分量最為敏感,對藍色分
量最不敏感,綠色分量值改變1個單位就相當於藍色分量改變5個、紅色分量改變2個單位
。折算為二進位,紅色分量可以改變其低兩位、綠色分量可以改變其最低位、藍色分量
可以改變其低三位。這樣就可以從描述每個像素的3個位元組中得到2+1+3=6個位元的冗
餘資訊位用於儲存其他檔案的位元流。按此比率,從一幅24位BMP位元影像中可以得到的最大
儲存空間為:((位元影像檔案大小-40)×6)/(3×8),大約是其位元影像檔案長度的1/4。
由於位元影像檔案中每3個位元組只能提供6個位元的儲存空間,所以需要12個位元組才能完整保
存源檔案的3個位元組。
本文制定的加密協議是先通過程式視窗輸入密碼,從位元影像檔案的第39個位元組即位元影像陣列
部分開始儲存,依次為:源檔案名稱、分割符“*”、源檔案的長度、分割符“*”、源
檔案的內容。其中源檔案內容的每個位元組需與密碼相異或,以起到對檔案進一步加密的
效果。
這裡需要特別指出的是:如果在網路傳輸時該檔案被截獲,從中將加密檔案恢複出來的
可能性是非常小的。首先,從外觀上加密後的檔案和一般的BMP檔案沒有任何區別,而且
加密前後位元影像檔案大小也不會發生變化,不會引起截獲者的注意。其次,由於只有大約1
/4的空間存有隱含資訊,對於被加密資訊而言,其他3/4的內容都是冗餘位。在密碼學上
,往往採取加大冗餘位的方式來提高加密的可靠性,顯然這種擁有75%的冗餘碼的加密
方法還是頗為可靠的。最後,破譯者並不知曉加密所採取的協議,而且待加密的內容都
同加密金鑰經過了異或運算,在沒有正確解密密碼的情況下也是很難破譯的。
源檔案的加密
由於進行加密處理的過程中要頻繁進行位元運算,所以選用在這方面較有優勢的Micorsoft
Visual C++ 6.0作為開發工具。
加密的關鍵是將待加密資訊組成位元流,並將其按規定的格式依次填入載體位元影像檔案的
指定位中。首先要做好準備工作,由於每12個位元影像檔案的位元組恰好可以用於儲存3個源文
件的位元組,而12又是3的整數倍,所以可以設定每12個位元組為一個迴圈。下面5個數組中
存放的資料用於處理每個迴圈中的各個位元組所要進行的移位量和掩碼值等:
//填冗餘位時的移位序列
int move1[13]={6,5,2,0,7,4,2,1,-2,6,4,3,0};
//源檔案的位元組掩碼序列
unsigned char mask1[13]={192,32,28,3,128,112,12,2,1,192,48,8,7};
//位元影像檔案的位元組掩碼序列
unsigned char
mask2[13]={252,254,248,252,254,248,252,254,251,252,252,254,248};
//為1時,存放待加密資訊的緩衝指標加一
int add1[13]={0,0,0,1,0,0,0,0,1,0,0,0,1};
//為1時,存放BMP檔案的緩衝指標加一
int add2[13]={1,1,1,1,1,1,1,1,0,1,1,1,1};
接下來通過CFile類的Open()、ReadFile()等相關函數將待加密的源檔案和用作加密載體
的位元影像檔案內容分別讀取到緩衝buf1和buf2中,在此需要強調的是緩衝的類型不能是通
常的char型,而必須是unsigned char型,否則會在處理帶有漢字的檔案時出現錯誤。如
果源檔案和載體位元影像檔案的大小不滿足1∶4的必要條件會導致null 指標的異常錯誤,所以
要在進行密碼編譯演算法之前先對源檔案和載體位元影像檔案的大小進行比較。
下面是部分主要代碼:
//將源檔案的檔案名稱、檔案長度、分割符、內容填入buf3緩衝
total=3+FileName1.GetLength()+str.GetLength()+FileLen1;
buf3=new unsigned char[total];
for(int i0=0;i0<FileName1.GetLength()+str.GetLength()+1;i0++)
buf3[i0]=title[i0];
i0++;
//從第39個位元組開始儲存其他資訊
pointer2=38;
buf3[i0]=‘*’;
//把檔案內容與加密金鑰相異或
for(int i1=0;i1<FileLen1;i1++)
buf3[i0+i1+1]=buf1[i1]^PSW;
while(pointer1<=total)
{
//把將從buf3緩衝中提取出來的位元流填入載體位元影像的每個位元組的冗餘位元位中
if(move1[pointer3]>0)
buf2[pointer2]=(buf2[pointer2]&mask2[pointer3])|((buf3[pointer1]&mask1[poin
ter3])>>
move1[pointer3]);
else
buf2[pointer2]=(buf2[pointer2]&mask2[pointer3])|((buf3[pointer1]&mask1[poin
ter3])
<<(move1[pointer3]*(-1)));
if(add1[pointer3]==1)
pointer1++; //修正指標
if(add2[pointer3]==1)
pointer2++; //修正指標
pointer3++; //修正指標
pointer3%=13;
}
程式跳出While迴圈時,已經將源文資訊和位元影像資訊按協議融合好了,剩下的工作就是調
用MFC的CFile類的Write()和Close()成員函數將產生的密文從緩衝儲存到磁碟上的檔案
,即含有加密資訊的位元影像檔案。
可以通過ACDSee等看圖軟體比較一下不含任何其他資訊的原始位元影像檔案和新產生的融合
有密文資訊的位元影像檔案,用肉眼是看不出它們的區別的,而且檔案長度也沒有發生變化
。
加密資訊的恢複
和大多數加解密程式一樣,本方法的解密和加密過程也是很類似的,只要將流程逆轉即
可。其實現的主要思路是按照協議將作為載體的位元影像檔案中的指定位資訊提取出來組成
位元流,並將此位元流拆分恢複成原始的源文資訊。首先設定好移位值和掩碼:
//合并檔案資訊位元組時的移位序列
int move2[13]={6,5,2,0,7,4,2,1,-2,6,4,3,0};
//位元影像檔案位元組的掩碼序列
unsigned char mask2[13]={3,1,7,3,1,7,3,1,4,3,3,1,7};
//為1時,存放待恢複資訊的緩衝指標加一
int add1[13]={0,0,0,1,0,0,0,0,1,0,0,0,1};
//為1時,存放含有加密資訊的BMP檔案內容的緩衝指標加一
int add2[13]={1,1,1,1,1,1,1,1,0,1,1,1,1};
然後再通過CFile類的Open()、Read()等檔案處理函數將含有加密資訊的位元影像檔案內容讀
取到緩衝中,下面進行的就是密文的提取與恢複工作:
//其他檔案的資訊是從第39個位元組開始存放的
pointer2=38;
//buf3緩衝清零
memset(buf3,0,FileLen2);
//計算異或運算值
PSW=MakePSW();
while(true)
{
//從位元影像檔案的位元組中提取出儲存的資訊流並重組成待恢複的檔案位元組序列
if(move2[pointer3]>0)
buf3[pointer1]|=(buf2[pointer2]&mask2[pointer3])<<move2[pointer3];
else
buf3[pointer1]|=(buf2[pointer2]&mask2[pointer3])>>(move2[pointer3]*(-1));
if(add1[pointer3]==1)
{
if(buf3[pointer1]==‘*’)
//分割符記數
times++;
if((pointer1>=0)&&(times==0))
{
CString str;
str.Format(“%c”,buf3[pointer1]);
//提取待恢複的檔案名稱
FileName1+=str;
}
if((times==1)&&(buf3[pointer1]!=‘*’))
{
temp[pointer4]=buf3[pointer1]; pointer4++;
}
if((times==2)&&(buf3[pointer1]==‘*’))
{
temp[pointer4]=0;
//提取待恢複的檔案長度
FileLen1=static_cast<DWORD>(atoi
(temp));
pointer4=0;
}
if(times>=2)
{
//通過再次異或運算恢複出源檔案內容
buf1[pointer4]=buf3[pointer1]^PSW;
pointer4++;
}
if(pointer4>FileLen1)
//檔案恢複完畢則跳出迴圈
break;
pointer1++; //修正指標
}
if(add2[pointer3]==1)
pointer2++;//修正指標
pointer3++;//修正指標
pointer3%=13;
}
最後,根據已經從位元影像中提取出來的源檔案的檔案名稱、副檔名,將解密出來的資訊
按原來的檔案名稱重新恢複出來。顯然如果在解密時輸入的密碼不正確的話,解出來的信
息是無法識別的。
小 結
該方法將重要訊息隱藏於位元影像檔案中可以對其起到很好的保護作用,尤其是其載體為普
通的24位BMP位元影像檔案,幾乎不會有人能意識到裡面會含有其他的資訊。我們在利用電子
郵件發送機要檔案時可以先將其隱藏於位元影像之中,然後將此位元影像以附件形式發送出去,
即使郵件被攔截偵聽,也不至於造成泄密。
http://www.blog.ccoo.cn/nbk3/lshow.asp?id=65264