使用BlackBerry Transcoder API集成第三方加密方案
BlackBerry平臺自身帶有完整的加密機制。所有數(shù)據(jù)從BES(BlackBerry Enterprise Server)流出前都做了加密處理,使用的是AES或者是Triple-DES的方式進行加密。在數(shù)據(jù)到達BlackBerry智能手機后手機端平臺會對數(shù)據(jù)進行解密操作。同樣,當數(shù)據(jù)從BlackBerry智能手機流向服務(wù)器端時也通過AES或者是Triple-DES方式進行加解密操作。也就是說,在BlackBerry平臺上,從BES服務(wù)器端到BlackBerry智能手機端都是受平臺的加密保護的。
BlackBerry平臺的加密機制
其架構(gòu)如下圖:

架構(gòu)圖中的紅線部分表示BlackBerry平臺加密的數(shù)據(jù)通道,字符串“Test…”表示明文數(shù)據(jù),而字符串“&^%$...”表示加密后的數(shù)據(jù)??梢钥吹疆敂?shù)據(jù)由BES服務(wù)器傳向網(wǎng)絡(luò)之前BES服務(wù)器會使用管理員指定的AES或者是Triple-DES加密方法對數(shù)據(jù)進行加密。在加密數(shù)據(jù)到達BlackBerry智能手機之前都無法被正常讀取,從而達到保護傳輸數(shù)據(jù)的目的。在BlackBerry智能手機接收到服務(wù)器端發(fā)送的數(shù)據(jù)后,首先會根據(jù)管理員指定的解密方法對數(shù)據(jù)進行解密操作,然后將數(shù)據(jù)以明文形式傳遞給手機應(yīng)用程序。如上所述,當數(shù)據(jù)從BlackBerry智能手機端提交給服務(wù)器時BlackBerry會以相類似的過程對數(shù)據(jù)進行加解密操作,從而保證數(shù)據(jù)從手機端提交到服務(wù)器端時也受保護。然而,對于很多企業(yè)和組織而言,單純使用廠商提供的密鑰和加密機制是不夠的,他們必須使用自己提供的加密密鑰,或者更進一步,要求使用自身研發(fā)的或者是指定安全提供商研發(fā)的加密機制。在這種情況下就需要在BlackBerry平臺上加入額外的加密機制。如果企業(yè)只是對郵件有額外的加密要求,同時可以接受標準的加密算法,則可以通過S/MIME加密機制的配置形成額外的加密機制。BlackBerry平臺支持S/MIME標準,可以在BES服務(wù)器端配置LDAP連接以查找密鑰,然后在BlackBerry智能設(shè)備上引入用戶的密鑰,從而形成完整的加密/簽名機制。在這種配置下,服務(wù)器端和BlackBerry智能手機端都可以使用企業(yè)自身提供的加密密鑰。不過,通過S/MIME配置額外的加密機制只作用于郵件,后臺企業(yè)應(yīng)用通過BES推送到BlackBerry手機端的數(shù)據(jù)是不受二次加密保護的,同樣,BlackBerry智能手機端應(yīng)用提交到服務(wù)器的應(yīng)用數(shù)據(jù)也是不受二次加密保護的。如果希望對郵件數(shù)據(jù)和應(yīng)用數(shù)據(jù)都實現(xiàn)二次加密,則需要使用BlackBerry平臺上的Transcoder API。本章節(jié)將詳細介紹Transcoder API,包括Transcoder API的接口和相關(guān)的代碼實現(xiàn)。如果希望了解S/MIME的配置過程,請參考相關(guān)文檔,本章節(jié)不對S/MIME的配置做詳細介紹。
Transcoder API的整體結(jié)構(gòu)
Transcoder API在BlackBerry平臺上提供了二次加密方法,其基本思路是在BES服務(wù)器端和BlackBerry智能手機端都預(yù)留接口,讓開發(fā)人員可以在服務(wù)器端和BlackBerry智能手機端部署加解密應(yīng)用,從而對所有傳輸?shù)臄?shù)據(jù)進行二次加密。其架構(gòu)示意圖如下:

