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,表示对所有消息都进行加解密操作。