使用BlackBerry Transcoder API集成第三方加密方案(三)
来源:互联网 发布:炫浪网络社区旧版 编辑:程序博客网 时间:2024/05/20 14:15
作者: 邓明轩
创建客户端程序
Transcoder API 客户端是以 BlackBerry 应用形式存在于智能手机的,所以,要创建客户端的 加解密应用,需要使用 BlackBerry 开发环境开发一个 BlackBerry 应用程序。
本例使用 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;
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 客户端加解密
应用的最关键步骤。
在包 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");
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 文件
TranscoderClient.jad 的内容,其中第 9 行的 RIM-COD-SHA1 的内容就是该 cod 文件的 has 码,
使 用 时 注 意 将 中 间 的 空 格 删 除 , 本 例 中 得 到 的 结 果 是 :
01f2524f00fa590896052556b6f7f15545027e52 。
javaloader 得到一个 cod 文件对应的 hash 码。Javaloader 命令的格式如下:
Javaloader siblinginfo <cod 文件名>
本例中为了更好地显示 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 配置后,所安装的 TranscoderClient 应用就可以正常运行了,运行该应用后, 可以选择菜单中的“Start”菜单项启动客户端加解密应用。
在启动客户端加解密应用后,会发现该 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 服务器的方法:
在服务器重启过程中注意观察 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 "BESTranscoderAPI.h"
#include <iostream> 以上代码为头文件引入代码,主要是要引入名为“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 将不会被载入内存,相关的加解密方法也不会被调用。
__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
输出流中。通过不断的循环可以将 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 函数将加解密客户端注册到系 统中。
25
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);
}
}
本 段 代 码 的 关 键 是 新 建 一 个 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();
}
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语句段中定义了变量readByte, 通过调用input的read函数将数据读入到readByte中,然后再将readByte中的数据写入到 output中。最后本函数返回true表示加密成功。
public boolean willTranscode(IntHashtable context) {
return true;
}
}
WillTranscode 函数用于确定是否对消息进行加解密操作,本例对所有消息都返回 true,表示 对所有消息都进行加解密操作。
BlackBerry SDK下载
相关链接:
使用BlackBerry Transcoder API集成第三方加密方案(一)
使用BlackBerry Transcoder API集成第三方加密方案(二)
使用BlackBerry Transcoder API集成第三方加密方案(三)
- 使用BlackBerry Transcoder API集成第三方加密方案(三)
- 使用BlackBerry Transcoder API集成第三方加密方案(一)
- 使用BlackBerry Transcoder API集成第三方加密方案(二)
- 第三方API使用方案
- Android使用官方API分享内容到QQ和微信(非第三方集成)
- Blackberry 上如何使用第三方 Library (jar)
- 毕业设计使用第三方api
- 支付宝的集成与使用(第三方)
- Android实战——第三方服务之Bmob后端云的推送服务的集成和使用(三)
- iOS集成第三方登录的使用
- Cocos2d-Lua(Quick-Cocos2d-x)集成第三方SDK(三)
- 即时通信(集成第三方)
- 集成通讯录(第三方框架RHAddressBook)
- 第三方支付集成
- 第三方支付集成
- Android 上线前的代码混淆之(三)第三方加密
- BlackBerry平台加密安全机制(Crypto API)
- 关于BlackBerry API的使用
- 树,B树,B-树,B+树,B*树
- 接收不到消息WM_LBUTTONDBLCLK的解决方法
- Yslow技术
- PHP unlink() 函数
- PL/SQL导出数据库脚本 转帖
- 使用BlackBerry Transcoder API集成第三方加密方案(三)
- Dij + 优先队列 poj 1724 Road
- jacorb编译idl命令(含include)
- Android opencore编译问题总结
- Visual Studio 2008项目模板丢失的解决办法
- 敏捷需要公平,ThoughtWorks 确实做到了,前TWer感想。
- dom
- 输入规模决定算法
- 在kernel中编译单独的.ko