示意圖中紅線部分同樣表示BlackBerry平臺加密的數(shù)據(jù),橙線部分表示使用Transcoder加密接口進行加密的數(shù)據(jù)通道,字符串“Test…”表示明文,字符串“Uftu…”表示通過Transcoder加密接口加密過后數(shù)據(jù),字符串“%^$(*…”表示BES加密后的數(shù)據(jù)??梢钥吹皆跀?shù)據(jù)離開BES服務(wù)器后,它同時受到BlackBerry平臺加密和Transcoder加密接口加密兩層保護,這就形成了數(shù)據(jù)的二次加密。
使用Transcoder API進行二次加密的關(guān)鍵是在服務(wù)器和智能手機兩端加入了額外的加解密程序。
服務(wù)器端在使用BlackBerry平臺提供的加密方法對數(shù)據(jù)進行加密前會調(diào)用服務(wù)器端加解密程序,將所有數(shù)據(jù)提交給服務(wù)器端加解密程序。此時服務(wù)器端加解密程序的任務(wù)就是通過特定的加密算法對傳入的數(shù)據(jù)進行加密,加密后返還給BES服務(wù)器。BES服務(wù)器在收到服務(wù)器端加解密應(yīng)用所加密的數(shù)據(jù)后,會使用BlackBerry平臺的加密方法對數(shù)據(jù)進行再一次加密,然后才將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上傳送給智能手機端。
智能手機端在接收到BES服務(wù)器傳送的數(shù)據(jù)后,首先使用BlackBerry平臺的解密方法對數(shù)據(jù)進行解密。解密后再調(diào)用手機端加解密應(yīng)用,將數(shù)據(jù)傳遞給手機端加解密應(yīng)用。此時的數(shù)據(jù)正是服務(wù)器端加解密應(yīng)用加密過的數(shù)據(jù)。手機端加解密應(yīng)用需要使用對應(yīng)的解密算法對數(shù)據(jù)進行解密,最終得到明文,將明文傳遞給手機應(yīng)用。
從智能手機提交數(shù)據(jù)到BES服務(wù)器也通過類似的過程對數(shù)據(jù)進行兩次加解密處理。從以上描述不難發(fā)現(xiàn),在實現(xiàn)二次加密過程服務(wù)器端和手機端的加解密程序必須對應(yīng),服務(wù)器端加解密應(yīng)用使用某一種加密算法和密鑰,手機端加解密應(yīng)用必須使用對應(yīng)的解密算法和密鑰。否則會出現(xiàn)一端無法解密數(shù)據(jù)導(dǎo)致應(yīng)用處理錯誤的問題。
所以,通過Transcoder API實現(xiàn)二次加密的關(guān)鍵在于如何開發(fā)和部署服務(wù)器端和手機端的加解密應(yīng)用。在下面的章節(jié)中我們會介紹接口的詳細情況和開發(fā)過程。
服務(wù)器端Transcoder API介紹
服務(wù)器端的Transcoder API是以C/C++頭文件形式提供的,名為:“BES Transcoder API.h”開發(fā)人員需要通過C/C++開發(fā)工具將“BES Transcoder API.h”文件引入項目中,使用c語言對指定方法進行實現(xiàn),最終通過C/C++編譯工具形成一個dll文件。當開發(fā)人員部署所開發(fā)的服務(wù)器端加解密應(yīng)用時需要將所生成的dll文件拷貝到BES服務(wù)器上,并通過注冊表“HKEY_LOCAL_MACHINE\SOFTWARE\Research In Motion\BlackBerry Enterprise Server\Dispatcher\Transcoder”指向dll文件所在的位置。
通過這樣的配置,在BES服務(wù)器啟動的時候會根據(jù)注冊表找到開發(fā)人員所開發(fā)的dll文件并調(diào)入內(nèi)存。在BES服務(wù)器需要對數(shù)據(jù)進行處理時會調(diào)用dll內(nèi)的方法,由開發(fā)人員所實現(xiàn)的加解密方法對數(shù)據(jù)進行處理。
下面詳細描述BES Transcoder API.h為服務(wù)器端dll程序定義的函數(shù)
函數(shù):Load DLL()
該函數(shù)在BES服務(wù)器加載這個dll時被調(diào)用,開發(fā)人員可以在這個函數(shù)中實現(xiàn)加解密程序所需要的初始化代碼。
函數(shù)定義:int__cdecl Load DLL()
參數(shù):無
返回值:返回0表示加載成功,返回其它值表示加載失敗,該函數(shù)的返回值由開發(fā)人員指定,用于告訴BES服務(wù)器本dll是否成功加載了相關(guān)內(nèi)容。
函數(shù):Free DLL()
該函數(shù)在BES服務(wù)器釋放該dll時被調(diào)用,開發(fā)人員可以在這個函數(shù)中加入對象釋放等資源回收代碼。
函數(shù)定義:void Free DLL()
參數(shù):無
返回值:無
函數(shù):Get ID()
該函數(shù)用于返回服務(wù)器端加解密應(yīng)用程序的ID,這個函數(shù)的返回值作為該應(yīng)用的標記,所以它必須和客戶端的對應(yīng)getID方法返回相同的值,讓BlackBerry平臺可以找到對應(yīng)的解密應(yīng)用。
函數(shù)定義:unsignedchar__cdecl GetId()
參數(shù):無
返回值:返回一個unsignedchar類型的值,不能為0,開發(fā)人員可以根據(jù)約定任意指定一個非0的unsignedchar值,前提就是服務(wù)器端返回的ID值和客戶端返回的ID值相同。
函數(shù):Will Transcode()
該函數(shù)讓dll決定是否對相應(yīng)的內(nèi)容進行加解密處理。函數(shù)定義:int__cdecl Will Transcode(const Transcoder Context*constcontext)
參數(shù):context用于獲取傳入的內(nèi)容的上下文,從而判斷是否需要對其進行加密。返回值:當該函數(shù)返回0時表明需要對內(nèi)容進行加解密處理,返回其它非0值則表明不需要對內(nèi)容進行加解密處理。值-1為保留字TRANSCODE_ERROR,在這里不能作為返回值使用。
函數(shù):Encode()
該函數(shù)用于對數(shù)據(jù)進行加密,BES服務(wù)器在發(fā)送數(shù)據(jù)前會調(diào)用這個方法,開發(fā)人員需要在這個函數(shù)中實現(xiàn)加密算法。
函數(shù)定義:int__cdecl Encode(Transcoder Input Stream*constinput,Transcoder Output Stream*constoutput,const Transcoder Context*constcontext)
參數(shù):
input為BES服務(wù)器傳入的內(nèi)容,開發(fā)人員可以從中讀取消息內(nèi)容output為傳給BES服務(wù)器的內(nèi)容,開發(fā)人員需要將加密過的數(shù)據(jù)寫入該參數(shù)中context消息上下文,用于獲取消息內(nèi)容主體以外的其它相關(guān)信息。返回值:當該函數(shù)返回0時表明加密成功,返回其它非0值則表明加密不成功。注意,如果該函數(shù)返回非0值則傳入的內(nèi)容會被丟失。值-1為保留字TRANSCODE_ERROR,在這里不能作為返回值使用。
函數(shù):Decode()
該函數(shù)用于對數(shù)據(jù)進行解密,BES服務(wù)器在接收到數(shù)據(jù)后會調(diào)用這個方法,開發(fā)人員需要在這個函數(shù)中實現(xiàn)解密算法。
函數(shù)定義:int__cdecl Decode(Transcoder Input Stream*constinput,Transcoder Output Stream*constoutput,const Transcoder Context*constcontext)
參數(shù):input為BES服務(wù)器傳入的內(nèi)容,開發(fā)人員可以從中讀取消息內(nèi)容output為傳給BES服務(wù)器的內(nèi)容,開發(fā)人員需要將解密過的數(shù)據(jù)寫入該參數(shù)中context消息上下文,用于獲取消息內(nèi)容主體以外的其它相關(guān)信息。返回值:當該函數(shù)返回0時表明解密成功,返回其它非0值則表明解密不成功。注意,如果該函數(shù)返回非0值則傳入的內(nèi)容會被丟失。值-1為保留字TRANSCODE_ERROR,在這里不能作為返回值使用。
手機端Transcoder API介紹
手機端Transcoder API以Java類的形式提供,主要的類名為“net.rim.device.api.crypto.transcoder.Transcoder”。開發(fā)人員需要繼續(xù)這個類,并實現(xiàn)其中指定的方法。另外,為了讓開發(fā)人員所開發(fā)的類生效,需要調(diào)用“net.rim.device.api.crypto.transcoder.TranscoderManager”類的register方法注冊所開發(fā)的Transcoder子類。最后,開發(fā)人員通BlackBerry開發(fā)工具生成一個可以在BlackBerry智能手機上運行的程序。因為手機端的加解密程序需要對所有數(shù)據(jù)進行處理,一般這個應(yīng)用會設(shè)置為自啟動的應(yīng)用。在部署手機端加解密應(yīng)用的時候,需要將所生成的手機應(yīng)用安裝到手機設(shè)備上。如果該程序被設(shè)置為自啟動應(yīng)用,BlackBerry智能手機會自動將該程序載入內(nèi)存。當需要對數(shù)據(jù)進行處理的時候,會調(diào)用該程序特定的方法,由該方法對數(shù)據(jù)進行處理。
繼承法Transcoder類后所需要實現(xiàn)的函數(shù)有:
函數(shù):get ID()
用于獲取客戶端應(yīng)用的ID,開發(fā)人員可以在該函數(shù)中返回0以外的任意byte類型的值,前提是返回值必須和服務(wù)器端應(yīng)用GetID函數(shù)的返回值相同。
函數(shù)定義:publicfinalbyteget ID()
參數(shù):無
返回值:該方法返回一個byte類型的值作為客戶端應(yīng)用的ID,如上所述,這里返回的ID必須和服務(wù)器端應(yīng)用GetID方法返回的值相同。
函數(shù):will Transcode()
該函數(shù)讓開發(fā)人員判斷是否需要對傳入的內(nèi)容進行加解密處理。它的作用主要是過濾那些不需要加解密操作的消息。
函數(shù)定義:publicbooleanwillTranscode(IntHashtablecontext)
參數(shù):context用于獲取傳入的內(nèi)容的上下文,從而判斷是否需要對其進行加密。返回值:返回true表示需要對內(nèi)容進行加解密處理,返回false表示不需要對內(nèi)容進行加解密處理。
函數(shù):encode()
該函數(shù)用于對數(shù)據(jù)進行加密,BlackBerry智能手機在發(fā)送數(shù)據(jù)前會調(diào)用這個方法,開發(fā)人員需要在這個函數(shù)中實現(xiàn)加密算法。
函數(shù)定義:publicbooleanencode(Input Streaminput,Output Streamoutput,Int Hashtablecontext)
參數(shù):input為BlackBerry智能手機傳入的內(nèi)容,開發(fā)人員可以從中讀取消息內(nèi)容output為BlackBerry智能手機的內(nèi)容,開發(fā)人員需要將加密過的數(shù)據(jù)寫入該參數(shù)中context消息上下文,用于獲取消息內(nèi)容主體以外的其它相關(guān)信息。返回值:當該函數(shù)返回true時表明加密成功,返回false則表明加密不成功。注意,如果該函數(shù)返回非0值則傳入的內(nèi)容會被丟失。值-1為保留字TRANSCODE_ERROR,在這里不能作為返回值使用。
函數(shù):decode()
該函數(shù)用于對數(shù)據(jù)進行解密,BlackBerry智能手機在接收到數(shù)據(jù)后會調(diào)用這個方法,開發(fā)人員需要在這個函數(shù)中實現(xiàn)解密算法。
函數(shù)定義:publicbooleandecode(Input Streaminput,Output Streamoutput,Int Hashtablecontext)
參數(shù):input為BlackBerry智能手機傳入的內(nèi)容,開發(fā)人員可以從中讀取消息內(nèi)容output為傳給BlackBerry智能手機的內(nèi)容,開發(fā)人員需要將解密過的數(shù)據(jù)寫入該參數(shù)中context消息上下文,用于獲取消息內(nèi)容主體以外的其它相關(guān)信息。返回值:當該函數(shù)返回true時表明解密成功,返回false則表明多彩密不成功。#p#
創(chuàng)建服務(wù)器端程序
如以前文所述,服務(wù)器端的加解密程序是以dll形式部署的,所以,要開發(fā)服務(wù)器端加解密程序需要一個可以將c語言代碼編譯成dll的工具。為了方便,本例使用Visual C++ 6.0作為開發(fā)工具,讀者可以根據(jù)自己的習慣選用其它類似工具。
啟動Visual C++ 6.0,點擊“File->New”以創(chuàng)建一個新的項目,如下圖:

