使用BlackBerry Transcoder API


1 使用 BlackBerry Transcoder API 集成第三方加密方案 作者: 邓明轩 2 目录 BlackBerry 平台的加密机制 .................................................................................................... 3 Transcoder API 的整体结构 ...................................................................................................... 4 服务器端 Transcoder API 介绍 ................................................................................................. 5 手机端 Transcoder API 介绍 ..................................................................................................... 7 创建服务器端程序 ................................................................................................................... 8 创建客户端程序 ..................................................................................................................... 15 加载客户端程序 ..................................................................................................................... 19 配置服务器端程序 ................................................................................................................. 21 测试结果 ................................................................................................................................. 22 代码分析 ................................................................................................................................. 23 3 BlackBerry 平台的加密机制 BlackBerry 平台自身带有完整的加密机制。所有数据从 BES(BlackBerry Enterprise Server)流 出前都做了加密处理,使用的是 AES 或者是 Triple-DES 的方式进行加密。在数据到达 BlackBerry 智能手机后手机端平台会对数据进行解密操作。同样,当数据从 BlackBerry 智能 手机流向服务器端时也通过 AES 或者是 Triple-DES 方式进行加解密操作。也就是说,在 BlackBerry 平台上,从 BES 服务器端到 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 的配置做详细介绍。 4 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 实现二次加密的关键在于如何开发和部署服务器端和手机端的加 解密应用。在下面的章节中我们会介绍接口的详细情况和开发过程。 5 服务器端 Transcoder API 介绍 服务器端的 Transcoder API 是以 c/c++头文件形式提供的,名为:“BESTranscoderAPI.h”开发 人员需要通过 c\c++开发工具将“BESTranscoderAPI.h”文件引入项目中,使用 c 语言对指定 方法进行实现,最终通过 c\c++编译工具形成一个 dll 文件。 当开发人员部署所开发的服务器端加解密应用时需要将所生成的 dll 文件拷贝到 BES 服务器 上 , 并 通 过 注 册 表 “ HKEY_LOCAL_MACHINE\SOFTWARE\Research In Motion\BlackBerry Enterprise Server\Dispatcher\Transcoder”指向 dll 文件所在的位置。 通过这样的配置,在 BES 服务器启动的时候会根据注册表找到开发人员所开发的 dll 文件并 调入内存。在 BES 服务器需要对数据进行处理时会调用 dll 内的方法,由开发人员所实现的 加解密方法对数据进行处理。 下面详细描述 BESTranscoderAPI.h 为服务器端 dll 程序定义的函数 函数: LoadDLL() 该函数在 BES 服务器加载这个 dll 时被调用,开发人员可以在这个函数中实现加解密程序所 需要的初始化代码。 函数定义: int __cdecl LoadDLL() 参数: 无 返回值: 返回 0 表示加载成功,返回其它值表示加载失败,该函数的返回值由开发人员指定,用于告 诉 BES 服务器本 dll 是否成功加载了相关内容。 函数: FreeDLL() 该函数在 BES 服务器释放该 dll 时被调用,开发人员可以在这个函数中加入对象释放等资源 回收代码。 函数定义: void FreeDLL() 参数: 无 返回值: 无 函数: GetID() 该函数用于返回服务器端加解密应用程序的 ID,这个函数的返回值作为该应用的标记,所 以它必须和客户端的对应 getID 方法返回相同的值,让 BlackBerry 平台可以找到对应的解密 应用。 函数定义: unsigned char __cdecl GetId() 6 参数: 无 返回值: 返回一个 unsigned char 类型的值,不能为 0,开发人员可以根据约定任意指定一个非 0 的 unsigned char 值,前提就是服务器端返回的 ID 值和客户端返回的 ID 值相同。 函数: WillTranscode() 该函数让 dll 决定是否对相应的内容进行加解密处理。 函数定义: int __cdecl WillTranscode(const TranscoderContext *const context) 参数: context 用于获取传入的内容的上下文,从而判断是否需要对其进行加密。 返回值: 当该函数返回 0 时表明需要对内容进行加解密处理,返回其它非 0 值则表明不需要对内容进 行加解密处理。值-1 为保留字 TRANSCODE_ERROR ,在这里不能作为返回值使用。 函数: Encode() 该函数用于对数据进行加密,BES 服务器在发送数据前会调用这个方法,开发人员需要在这 个函数中实现加密算法。 函数定义: int __cdecl Encode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context) 参数: input 为 BES 服务器传入的内容,开发人员可以从中读取消息内容 output 为传给 BES 服务器的内容,开发人员需要将加密过的数据写入该参数中 context 消息上下文,用于获取消息内容主体以外的其它相关信息。 返回值: 当该函数返回 0 时表明加密成功,返回其它非 0 值则表明加密不成功。注意,如果该函数返 回非 0 值则传入的内容会被丢失。值-1 为保留字 TRANSCODE_ERROR ,在这里不能作为返 回值使用。 函数: Decode() 该函数用于对数据进行解密,BES 服务器在接收到数据后会调用这个方法,开发人员需要在 这个函数中实现解密算法。 函数定义: int __cdecl Decode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context) 7 参数: 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 类后所需要实现的函数有: 函数:getID() 用于获取客户端应用的 ID,开发人员可以在该函数中返回 0 以外的任意 byte 类型的值,前 提是返回值必须和服务器端应用 GetID 函数的返回值相同。 函数定义: public final byte getID() 参数: 无 返回值: 该方法返回一个 byte 类型的值作为客户端应用的 ID,如上所述,这里返回的 ID 必须和服务 器端应用 GetID 方法返回的值相同。 函数:willTranscode() 该函数让开发人员判断是否需要对传入的内容进行加解密处理。它的作用主要是过滤那些不 需要加解密操作的消息。 函数定义: public boolean willTranscode(IntHashtable context) 参数: 8 context 用于获取传入的内容的上下文,从而判断是否需要对其进行加密。 返回值: 返回 true 表示需要对内容进行加解密处理,返回 false 表示不需要对内容进行加解密处理。 函数: encode() 该函数用于对数据进行加密,BlackBerry 智能手机在发送数据前会调用这个方法,开发人员 需要在这个函数中实现加密算法。 函数定义: public boolean encode( InputStream input, OutputStream output, IntHashtable context) 参数: input 为 BlackBerry 智能手机传入的内容,开发人员可以从中读取消息内容 output 为 BlackBerry 智能手机的内容,开发人员需要将加密过的数据写入该参数中 context 消息上下文,用于获取消息内容主体以外的其它相关信息。 返回值: 当该函数返回 true 时表明加密成功,返回 false 则表明加密不成功。注意,如果该函数返回 非 0 值则传入的内容会被丢失。值-1 为保留字 TRANSCODE_ERROR ,在这里不能作为返回 值使用。 函数: decode() 该函数用于对数据进行解密,BlackBerry 智能手机在接收到数据后会调用这个方法,开发人 员需要在这个函数中实现解密算法。 函数定义: public boolean decode( InputStream input, OutputStream output, IntHashtable context) 参数: input 为 BlackBerry 智能手机传入的内容,开发人员可以从中读取消息内容 output 为传给 BlackBerry 智能手机的内容,开发人员需要将解密过的数据写入该参数中 context 消息上下文,用于获取消息内容主体以外的其它相关信息。 返回值: 当该函数返回 true 时表明解密成功,返回 false 则表明多彩密不成功。 创建服务器端程序 如以上章节所述,服务器端的加解密程序是以 dll 形式部署的,所以,要开发服务器端加解 密程序需要一个可以将 c 语言代码编译成 dll 的工具。为了方便,本例使用 Visual C++ 6.0 作 为开发工具,读者可以根据自己的习惯选用其它类似工具。 9 启动 Visual C++ 6.0,点击“File -> New”以创建一个新的项目,如下图: 点击“File->New”菜单后系统会弹出新建向导,选择“Projects”标签页以创建项目。在“Project” 标签页中选择“Win32 Dynamic-Link Library”以创建一般的 Win32 dll。在“Project Name”一 栏中输入项目名,本例为“MyTranscoder”,同时指定项目所在的文件目录,本例使用 “c:\workspace\vc6\MyTranscoder”作为项目所在的文件目录。保持其它选项,点击“OK”, 系统将创建一个名为 MyTranscoder 的项目,同时创建对应的 workspace。示意图如下: 在项目创建过程中向导会提示需要创建什么类型的 dll 项目,为了代码编写方便,我们选择 “A simple Dll project”,就是创建一个简单的 dll 项目,由系统生成一些基本的文件。选择后 点击“Finish” 10 向导结束后系统会出现提示框以确认项目的细节,点击“OK”关闭这个确认框。 MyTranscoder项目创建以后可以发现项目中有一些系统生成的文件,包括StdAfx.h,StdAfx.cpp 和 MyTranscoder.cpp。我们需要编辑的是 MyTranscoder.cpp,在这里完成 Transcoder API 的实 现。双击左边导航条中的“MyTranscoder.cpp”文件以打开该文件,可以看到系统生成了 DllMain 方法,这个方法是 dll 的入口方法,所以系统自动生成了该方法。如下图: 11 因为我们在项目中要使用 Transcoder API,所以下一步工作就是将 Transcoder API 提供的头文 件“BESTranscoderAPI.h”引入到项目中。“BESTranscoderAPI.h”文件随文档提供,读者也可 以在 BlackBerry 官方网站上下载。获取到“BESTranscoderAPI.h”文件后,在操作系统上将这 个文件拷贝到项目目录中,本例就是“C:\workspace\vc6\MyTranscoder”目录。然后,在 Visual C++ 6.0 界面中左边导航条中选择“Header Files”,点击右键,选择“Add Files to Folder…”, 然后选择刚拷贝的“BESTranscoderAPI.h”,这样就可以将头文件“BESTranscoderAPI.h”引入 到当前项目中。 引入头文件“BESTranscoderAPI.h”后结果如下图,有兴趣的读者可以双击该文件打开它, 了解一下头文件“BESTranscoderAPI.h”中所定义的方法。 12 引入头文件“BESTranscoderAPI.h”后,在左边导航条中双击“MyTranscoder.cpp”文件,编 辑 MyTranscoder.cpp 文件,使该文件的内容和似下代码相同。读者为了方便,可以将系统生 成的代码行删除,直接将以下代码粘贴到 MyTranscoder.cpp 文件中: // MyTranscoder.cpp : Defines the entry point for the DLL application. #include "stdafx.h" #include "BESTranscoderAPI.h" #include FILE * logFile; char LogFileName[64]="c:\\Transcoder\\Transcoder-Log.txt"; DEFINE_BES_TRANSCODER_DLL BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { printf("Loading Dll"); 13 return TRUE; } __declspec (dllexport) int __cdecl LoadDLL() { logFile = fopen(LogFileName, "a"); fprintf(logFile,"trying to call LoadDll"); return 0; } __declspec (dllexport) void __cdecl FreeDLL() { fprintf(logFile,"Dll free"); } __declspec (dllexport) unsigned char __cdecl GetID() { unsigned long TranscoderID=20; fprintf(logFile,"trying to get ID"); return (unsigned char) TranscoderID; } __declspec (dllexport) int __cdecl WillTranscode( const TranscoderContext *const context ) { return 0; } __declspec (dllexport) int __cdecl Encode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context ) { 14 fprintf(logFile,"testing encode is running now"); unsigned char readC; fprintf(logFile," /nencode read char:"); while (input->Read(&readC)) { fprintf(logFile,"%c",readC); output->Write(readC); } return 0; } __declspec (dllexport) int __cdecl Decode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context ) { fprintf(logFile," /ndecode read char:"); unsigned char readC; while (input->Read(&readC)) { fprintf(logFile,"%c",readC); output->Write(readC); } return 0; } 编辑代码后结果如下图: 15 最后,点击“Build->Build MyTranscoder.dll”菜单生成 dll 文件。使用 Visual C++ 6.0 的标准设 置 的 话 , 所 生 成 的 dll 文 件 可 以 在 项 目 的 Debug 目 录 中 找 到 , 本 例 就 是 c:\workspace\vc6\MyTranscoder\Debug 目录。 如果在编译过程中出现错误的话请根据控制台的错误提示对代码进行修改并重新编译。编译 成功后你就完成了服务器加解密应用的生成工作了,在后面的章节中会对代码进行详细解释。 这里提醒读者的是本例使用的样例代码是一个“空”的加解密代码,在加解密过程中只是单 纯地将输入的数据传到输出接口,在现实环境中使用的话开发人员的进一步工作就是修改样 例代码中的 Encode 和 Decode,使其可以对数据进行真正的加密和解密工作。 创建客户端程序 Transcoder API 客户端是以 BlackBerry 应用形式存在于智能手机的,所以,要创建客户端的 加解密应用,需要使用 BlackBerry 开发环境开发一个 BlackBerry 应用程序。 16 本例使用 BlackBerry JDE Plug-In For Eclipse 1.1 开发环境创建了一个名为 TranscoderClient 的 BlackBerry 项目。有关 BlackBerry 项目的创建过程以及 BlackBerry JDE Plug-In For Eclipse 的具 体使用,请参考相关文档,本章节只描述 TranscoderClient 项目创建过程中的关键步骤,不 对项目创建的每一个过程进行描述。 创建 BlackBerry 项 目 后 , 创 建 一 个 java 包 以 包 含 将 要 创 建 的 java 类 , 本 例 使 用 “org.bbtesting.transcoder”作为包名。然后在该包中创建 Transcoder API 客户端的入口程序, 名为 MainApp。 注意,一般而言 Transcoder API 客户端加解密应用会以自启动方式启动,不需要用户干预, 本例考虑到测试的便利性,通过应用程序图标的方式启动应用,然后通过菜单启动客户端加 解密线程。 在 MainApp 类文件中对代码进行修改,使其如以下代码: 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); } } 如果读者仔细阅读 MainApp 中的方法,可以发现该类主要是创建了应用程序入口,在程序 入口中创建了 MyScreen 类的实例,并将该实例显示出来。所以,下一步的工作就是要创建 MyScreen 类,读者在按以下步骤创建 MyScreen 类之前编译本项目的话会出现找不到类 MyScreen 的错误。 在包 org.bbtesting.transcoder 中创建类 MyScreen,创建过程中选择继承类 MainScreen,然后 编辑 MyScreen 类,使其如以下代码: 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; 17 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); } }); } } 在以上的 MyScreen 类中主要是创建一个屏幕,在该屏幕上添加一个菜单项,用户可以点击 这个菜单项启动客户端加解密应用。启动的过程就是将一个 MyTranscoder 类传递给系统, 让系统在处理所有数据的时候都调用这个 MyTranscoder 类。 因为我们在本项目中还没有创建 MyTranscoder 类,此时编译项目会出现找不到 MyTranscoder 类的错误。下面的工作就是要创建 MyTranscoder 类,这也是创建 Transcoder 客户端加解密 18 应用的最关键步骤。 在包 org.bbtest.transcoder 中创建名为 MyTranscoder 的类,创建过程中选择继承类 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 Transcoder { private MyScreen screen = null; public MyTranscoder() { super((byte) 20); } public void SetScreen(MyScreen screen) { this.screen = 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"); 19 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,没有签名的话将无法运行。有关客户端应用的签名密钥的申请和签名工具 的使用请参考相关文档。 对客户端程序进行签名后,可以通过 javaloader 将客户端程序的 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 码,下面是 TranscoderClient.cod 对应的 jad 文件 20 TranscoderClient.jad 的内容,其中第 9 行的 RIM-COD-SHA1 的内容就是该 cod 文件的 has 码, 使用时注意将中间的空格删除,本例中得到的结果是: 01f2524f00fa590896052556b6f7f15545027e52 。 如果是他人开发的 cod 应用,有时并没有附带提供对应的 jad 文件。这时可以通过命令 javaloader 得到一个 cod 文件对应的 hash 码。Javaloader 命令的格式如下: Javaloader siblinginfo 本例中为了更好地显示 javaloader 命令的输出,在命令行界面执行以下命令将输出的结果写 入文件 c:\temp\codinfo.txt 中: javaloader siblinginfo transcoderclient.cod > c:\temp\codinfo.txt 得到的 codinfo.txt 打开以后如下图: 其中 Hash 一栏显示的就是 TranscoderClient.cod 对应的 hash 码。 获取 cod 的 hash 码以后需要将该 hash 码配置到 IT Policy 中,编辑所创建的 IT Policy,选择 “Security”标签页,下图是在 BES 5.0 Web 管理界面中得到的载图: 在选择“Security”签标页后,滚动页面,找到“Security Transcoder Cod File Hashes”一栏, 将上面找到的 hash 码填入,如下图: 完成后保存该 IT Policy,为测试用户分配该 IT Policy,并向测试用户推送一次 IT Policy, 以 保证该 IT Policy 作用于该测试用户。 在完成 IT Policy 配置后,所安装的 TranscoderClient 应用就可以正常运行了,运行该应用后, 可以选择菜单中的“Start”菜单项启动客户端加解密应用。 21 在启动客户端加解密应用后,会发现该 BlackBerry 智能手机无法正常收发邮件,这是因为服 务器端没有部署对应的加解密程序。只有在服务器端也部署对应的加解密程序后整个 Transcoder 应用才能正常工作, 下面将描述服务器端应用的部署过程。 配置服务器端程序 如之前描述的,Transcoder 服务器端程序是通过 BES 服务器上的注册表配置的。要配置服务 器 Transcoder 程序,需要访问 BES 服务器,将我们创建的服务器端程序,也就是生成的 MyTranscoder.dll 拷贝到 BES 服务器上,本例将 MyTranscoder.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\MyTranscoder.dll”。 配置后的结果如下图: 完成该配置之后需要重启 BES 服务器的 Dispatcher 任务才能让所配置的 dll 调入内存,重启 的时候可以通过 Windows 的服务管理器重启,也可以通过 BES 管理界面重启整个 BES 服务 器。注意,因为在本例中的 MyTranscoder.dll 在载入时会尝试在目录“c:\transcoder”目录下 生成日志文件,所以读者如果使用本例的 MyTranscoder.dll,在重新启动 BES 服务器之前需 要手工创建目录:“c:\transcoder”,以避免程序出现错误导致 BES 服务器无法启动。下图为 BES 5.0 Web 管理界面中重启 BES 服务器的方法: 22 在服务器重启过程中注意观察 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 加密解应用载获了。 23 代码分析 服务器端代码分析 为了让读者更好地了解 Transcoder API 的使用,下面对服务器端代码进行分析。 #include "stdafx.h" #include "BESTranscoderAPI.h" #include 以上代码为头文件引入代码,主要是要引入名为“BESTranscoderAPI.h”,该头文件内定义了 Transcoder API 使用所需要的函数与常量等关键元素。 FILE * logFile; char LogFileName[64]="c:\\Transcoder\\Transcoder-Log.txt"; 以上代码为日志文件句柄与日志文件名的定义,日志操作在 Transcoder API 的使用中并不是 必要的,本例为了让应用执行有更加明显的,可以跟踪的结果,所以通过日志文件记录相关 信息。本例中的日志文件名硬编码为“c:\\Transcoder\\Transcoder-Log.txt”,读者可以根据测 试环境的情况进行修改,注意要调整变量 LogFileName 数组的上界。 DEFINE_BES_TRANSCODER_DLL 以上这句为 Transcoder API 定义语句的引入,这句是 Transcoder API 使用的关键,为了让头 文件“BESTranscoderAPI.h ”中的相关定义可以在本程序中使用,必须通过语句 DEFINE_BES_TRANSCODER_DLL 将预定义好的相关元素引入。对 c/c++中的预定义机制有疑惑 的读者可以阅读相关文档以了解这句语句的含义,当然,也可以不深究这句语句的语法,只 记住在 Transcoder API 使用时必须有这句语句也可以。 BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { printf("Loading Dll"); return TRUE; } 以上为本 dll 应用的入口,按 dll 的载入机制,这个函数在服务器载入该 dll 时被调用。本例 只是在标准输出中输出了“Loading Dll”,并没有执行其它操作。 __declspec (dllexport) int __cdecl LoadDLL() { logFile = fopen(LogFileName, "a"); fprintf(logFile,"trying to call LoadDll"); return 0; } 以上代码为 dll 载入代码,适合加入一些只需要在载入过程中运行一次的代码,如本例中日 志文件的打开只需要在 dll 载入时运行一次,所以在这里加入日志文件打开的语句 fopen。 此外,本例的该函数还在日志文件中记录了“trying to call LoadDll”字符串。 在实际环境中这里可以加入加解密应用初始化的代码,比如在这里可以加入连接 CA 获取服 务器密钥的代码。此函数返回 0,表示加载成功,实际环境中此处可以根据初始化代码的运 行情况决定返回什么值,如果初始化失败,则可以返回其它非 0 值。注意,如果返回其它非 0 值,该 dll 将不会被载入内存,相关的加解密方法也不会被调用。 24 __declspec (dllexport) void __cdecl FreeDLL() { fprintf(logFile,"Dll free"); } FreeDll 函数为 dll 释放函数,可以加入连接关闭等资源释放代码。本例不需要释放资源,所 以在该函数中只是通过日志文件记录了字符串“Dll free”。 __declspec (dllexport) unsigned char __cdecl GetID() { unsigned long TranscoderID=20; fprintf(logFile,"trying to get ID"); return (unsigned char) TranscoderID; } GetID 函数需要返回本应用的 ID,本例使用 20 作为应用 ID,所以返回 20。注意要确定返回 的值是 unsigned char 类型。 __declspec (dllexport) int __cdecl WillTranscode( const TranscoderContext *const context ) { return 0; } WillTranscode 用于确定是否需要对消息进行加解密操作,返回非零值表示不需要进行加解密 操作,本例对所有消息都返回 0,表示对所有消息都需要进行加解密操作。 __declspec (dllexport) int __cdecl Encode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context ) { 函数 Encode 用于对消息进行加密操作,注意参数有 input,output,context,其中 input 为 系统传入的输入流,output 为传给系统的输出流,本函数的主要工作就是从 input 中获取数 据,进行加密操作,然后通过 output 传送给系统。 fprintf(logFile,"testing encode is running now"); 以上代码在日志文件中输出“testing encode is running now”,用于记录 Encode 事件。 unsigned char readC; fprintf(logFile," /nencode read char:"); while (input->Read(&readC)) { fprintf(logFile,"%c",readC); output->Write(readC); } 以上代码定义了变量 readC,调用 input 的 Read 方法将数据读到变量 readC 中,然后将 readC 输出到日志文件中进行记录,同时调用 output 的 Write 方法将 readC 中的数据写入到 output 25 输出流中。通过不断的循环可以将 input 中的所有数据传送到 output 中。 本段代码是加密操作的关键, 在本例中只是将数据原封不动地传送到 output 输出流中,在 现实环境中需要在这里对数据进行处理,完成加密操作后才将数据写入到output输出流中。 return 0; } 最后,本函数返回零表示加密成功。 __declspec (dllexport) int __cdecl Decode( TranscoderInputStream *const input, TranscoderOutputStream *const output, const TranscoderContext *const context ) { D fprintf(logFile," /ndecode read char:"); unsigned char readC; while (input->Read(&readC)) { fprintf(logFile,"%c",readC); output->Write(readC); } 以上代码定义了变量 readC,类似于 Encode 函数中的循环,这里通过循环将 input 中的所有 数据写入到 output 中。同样,在现实环境中需要对 input 中读取的数据进行处理,完成解密 后才写入到 output 中。 return 0; } 最后本函数返回零表示解密成功。 手机端代码分析 以下为手机端代码分析: 手机端应用的关键是需要调用 TranscoderManager 的 register 函数将加解密客户端注册到系 统中。 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); 26 } } 本段代码的关键是新建一个 MyTranscoder 类 的 实 例 , 名 为 transcoder , 然 后 调 用 TranscoderManager 的 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 { 以上为MyTranscoder类的类定义语句,声明MyTranscoder类是Transcoder类的子类。 private MyScreen screen = null; 以上为类属性定义,本例只定义了screen一个属性,用于更新主屏幕。 public MyTranscoder() { super((byte) 20); } 此处为MyTranscoder的构造函数,适合加入初始化相关的代码。注意在加入初始化代码之前 必须通过super语句调用父类的构造函数。而且要注意,调用super时要传入客户端加解密应用 的ID。在以上章节描述Transcoder客户端应用时提到客户端应用需要通过getID方法的返回 和服务器端应用相关的ID。在本例中,通过super的调用将ID传给父类Transcoder,从而使用 父类的getID函数,这样就不用自己实现getID函数了。 public void SetScreen(MyScreen screen) { this.screen = screen; } 本函数是为screen属性指定对象,会在MyTranscoder实例化后被主屏幕类调用,主要作用是 在MyTranscoder实例中保存屏幕类的句柄,从而调用主屏幕的相关方法以刷新主屏幕显示的 内容。该函数在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(); 27 } 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语句段中定义了变量readByte, 通过调用input的read函数将数据读入到readByte中,然后再将readByte中的数据写入到 output中。最后本函数返回true表示加密成功。 public boolean willTranscode(IntHashtable context) { return true; } } WillTranscode 函数用于确定是否对消息进行加解密操作,本例对所有消息都返回 true,表示 对所有消息都进行加解密操作。
还剩26页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

BlackBerry

贡献于2010-09-16

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf