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

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

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

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

在項目創建過程中向導會提示需要創建什么類型的dll項目,為了代碼編寫方便,我們選擇“Asimple Dllproject”,就是創建一個簡單的dll項目,由系統生成一些基本的文件。選擇后點擊“Finish”

向導結束后系統會出現提示框以確認項目的細節,點擊“OK”關閉這個確認框。

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

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

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

引入頭文件“BES Transcoder API.h”后,在左邊導航條中雙擊“My Transcoder.cpp”文件,編輯My Transcoder.cpp文件,使該文件的內容和似下代碼相同。讀者為了方便,可以將系統生成的代碼行刪除,直接將以下代碼粘貼到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 ;
- }
編輯代碼后結果如下圖:

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

如果在編譯過程中出現錯誤的話請根據控制臺的錯誤提示對代碼進行修改并重新編譯。編譯成功后你就完成了服務器加解密應用的生成工作了,在后面的章節中會對代碼進行詳細解釋。這里提醒讀者的是本例使用的樣例代碼是一個“空”的加解密代碼,在加解密過程中只是單純地將輸入的數據傳到輸出接口,在現實環境中使用的話開發人員的進一步工作就是修改樣例代碼中的Encode和Decode,使其可以對數據進行真正的加密和解密工作。#p#
創建客戶端程序
Transcoder API客戶端是以BlackBerry應用形式存在于智能手機的,所以,要創建客戶端的加解密應用,需要使用BlackBerry開發環境開發一個BlackBerry應用程序。
本例使用BlackBerry JDE Plug-In For Eclipse 1.1開發環境創建了一個名為Transcoder Client的BlackBerry項目。有關BlackBerry項目的創建過程以及BlackBerry JDE Plug-In For Eclipse的具體使用,請參考相關文檔,本文只描述Transcoder Client項目創建過程中的關鍵步驟,不對項目創建的每一個過程進行描述。
創建BlackBerry項目后,創建一個Java包以包含將要創建的Java類,本例使用
“org.bbtesting.transcoder”作為包名。然后在該包中創建Transcoder API客戶端的入口程序,名為Main App。
注意,一般而言Transcoder API客戶端加解密應用會以自啟動方式啟動,不需要用戶干預,本例考慮到測試的便利性,通過應用程序圖標的方式啟動應用,然后通過菜單啟動客戶端加解密線程。
在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中的方法,可以發現該類主要是創建了應用程序入口,在程序入口中創建了My Screen類的實例,并將該實例顯示出來。所以,下一步的工作就是要創建My Screen類,讀者在按以下步驟創建My Screen類之前編譯本項目的話會出現找不到類My Screen的錯誤。
在包org.bbtesting.transcoder中創建類My Screen,創建過程中選擇繼承類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類中主要是創建一個屏幕,在該屏幕上添加一個菜單項,用戶可以點擊這個菜單項啟動客戶端加解密應用。啟動的過程就是將一個My Transcoder類傳遞給系統,讓系統在處理所有數據的時候都調用這個My Transcoder類。
因為我們在本項目中還沒有創建My Transcoder類,此時編譯項目會出現找不到My Transcoder類的錯誤。下面的工作就是要創建My Transcoder類,這也是創建Transcoder客戶端加解密應用的最關鍵步驟。
在包org.bbtest.transcoder中創建名為My Transcoder的類,創建過程中選擇繼承類net.rim.device.api.crypto.transcoder.Transcoder。創建該類后修改其代碼,結果如下:
- 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 ;
- }
- }
完成代碼輸入后嘗試編譯該項目,如果讀者使用缺省的“自動編譯”的設置,則在保存代碼的時候開發環境會自動進行編譯。在編譯過程中出現錯誤的話按系統提示對錯誤進行修改。最終形成的cod文件就可以用于部署了。
加載客戶端程序
在完成應用創建過程后,就要開始加載客戶端的程序了。在這里要注意的是成功部署客戶端加解密應用后,該應用將作用于這個用戶的所有數據,如果服務器商地面有沒有部署對應加解密應用,用戶將會無法接收數據,同時也無法向服務器發送數據。所以在測試的時候要考慮到其它測試用戶,在生產環境中部署的過程中更是要注意配置過程對生產用戶的影響。本例只說明測試環境中的配置過程,所以不考慮生產環境配置過程的統籌安排。為了加載客戶端程序,首先要做的是對客戶端程序進行簽名,因為客戶端加解密程序使用到了受控制的API,沒有簽名的話將無法運行。有關客戶端應用的簽名密鑰的申請和簽名工具的使用請參考相關文檔。
對客戶端程序進行簽名后,可以通過Java loader將客戶端程序的cod文件直接通過USB連線安裝到BlackBerry智能手機上。當然讀者也可以選擇自己熟悉的方式,如OTA方式或者是BlackBerry Desktop Manager的方式將應用安裝到BlackBerry智能手機上。安裝完該應用后如果讀者嘗試在BlackBerry智能手機上運行該程序的話,會發現該應用程序仍無法正常工作。其原因是BlackBerry平臺對于Transcoder的使用控制比較嚴格,必須在BES服務器上做相應設置才可以在客戶端運行Transcoder API相關的應用。
要在客戶端運行Transcoder API相關的應用,必須在BES服務器上為該用戶創建一個IT Policy,并在IT Policy中指定客戶端程序的hash碼。
獲取cod文件的hash碼有多種方式,如果是自己開發的cod應用,可以在開發環境生成的對應的jad文件中得到該cod應用的hash碼,下面是Transcoder Client.cod對應的jad文件Transcoder Client.jad的內容,其中第9行的RIM-COD-SHA1的內容就是該cod文件的has碼,使用時注意將中間的空格刪除,本例中得到的結果是:
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 。

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

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

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

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

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

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