點擊“File->New”菜單后系統(tǒng)會彈出新建向?qū)?,選擇“Projects”標簽頁以創(chuàng)建項目。在“Project”標簽頁中選擇“Win32 Dynamic-Link Library”以創(chuàng)建一般的Win32 dll。在“Project Name”一欄中輸入項目名,本例為“My Transcoder”,同時指定項目所在的文件目錄,本例使用“c:\workspace\vc6\My Transcoder”作為項目所在的文件目錄。保持其它選項,點擊“OK”,系統(tǒng)將創(chuàng)建一個名為My Transcoder的項目,同時創(chuàng)建對應(yīng)的workspace。示意圖如下:

在項目創(chuàng)建過程中向?qū)崾拘枰獎?chuàng)建什么類型的dll項目,為了代碼編寫方便,我們選擇“Asimple Dllproject”,就是創(chuàng)建一個簡單的dll項目,由系統(tǒng)生成一些基本的文件。選擇后點擊“Finish”

向?qū)ЫY(jié)束后系統(tǒng)會出現(xiàn)提示框以確認項目的細節(jié),點擊“OK”關(guān)閉這個確認框。

My Transcoder項目創(chuàng)建以后可以發(fā)現(xiàn)項目中有一些系統(tǒng)生成的文件,包括Std Afx.h,Std Afx.cpp和My Transcoder.cpp。我們需要編輯的是My Transcoder.cpp,在這里完成Transcoder API的實現(xiàn)。雙擊左邊導(dǎo)航條中的“My Transcoder.cpp”文件以打開該文件,可以看到系統(tǒng)生成了Dll Main方法,這個方法是dll的入口方法,所以系統(tǒng)自動生成了該方法。如下圖:

因為我們在項目中要使用Transcoder API,所以下一步工作就是將Transcoder API提供的頭文件“BES Transcoder API.h”引入到項目中。“BES Transcoder API.h”文件隨文檔提供,讀者也可以在BlackBerry官方網(wǎng)站上下載。獲取到“BES Transcoder API.h”文件后,在操作系統(tǒng)上將這個文件拷貝到項目目錄中,本例就是“C:\workspace\vc6\My Transcoder”目錄。然后,在Visual C++ 6.0界面中左邊導(dǎo)航條中選擇“Header Files”,點擊右鍵,選擇“Add Filesto Folder…”,然后選擇剛拷貝的“BES Transcoder API.h”,這樣就可以將頭文件“BES Transcoder API.h”引入到當前項目中。

引入頭文件“BES Transcoder API.h”后結(jié)果如下圖,有興趣的讀者可以雙擊該文件打開它,了解一下頭文件“BES Transcoder API.h”中所定義的方法。

引入頭文件“BES Transcoder API.h”后,在左邊導(dǎo)航條中雙擊“My Transcoder.cpp”文件,編輯My Transcoder.cpp文件,使該文件的內(nèi)容和似下代碼相同。讀者為了方便,可以將系統(tǒng)生成的代碼行刪除,直接將以下代碼粘貼到My Transcoder.cpp文件中:
- // My T r ans c o de r .c pp : De fin es t he en t ry po in t f o r t he DL L app li ca t io n.
- #in cl ud e " s t da fx.h "
- #in cl ud e " BE S T r ans c o der AP I. h"
- #in cl ud e
- F ILE * logF il e;
- ch ar LogF il eN am e[ 6 4 ]="c :\\ T r ans c o der \\ T r ans c o de r -Log . t xt" ;
- DEF INE_ BE S _T RA NSC OD E R_ DLL
- BOO L AP IE NTR Y Dll Ma in ( HA NDLE h Mo du le,
- DW ORD ul _ r easo n_f o r _c al l, LP V OI D lp R ese r v ed
- )
- {
- prin t f( " Lo ad in g Dl l" ) ;
- r et urn T RU E ;
- }
- __ decls pec ( dl l e xpo rt )
- in t __cdec l Lo ad D LL( )
- {
- logF il e = f o pen( Log Fil eN am e , "a" ) ;
- fprin t f( logF il e,"t ryin g t o c al l Lo ad Dl l" ) ;
- r et urn 0 ;
- }
- __ decls pec ( dl l e xpo rt )
- v o id __cd ecl F r ee DL L( )
- {
- fprin t f( logF il e,"Dl l fre e") ;
- }
- __ decls pec ( dl l e xpo rt )
- uns ig ned c ha r __ cd ecl G et ID ( )
- {
- uns ig ned lo ng T r ans c o de rID =2 0 ;
- fprin t f( logF il e,"t ryin g t o g et I D" ) ;
- r et urn ( uns ig ned c ha r ) T r ans c o de rID ;
- }
- __ decls pec ( dl l e xpo rt )
- in t __cdec l W il l T r ans c o de( c o ns t T r ans c o derCo n t e xt *c o ns t c o n t e xt )
- {
- r et urn 0 ;
- }
- __ decls pec ( dl l e xpo rt )
- in t __cdec l En c o de ( T r ans co derIn putSt r eam *c o ns t in put , T r a ns c o derOut putSt r eam *c o ns t o ut put , c o ns t T r ans co der Co n t e xt *c o ns t c o n t e xt )
- {
- fprin t f( logF il e,"t es t in g enc o de is r un ni ng no w") ;
- uns ig ned c ha r r ead C;
- fprin t f( logF il e," /n enc o de r ead ch ar : " ) ;
- whi le ( in put - >R ead ( & r ead C) )
- {
- fprin t f( logF il e,"%c" ,r ea dC) ;
- o ut put ->W rit e( r eadC ) ;
- }
- r et urn 0 ;
- }
- __ decls pec ( dl l e xpo rt )
- in t __cdec l D ec o de( T r ans c o derIn putSt r eam *c o ns t in put , T r a ns c o derOut putSt r eam *c o ns t o ut put , c o ns t T r ans c o der Co n t e xt *c o ns t c o n t e xt )
- {
- fprin t f( logF il e," /n dec o de r ead c ha r : " ) ;
- uns ig ned c ha r r ead C;
- whi le ( in put - >R ead ( & r ead C) )
- {
- fprin t f( logF il e,"%c" ,r ea dC) ;
- o ut put ->W rit e( r eadC ) ;
- }
- r et urn 0 ;
- }
編輯代碼后結(jié)果如下圖:

最后,點擊“Build->Build My Transcoder.dll”菜單生成dll文件。使用Visual C++ 6.0的標準設(shè)置的話,所生成的dll文件可以在項目的Debug目錄中找到,本例就是C:\workspace\vc6\My Transcoder\Debug目錄。