如果無法加載的話也會有對應的錯誤,出現無法加載錯誤的話有可能是注冊表中輸入的dll路徑不對,需要檢查Transcoder項的內容。如果在Dispatcher日志中根本沒有發現transcoder相關的日志,則說明注冊表配置沒有生效,有可能注冊表項的名稱或者是位置不對,需要檢查注冊表中的Transcoder項是不是在正確位置。
測試結果
對應用進行測試會發現,如果客戶端和服務器端都正確部署了Transcoder加密解應用,智能手機可以正常收發郵件。因為本例中的Transcoder加密解應用是一個“空”的加解密應用,并沒有對數據進行加解密操作,所以在測試過程中的表現和沒有部署Transcoder加密解應用的情況相同。不過,讀者可以從Transcoder日志(本例為c:\transcoder\Transcoder-Log.txt)中看到所有服務器和客戶端交互的數據都被Transcoder加密解應用載獲了。
代碼分析
服務器端代碼分析
為了讓讀者更好地了解Transcoder API的使用,下面對服務器端代碼進行分析。
#include"stdafx.h"
#include"BEST ranscoder API.h"
#include
- FILE*logFile;
charLogFileName[64]="c:\\Transcoder\\Transcoder-Log.txt";以上代碼為日志文件句柄與日志文件名的定義,日志操作在Transcoder API的使用中并不是必要的,本例為了讓應用執行有更加明顯的,可以跟蹤的結果,所以通過日志文件記錄相關信息。本例中的日志文件名硬編碼為“c:\\Transcoder\\Transcoder-Log.txt”,讀者可以根據測試環境的情況進行修改,注意要調整變量LogFileName數組的上界。
- DEFINE_BES_TRANSCODER_DLL
以上這句為Transcoder API定義語句的引入,這句是Transcoder API使用的關鍵,為了讓頭文件“BESTranscoder API.h”中的相關定義可以在本程序中使用,必須通過語句DEFINE_BES_TRANSCODER_DLL將預定義好的相關元素引入。對c/c++中的預定義機制有疑惑的讀者可以閱讀相關文檔以了解這句語句的含義,當然,也可以不深究這句語句的語法,只記住在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應用的入口,按dll的載入機制,這個函數在服務器載入該dll時被調用。本例只是在標準輸出中輸出了“LoadingDll”,并沒有執行其它操作。
- __ 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。此外,本例的該函數還在日志文件中記錄了“tryingtocallLoadDll”字符串。在實際環境中這里可以加入加解密應用初始化的代碼,比如在這里可以加入連接CA獲取服務器密鑰的代碼。此函數返回0,表示加載成功,實際環境中此處可以根據初始化代碼的運行情況決定返回什么值,如果初始化失敗,則可以返回其它非0值。注意,如果返回其它非0值,該dll將不會被載入內存,相關的加解密方法也不會被調用。
- __ 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函數為dll釋放函數,可以加入連接關閉等資源釋放代碼。本例不需要釋放資源,所以在該函數中只是通過日志文件記錄了字符串“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函數需要返回本應用的ID,本例使用20作為應用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 )
- {
函數Encode用于對消息進行加密操作,注意參數有input,output,context,其中input為系統傳入的輸入流,output為傳給系統的輸出流,本函數的主要工作就是從input中獲取數據,進行加密操作,然后通過output傳送給系統。
- 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,調用input的Read方法將數據讀到變量readC中,然后將read C輸出到日志文件中進行記錄,同時調用output的Write方法將readC中的數據寫入到output輸出流中。通過不斷的循環可以將input中的所有數據傳送到output中。本段代碼是加密操作的關鍵,在本例中只是將數據原封不動地傳送到output輸出流中,在現實環境中需要在這里對數據進行處理,完成加密操作后才將數據寫入到output輸出流中。
- 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 )
- { 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函數中的循環,這里通過循環將input中的所有數據寫入到output中。同樣,在現實環境中需要對input中讀取的數據進行處理,完成解密后才寫入到output中。
- r et urn 0 ;
- }
最后本函數返回零表示解密成功。
手機端代碼分析
以下為手機端代碼分析:
手機端應用的關鍵是需要調用Transcoder Manager的register函數將加解密客戶端注冊到系統中。
- 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);
- }
- }
本段代碼的關鍵是新建一個My Transcoder類的實例,名為transcoder,然后調用Transcoder Manager的register將transcoder注冊到系統中。
- 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;
以上代碼為包定義語句和相關類的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的構造函數,適合加入初始化相關的代碼。注意在加入初始化代碼之前必須通過super語句調用父類的構造函數。而且要注意,調用super時要傳入客戶端加解密應用的ID。在以上章節描述Transcoder客戶端應用時提到客戶端應用需要通過getID方法的返回和服務器端應用相關的ID。在本例中,通過super的調用將ID傳給父類Transcoder,從而使用父類的getID函數,這樣就不用自己實現getID函數了。
- public void SetScreen(MyScreen screen) {
- this . screenscreen = screen;
- }
本函數是為screen屬性指定對象,會在My Transcoder實例化后被主屏幕類調用,主要作用是在My Transcoder實例中保存屏幕類的句柄,從而調用主屏幕的相關方法以刷新主屏幕顯示的內容。該函數在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用于對數據進行加密操作,因為對于input的read操作和output的wirte操作有可能會拋出導常,所以需要通過try,catch語句捕獲異常。在try語句段中定義了變量read Byte,通過調用input的read函數將數據讀入到read Byte中,然后再將read Byte中的數據寫入到output中。最后本函數返回true表示加密成功。
- public boolean willTranscode(IntHashtable context) {
- return true ;
- }
- }
Will Transcode函數用于確定是否對消息進行加解密操作,本例對所有消息都返回true,表示對所有消息都進行加解密操作。