如果在編譯過程中出現(xiàn)錯誤的話請根據(jù)控制臺的錯誤提示對代碼進行修改并重新編譯。編譯成功后你就完成了服務(wù)器加解密應(yīng)用的生成工作了,在后面的章節(jié)中會對代碼進行詳細解釋。這里提醒讀者的是本例使用的樣例代碼是一個“空”的加解密代碼,在加解密過程中只是單純地將輸入的數(shù)據(jù)傳到輸出接口,在現(xiàn)實環(huán)境中使用的話開發(fā)人員的進一步工作就是修改樣例代碼中的Encode和Decode,使其可以對數(shù)據(jù)進行真正的加密和解密工作。#p#
創(chuàng)建客戶端程序
Transcoder API客戶端是以BlackBerry應(yīng)用形式存在于智能手機的,所以,要創(chuàng)建客戶端的加解密應(yīng)用,需要使用BlackBerry開發(fā)環(huán)境開發(fā)一個BlackBerry應(yīng)用程序。
本例使用BlackBerry JDE Plug-In For Eclipse 1.1開發(fā)環(huán)境創(chuàng)建了一個名為Transcoder Client的BlackBerry項目。有關(guān)BlackBerry項目的創(chuàng)建過程以及BlackBerry JDE Plug-In For Eclipse的具體使用,請參考相關(guān)文檔,本文只描述Transcoder Client項目創(chuàng)建過程中的關(guān)鍵步驟,不對項目創(chuàng)建的每一個過程進行描述。
創(chuàng)建BlackBerry項目后,創(chuàng)建一個Java包以包含將要創(chuàng)建的Java類,本例使用
“org.bbtesting.transcoder”作為包名。然后在該包中創(chuàng)建Transcoder API客戶端的入口程序,名為Main App。
注意,一般而言Transcoder API客戶端加解密應(yīng)用會以自啟動方式啟動,不需要用戶干預(yù),本例考慮到測試的便利性,通過應(yīng)用程序圖標的方式啟動應(yīng)用,然后通過菜單啟動客戶端加解密線程。
在Main App類文件中對代碼進行修改,使其如以下代碼:
- package org.damon.transcoder;
- import net.rim.device.api.ui.UiApplication;
- import net.rim.device.api.ui.container.MainScreen;
- public class MainApp extends UiApplication {
- public static void main(String[] args) { MainApp _app = new MainApp();
- _app.enterEventDispatcher();
- }
- public MainApp() {
- MainScreen screen = new MyScreen( );
- this .pushScreen(screen);
- }
- }
如果讀者仔細閱讀Main App中的方法,可以發(fā)現(xiàn)該類主要是創(chuàng)建了應(yīng)用程序入口,在程序入口中創(chuàng)建了My Screen類的實例,并將該實例顯示出來。所以,下一步的工作就是要創(chuàng)建My Screen類,讀者在按以下步驟創(chuàng)建My Screen類之前編譯本項目的話會出現(xiàn)找不到類My Screen的錯誤。
在包org.bbtesting.transcoder中創(chuàng)建類My Screen,創(chuàng)建過程中選擇繼承類Main Screen,然后編輯My Screen類,使其如以下代碼:
- package org.bbtest .transcoder;
- import net.rim.device.api.crypto.transcoder.TranscoderManager;
- import net.rim.device.api.ui.MenuItem;
- import net.rim.device.api.ui.UiApplication;
- import net.rim.device.api.ui.component.EditField;
- import net.rim.device.api.ui.container.MainScreen;
- public class MyScreen extends MainScreen {
- private MenuItem start = new MenuItem( "start" , 200000, 10) {
- public void run() {
- register();
- }
- };
- private EditField logField = new EditField();
- public MyScreen() {
- this .addMenuItem( start ); logField .setText( "Transcoder Testing client" ); this .add( logField );
- }
- private void register() { this .log( "start to register" ); try {
- MyTranscoder transcoder = new MyTranscoder(); transcoder.SetScreen( this ); TranscoderManager .register (transcoder);
- } catch (Exception e) {
- System. out .println( "Exception while registering:" + e);
- this .log( "Exception while registering:" + e);
- }
- }
- public void log( final String msg) {
- UiApplication. getUiApplication ().invokeLater( new Runnable() {
- public void run() {
- logField .setText( logField .getText() + "\n" + msg); System. out .println(msg);
- }
- });
- }
- }
在以上的My Screen類中主要是創(chuàng)建一個屏幕,在該屏幕上添加一個菜單項,用戶可以點擊這個菜單項啟動客戶端加解密應(yīng)用。啟動的過程就是將一個My Transcoder類傳遞給系統(tǒng),讓系統(tǒng)在處理所有數(shù)據(jù)的時候都調(diào)用這個My Transcoder類。
因為我們在本項目中還沒有創(chuàng)建My Transcoder類,此時編譯項目會出現(xiàn)找不到My Transcoder類的錯誤。下面的工作就是要創(chuàng)建My Transcoder類,這也是創(chuàng)建Transcoder客戶端加解密應(yīng)用的最關(guān)鍵步驟。
在包org.bbtest.transcoder中創(chuàng)建名為My Transcoder的類,創(chuàng)建過程中選擇繼承類net.rim.device.api.crypto.transcoder.Transcoder。創(chuàng)建該類后修改其代碼,結(jié)果如下:
- package org.bbtest .transcoder;
- import java.io.InputStream;
- import java.io.OutputStream;
- import net.rim.device.api.crypto.transcoder.Transcoder;
- import net.rim.device.api.util.IntHashtable;
- public class MyTranscoder extends Tran scoder {
- private MyScreen screen = null ;
- public MyTranscoder() {
- super (( byte ) 20);
- }
- public void SetScreen(MyScreen screen) {
- this . screenscreen = screen;
- }
- public boolean decode(InputStream input, OutputStream output, IntHashtable context) {
- this . screen .log( "decodeing" );
- try {
- int readByte = input.read();
- while (readByte != -1) { output.write(readByte); readByte = input.read();
- }
- output.flush();
- } catch (Exception e) { this . screen .log( "Exception in decode:" + e); return false ;
- }
- return true ;
- }
- public boolean encode(InputStream input, OutputStream output,
- IntHashtable context) {
- this . screen .log( "encodeing" );
- try {
- int readByte = input.read();
- while (readByte != -1) { output.write(readByte); readByte = input.read();
- }
- output.flush();
- } catch (Exception e) { this . screen .log( "Exception in decode:" + e); return false ;
- }
- return true ;
- }
- public boolean willTranscode(IntHashtable context) {
- return true ;
- }
- }
完成代碼輸入后嘗試編譯該項目,如果讀者使用缺省的“自動編譯”的設(shè)置,則在保存代碼的時候開發(fā)環(huán)境會自動進行編譯。在編譯過程中出現(xiàn)錯誤的話按系統(tǒng)提示對錯誤進行修改。最終形成的cod文件就可以用于部署了。
加載客戶端程序
在完成應(yīng)用創(chuàng)建過程后,就要開始加載客戶端的程序了。在這里要注意的是成功部署客戶端加解密應(yīng)用后,該應(yīng)用將作用于這個用戶的所有數(shù)據(jù),如果服務(wù)器商地面有沒有部署對應(yīng)加解密應(yīng)用,用戶將會無法接收數(shù)據(jù),同時也無法向服務(wù)器發(fā)送數(shù)據(jù)。所以在測試的時候要考慮到其它測試用戶,在生產(chǎn)環(huán)境中部署的過程中更是要注意配置過程對生產(chǎn)用戶的影響。本例只說明測試環(huán)境中的配置過程,所以不考慮生產(chǎn)環(huán)境配置過程的統(tǒng)籌安排。為了加載客戶端程序,首先要做的是對客戶端程序進行簽名,因為客戶端加解密程序使用到了受控制的API,沒有簽名的話將無法運行。有關(guān)客戶端應(yīng)用的簽名密鑰的申請和簽名工具的使用請參考相關(guān)文檔。
對客戶端程序進行簽名后,可以通過Java loader將客戶端程序的cod文件直接通過USB連線安裝到BlackBerry智能手機上。當然讀者也可以選擇自己熟悉的方式,如OTA方式或者是BlackBerry Desktop Manager的方式將應(yīng)用安裝到BlackBerry智能手機上。安裝完該應(yīng)用后如果讀者嘗試在BlackBerry智能手機上運行該程序的話,會發(fā)現(xiàn)該應(yīng)用程序仍無法正常工作。其原因是BlackBerry平臺對于Transcoder的使用控制比較嚴格,必須在BES服務(wù)器上做相應(yīng)設(shè)置才可以在客戶端運行Transcoder API相關(guān)的應(yīng)用。
要在客戶端運行Transcoder API相關(guān)的應(yīng)用,必須在BES服務(wù)器上為該用戶創(chuàng)建一個IT Policy,并在IT Policy中指定客戶端程序的hash碼。
獲取cod文件的hash碼有多種方式,如果是自己開發(fā)的cod應(yīng)用,可以在開發(fā)環(huán)境生成的對應(yīng)的jad文件中得到該cod應(yīng)用的hash碼,下面是Transcoder Client.cod對應(yīng)的jad文件Transcoder Client.jad的內(nèi)容,其中第9行的RIM-COD-SHA1的內(nèi)容就是該cod文件的has碼,使用時注意將中間的空格刪除,本例中得到的結(jié)果是:
0 1 f 25 24 f 0 0 f a 5 9 0 89 6 05 2 556 b6f 7 f 1 55 4 5 0 27 e 5 2 。

如果是他人開發(fā)的cod應(yīng)用,有時并沒有附帶提供對應(yīng)的jad文件。這時可以通過命令Java loader得到一個cod文件對應(yīng)的hash碼。Java loader命令的格式如下:Java loader siblinginfo
本例中為了更好地顯示Java loader命令的輸出,在命令行界面執(zhí)行以下命令將輸出的結(jié)果寫入文件c:\temp\codinfo.txt中:Java loader siblinginfo Transcoder Client.cod>c:\temp\codinfo.txt
得到的codinfo.txt打開以后如下圖:

其中Hash一欄顯示的就是Transcoder Client.cod對應(yīng)的hash碼。
獲取cod的hash碼以后需要將該hash碼配置到IT Policy中,編輯所創(chuàng)建的IT Policy,選擇“Security”標簽頁,下圖是在BES5.0Web管理界面中得到的載圖:

在選擇“Security”簽標頁后,滾動頁面,找到“Security Transcoder Cod File Hashes”一欄,將上面找到的hash碼填入,如下圖:

完成后保存該IT Policy,為測試用戶分配該IT Policy,并向測試用戶推送一次IT Policy,以保證該IT Policy作用于該測試用戶。
在完成IT Policy配置后,所安裝的Transcoder Client應(yīng)用就可以正常運行了,運行該應(yīng)用后,可以選擇菜單中的“Start”菜單項啟動客戶端加解密應(yīng)用。
在啟動客戶端加解密應(yīng)用后,會發(fā)現(xiàn)該BlackBerry智能手機無法正常收發(fā)郵件,這是因為服務(wù)器端沒有部署對應(yīng)的加解密程序。只有在服務(wù)器端也部署對應(yīng)的加解密程序后整個Transcoder應(yīng)用才能正常工作,下面將描述服務(wù)器端應(yīng)用的部署過程。
配置服務(wù)器端程序
如之前描述的,Transcoder服務(wù)器端程序是通過BES服務(wù)器上的注冊表配置的。要配置服務(wù)器Transcoder程序,需要訪問BES服務(wù)器,將我們創(chuàng)建的服務(wù)器端程序,也就是生成的My Transcoder.dll拷貝到BES服務(wù)器上,本例將My Transcoder.dll拷貝在BES服務(wù)器的“c:\workspace\transcoder”目錄下。
然后,在BES服務(wù)器所在的Windows操作系統(tǒng)下運行“regedit”啟動注冊表編輯器。在注冊表編輯器中找到以下配置:“HKEY_LOCAL_MACHINE\SOFTWARE\Research In Motion\BlackBerry Enterprise Server\Dispatcher”。在初始狀態(tài)下,“Dispatcher”配置中不會出面“Transcoder”子項目,此時需要對“Dispatcher”項點擊右鍵,選擇“新建->項”,在新建項對話框中輸入項目名為“Transcoder”。完成“Transcoder”項創(chuàng)建后,雙擊“Transcoder”項打開該項,并點擊右鍵,選擇“新建->字符串值”,在新建字符串值對話框中,在名稱一欄輸入“Transcoder”,在數(shù)據(jù)一欄輸入服務(wù)器端應(yīng)用dll文件的全路徑名稱,本例中為
“c:\workspace\transcoder\My Transcoder.dll”。配置后的結(jié)果如下圖:

完成該配置之后需要重啟BES服務(wù)器的Dispatcher任務(wù)才能讓所配置的dll調(diào)入內(nèi)存,重啟的時候可以通過Windows的服務(wù)管理器重啟,也可以通過BES管理界面重啟整個BES服務(wù)器。注意,因為在本例中的My Transcoder.dll在載入時會嘗試在目錄“c:\transcoder”目錄下生成日志文件,所以讀者如果使用本例的My Transcoder.dll,在重新啟動BES服務(wù)器之前需要手工創(chuàng)建目錄:“c:\transcoder”,以避免程序出現(xiàn)錯誤導(dǎo)致BES服務(wù)器無法啟動。下圖為BES5.0Web管理界面中重啟BES服務(wù)器的方法:

在服務(wù)器重啟過程中注意觀察BES服務(wù)器的Dispatcher的日志,該日志在BES服務(wù)器安裝日錄的Logs目錄下,本例在“C:\Program Files\Research In Motion\BlackBerry Enterprise Server\Logs”目錄中。在缺省配置下,Dispatcher任務(wù)的日志以“APP_DISP”開頭,如:“APP_DISP_01_20100202_0001.txt”。
如果服務(wù)器端應(yīng)用加載成功的話,可以在Dispatcher日志中發(fā)現(xiàn)“Transcoder DLL loaded”一句,如下圖:

如果無法加載的話也會有對應(yīng)的錯誤,出現(xiàn)無法加載錯誤的話有可能是注冊表中輸入的dll路徑不對,需要檢查Transcoder項的內(nèi)容。如果在Dispatcher日志中根本沒有發(fā)現(xiàn)transcoder相關(guān)的日志,則說明注冊表配置沒有生效,有可能注冊表項的名稱或者是位置不對,需要檢查注冊表中的Transcoder項是不是在正確位置。
測試結(jié)果
對應(yīng)用進行測試會發(fā)現(xiàn),如果客戶端和服務(wù)器端都正確部署了Transcoder加密解應(yīng)用,智能手機可以正常收發(fā)郵件。因為本例中的Transcoder加密解應(yīng)用是一個“空”的加解密應(yīng)用,并沒有對數(shù)據(jù)進行加解密操作,所以在測試過程中的表現(xiàn)和沒有部署Transcoder加密解應(yīng)用的情況相同。不過,讀者可以從Transcoder日志(本例為c:\transcoder\Transcoder-Log.txt)中看到所有服務(wù)器和客戶端交互的數(shù)據(jù)都被Transcoder加密解應(yīng)用載獲了。
代碼分析
服務(wù)器端代碼分析
為了讓讀者更好地了解Transcoder API的使用,下面對服務(wù)器端代碼進行分析。
#include"stdafx.h"
#include"BEST ranscoder API.h"
#include
- FILE*logFile;
charLogFileName[64]="c:\\Transcoder\\Transcoder-Log.txt";以上代碼為日志文件句柄與日志文件名的定義,日志操作在Transcoder API的使用中并不是必要的,本例為了讓應(yīng)用執(zhí)行有更加明顯的,可以跟蹤的結(jié)果,所以通過日志文件記錄相關(guān)信息。本例中的日志文件名硬編碼為“c:\\Transcoder\\Transcoder-Log.txt”,讀者可以根據(jù)測試環(huán)境的情況進行修改,注意要調(diào)整變量LogFileName數(shù)組的上界。
- DEFINE_BES_TRANSCODER_DLL
以上這句為Transcoder API定義語句的引入,這句是Transcoder API使用的關(guān)鍵,為了讓頭文件“BESTranscoder API.h”中的相關(guān)定義可以在本程序中使用,必須通過語句DEFINE_BES_TRANSCODER_DLL將預(yù)定義好的相關(guān)元素引入。對c/c++中的預(yù)定義機制有疑惑的讀者可以閱讀相關(guān)文檔以了解這句語句的含義,當然,也可以不深究這句語句的語法,只記住在Transcoder API使用時必須有這句語句也可以。
- BOO L AP IE NTR Y Dll Ma in ( HA NDLE h Mo du le, DWO RD ul_ r easo n_f o r _c al l, LP V OI D lp R e s er v ed )
- {
- prin t f( " Lo ad in g Dl l" ) ;
- r et urn T RU E ;
- }
以上為本dll應(yīng)用的入口,按dll的載入機制,這個函數(shù)在服務(wù)器載入該dll時被調(diào)用。本例只是在標準輸出中輸出了“LoadingDll”,并沒有執(zhí)行其它操作。
- __ decls pec ( dl l e xpo rt )
- in t __cdec l Lo ad D LL( )
- {
- logF il e = f o pen( LogF il eN am e , "a" ) ; fprin t f( logF il e,"t ryin g t o c al l Lo ad Dl l" ) ; r et urn 0 ;
- }
以上代碼為dll載入代碼,適合加入一些只需要在載入過程中運行一次的代碼,如本例中日志文件的打開只需要在dll載入時運行一次,所以在這里加入日志文件打開的語句fopen。此外,本例的該函數(shù)還在日志文件中記錄了“tryingtocallLoadDll”字符串。在實際環(huán)境中這里可以加入加解密應(yīng)用初始化的代碼,比如在這里可以加入連接CA獲取服務(wù)器密鑰的代碼。此函數(shù)返回0,表示加載成功,實際環(huán)境中此處可以根據(jù)初始化代碼的運行情況決定返回什么值,如果初始化失敗,則可以返回其它非0值。注意,如果返回其它非0值,該dll將不會被載入內(nèi)存,相關(guān)的加解密方法也不會被調(diào)用。
- __ decls pec ( dl l e xpo rt )
- v o id __cd ecl F r ee DL L( )
- {
- fprin t f( logF il e,"Dl l fre e") ;
- }
Free Dll函數(shù)為dll釋放函數(shù),可以加入連接關(guān)閉等資源釋放代碼。本例不需要釋放資源,所以在該函數(shù)中只是通過日志文件記錄了字符串“Dll free”。
- __ decls pec ( dl l e xpo rt )
- uns ig ned c ha r __ cd ecl G et ID ( )
- {
- uns ig ned lo ng T r ans c o de rID =2 0 ; fprin t f( logF il e,"t ryin g t o g et I D" ) ; r et urn ( uns ig ned c ha r ) T r ans c o de rID ;
- }
Get ID函數(shù)需要返回本應(yīng)用的ID,本例使用20作為應(yīng)用ID,所以返回20。注意要確定返回的值是unsignedchar類型。
- __ decls pec ( dl l e xpo rt )
- in t __cdec l W il l T r ans c o de( c o ns t T r ans c o derCo n t e xt *c o ns t c o n t e xt )
- {
- r et urn 0 ;
- }
Will Transcode用于確定是否需要對消息進行加解密操作,返回非零值表示不需要進行加解密操作,本例對所有消息都返回0,表示對所有消息都需要進行加解密操作。
- __ decls pec ( dl l e xpo rt )
- in t __cdec l En c o de ( T r ans co derIn putSt r eam *c o ns t in put , T r a ns c o derOut putSt r eam *c o ns t o ut put , c o ns t T r ans co der Co n t e xt *c o ns t c o n t e xt )
- {
函數(shù)Encode用于對消息進行加密操作,注意參數(shù)有input,output,context,其中input為系統(tǒng)傳入的輸入流,output為傳給系統(tǒng)的輸出流,本函數(shù)的主要工作就是從input中獲取數(shù)據(jù),進行加密操作,然后通過output傳送給系統(tǒng)。
- fprin t f( logF il e,"t es t in g enc o de is r un ni ng no w") ;
以上代碼在日志文件中輸出“testingencodeisrunningnow”,用于記錄Encode事件。
- uns ig ned c ha r r ead C;
- fprin t f( logF il e," /n enc o de r ead c ha r : " ) ;
- whi le ( in put - >R ead ( & r ead C) )
- {
- fprin t f( logF il e,"%c" ,r ea dC) ;
- o ut put ->W rit e( r eadC ) ;
- }
以上代碼定義了變量readC,調(diào)用input的Read方法將數(shù)據(jù)讀到變量readC中,然后將read C輸出到日志文件中進行記錄,同時調(diào)用output的Write方法將readC中的數(shù)據(jù)寫入到output輸出流中。通過不斷的循環(huán)可以將input中的所有數(shù)據(jù)傳送到output中。本段代碼是加密操作的關(guān)鍵,在本例中只是將數(shù)據(jù)原封不動地傳送到output輸出流中,在現(xiàn)實環(huán)境中需要在這里對數(shù)據(jù)進行處理,完成加密操作后才將數(shù)據(jù)寫入到output輸出流中。
- r et urn 0 ;
- }
最后,本函數(shù)返回零表示加密成功。
- __ decls pec ( dl l e xpo rt )
- in t __cdec l D ec o de( T r ans c o derIn putSt r eam *c o ns t in put , T r a ns c o derOut putSt r eam *c o ns t o ut put , c o ns t T r ans c o der Co n t e xt *c o ns t c o n t e xt )
- { D
- fprin t f( logF il e," /n dec o de r ead c ha r : " ) ;
- uns ig ned c ha r r ead C;
- whi le ( in put - >R ead ( & r ead C) )
- {
- fprin t f( logF il e,"%c" ,r ea dC) ;
- o ut put ->W rit e ( r eadC ) ;
- }
以上代碼定義了變量readC,類似于Encode函數(shù)中的循環(huán),這里通過循環(huán)將input中的所有數(shù)據(jù)寫入到output中。同樣,在現(xiàn)實環(huán)境中需要對input中讀取的數(shù)據(jù)進行處理,完成解密后才寫入到output中。
- r et urn 0 ;
- }
最后本函數(shù)返回零表示解密成功。
手機端代碼分析
以下為手機端代碼分析:
手機端應(yīng)用的關(guān)鍵是需要調(diào)用Transcoder Manager的register函數(shù)將加解密客戶端注冊到系統(tǒng)中。
- 25
- private void register() { this .log( "start to register" ); try {
- MyTranscoder transcoder = new MyTranscoder(); transcoder.SetScreen( this ); TranscoderManager .register (transcoder);
- } catch (Exception e) {
- System. out .println( "Exception while registering:" + e);
- this .log( "Exception while registering:" + e);
- }
- }
本段代碼的關(guān)鍵是新建一個My Transcoder類的實例,名為transcoder,然后調(diào)用Transcoder Manager的register將transcoder注冊到系統(tǒng)中。
- package org.bbtest .transcoder;
- import java.io.InputStream;
- import java.io.OutputStream;
- import net.rim.device.api.crypto.transcoder.Transcoder;
- import net.rim.device.api.util.IntHashtable;
以上代碼為包定義語句和相關(guān)類的import,主要一點是要import名為net.rim.device.api.crypto.transcoder.Transcoder的類。
- public class MyTranscoder extends Transcoder {
以上為My Transcoder類的類定義語句,聲明My Transcoder類是Transcoder類的子類。
- private MyScreen screen = null ;
以上為類屬性定義,本例只定義了screen一個屬性,用于更新主屏幕。
- public MyTranscoder() {
- super (( byte ) 20);
- }
此處為My Transcoder的構(gòu)造函數(shù),適合加入初始化相關(guān)的代碼。注意在加入初始化代碼之前必須通過super語句調(diào)用父類的構(gòu)造函數(shù)。而且要注意,調(diào)用super時要傳入客戶端加解密應(yīng)用的ID。在以上章節(jié)描述Transcoder客戶端應(yīng)用時提到客戶端應(yīng)用需要通過getID方法的返回和服務(wù)器端應(yīng)用相關(guān)的ID。在本例中,通過super的調(diào)用將ID傳給父類Transcoder,從而使用父類的getID函數(shù),這樣就不用自己實現(xiàn)getID函數(shù)了。
- public void SetScreen(MyScreen screen) {
- this . screenscreen = screen;
- }
本函數(shù)是為screen屬性指定對象,會在My Transcoder實例化后被主屏幕類調(diào)用,主要作用是在My Transcoder實例中保存屏幕類的句柄,從而調(diào)用主屏幕的相關(guān)方法以刷新主屏幕顯示的內(nèi)容。該函數(shù)在Transcoder API使用過程中必不是必須的。
- public boolean decode(InputStream input, OutputStream output, IntHashtable context) {
- this . screen .log( "decodeing" );
- try {
- int readByte = input.read();
- while (readByte != -1) { output.write(readByte); readByte = input.read();
- }
- output.flush();
- } catch (Exception e) { this . screen .log( "Exception in decode:" + e); return false ;
- }
- return true ;
- }
- public boolean encode(InputStream input, OutputStream output,
- IntHashtable context) { this . screen .log( "encodeing" ); try {
- int readByte = input.read();
- while (readByte != -1) { output.write(readByte); readByte = input.read();
- }
- output.flush();
- } catch (Exception e) { this . screen .log( "Exception in decode:" + e); return false ;
- }
- return true ;
- }
encode用于對數(shù)據(jù)進行加密操作,因為對于input的read操作和output的wirte操作有可能會拋出導(dǎo)常,所以需要通過try,catch語句捕獲異常。在try語句段中定義了變量read Byte,通過調(diào)用input的read函數(shù)將數(shù)據(jù)讀入到read Byte中,然后再將read Byte中的數(shù)據(jù)寫入到output中。最后本函數(shù)返回true表示加密成功。
- public boolean willTranscode(IntHashtable context) {
- return true ;
- }
- }
Will Transcode函數(shù)用于確定是否對消息進行加解密操作,本例對所有消息都返回true,表示對所有消息都進行加解密操作。