使 NetBeans 开发的移动应用程序更安全

安全 移动应用程序的定义取决于用户的看法。通常,该术语是指可加密或保护其处理的数据以避免误用或有意使用的应用程序。但作为开发人员,必须考虑安全性的其他两个方面。其一是确保应用程序具备必要的权限以正确行使功能。其二是保持应用程序源代码安全,防止被窃取。对于基于 Java 2 Platform, Micro Edition (J2ME) 的应用程序和按 Mobile Information Device Profile (MIDP) 标准编写的应用程序,这两个方面显得尤为重要。NetBeans IDE 使您能够方便地用这些方法保护 MIDP 应用程序。

准备

在本文中,我假设您已熟悉 MIDP 编程,并且使用 NetBeans IDE 4.0 和 NetBeans Mobility Pack 4.0 来创建 MIDP 应用程序。如果您没有 IDE 和 Mobility Pack,可以从 NetBeans.org 网站上的 J2ME MIDP Development for NetBeans IDE 4.0 下载。首先下载并安装 IDE,然后在同一目录中下载并安装 Mobility Pack。注意,NetBeans 可以方便地导入由不同的开发工具(如 J2ME Wireless Toolkit、Java Studio 或 Eclipse)创建的项目。

在继续以下操作之前,您应先创建新 MIDP 应用程序。有关说明,请参阅 NetBeans J2ME MIDP 开发快速入门指南。应用程序的类型应是 Mobile 应用程序。让 NetBeans 创建一个 "Hello" MIDlet。确保选择的设备配置文件是 MIDP 2.0,因为 MIDP 1.0 不支持安全权限。现在您可以深入了解移动应用程序的安全性了。首先我们来看看如何保护您的源代码。

混淆(Obfuscation):保护和收缩您的代码

英语动词 obfuscate 在一部 1913 年出版的韦伯斯特词典中作出了如下解释:

  1. 使黑暗;变模糊;使遮暗。
  2. 引申意:混淆;迷惑;使糊涂。

程序员长久以来一直在使用术语来描述难以阅读或维护的源代码。一些编程语言使得程序员很容易便编写出晦涩难懂的代码。 C 语言已经被 International Obfuscated C Code Contest (国际最难以理解的 C 语言代码竞赛)证明是一种规范的语言,该赛事自 1984 年以来一直在邀请参赛者来编写最晦涩难懂的代码。这种情况下,晦涩难懂主要是为了考验程序员创造性和智力。

但对于在 Java 平台上工作的程序员来说,晦涩难懂在保护程序源代码方面却具有高度的实际使用价值。.class 文件包括大量信息,这些信息可用于重构类的源代码,如所有变量的名称和类型。其中有些信息不是运行应用程序所需要的,但由于 Java 技术的动态性因素,所以大多数信息必需的。因此,已经开发了直接从类文件再造源代码的反编译器。在某种程度上,您可以使用称为 obfuscator 的工具,防止反编译器重构代码。

与反编译器一样,obfuscator 对类文件产生作用,而不是原始的源代码。不同的工具使用不同的技术,但在通常情况下,obfuscator 首先剥离不使用或不需要的代码,然后再重命名类、方法和变量。各选项控制如何剥离和重命名工具执行的对象。如果您正在编写类库,例如,您不想混淆公共部分或受保护的部分,那么库外的类将不能加载或调用库里面的类。要达到最大混淆效果,您通常需要一套封闭的类。MIDlet 套件是一个封闭集合,所以 MIDlet 通常从积极的混淆级别受益。

结果是类文件通常比原始版本要小,主要是因为长标识符被短标识符替代了。产生的副作用却是 MIDP 应用程序的一大优势,其中多数应用程序可运行在内存较少的设备上。更小的应用程序体积还意味着下载和安装可以更快更省时。无论是否为了保护源代码,混淆您的 MIDlet 套件 -- 单单从减少体积的角度来说,这个额外步骤也是非常值得的。

注意,混淆必须在 MIDP 建立序列中执行预验证之前 进行。否则,obfuscator 将删除预检器留在类文件中的重要信息,使这些信息变为无效。Java 解释器将无法加载它们。

有许多 Java obfuscator 可供使用,包括商用的以及开放源码的。NetBeans Mobility Pack 包含免费的 ProGuard obfuscator。

使用 NetBeans 混淆您的代码

NetBeans 认识到了混淆对于 MIDP 开发人员的重要性,在构建流程中直截了当地加入了混淆步骤。当您构建应用程序时,请密切观察构建窗口;您将发现在预验证之前会立即发生混淆。在 Ant 构建文件中,混淆步骤的目标名称是 obfuscate。除非您打算使用另一个不同的 obfuscator,否则没有必要修改构建文件的这个部分。通过修改 pre-obfuscatepost-obfuscate 这两个步骤,可以在混淆之前或之后立即指定其他处理事项。

默认情况下,NetBeans 不混淆您的类文件。要启动混淆,您必须先打开项目属性对话框。右键单击项目节点并从弹出菜单选择 Properties。在出现的对话框中,打开左侧窗格的 Build 节点并选择 Obfuscating 项:

图 1:设置混淆级别

右侧窗格的滑块控制混淆级别。默认级别为 0,表示不执行任何混淆。根据需要设置滑块 -- 滑块下方显示针对每个混淆级别的常规描述 -- 并按 OK 保存新的属性设置。重新构建应用程序以使其被混淆。

使用配置

当处于调试阶段时,请不要干扰对代码的混淆,因为 obfuscator 正在从类文件中剥离调试信息。混淆通常在生产构件时进行。通过创建单独的 project configuration,您就可以将这些代码与测试的产品区分开来。

配置使用不同的项目设置来构建同一项目的不同版本。每个配置都有一个名称。给定项目的默认配置称为 DefaultConfiguration。要管理项目配置,在项目属性对话框的顶端按 Manage Configurations 按钮。任何添加的新配置都从默认配置继承它们的设置,但您可以就事论事重写这些配置。

您可以使用 NetBeans Build 菜单设置在构建和测试过程中要用到的活动配置。也可以在一个项目中构建所有配置。每个非默认配置在构建配置后命名的独立子目录中构建其文件,这些子目录在标准 builddist 目录内。

要利用混淆,一个好的方法是使用两个项目配置,即默认配置用于调试,另一个配置用于生产。生产配置只需要重写构建设置 -- 关闭调试信息的生成并打开代码优化 -- 和混淆设置。

应用程序在混淆后请务必进行测试。混淆过程中有可能产生意外行为。您必须用各项设置进行实验以找出适当的混淆级别。

现在我们来看看权限和代码签名。

访问受保护的 API

MIDP 的第一个版本针对运行移动应用程序定义了一个精确的沙箱。MIDP 1.0 应用程序具有受限的系统访问权限,因此几乎没有什么应用程序可以行为不端。仅有的异常是网络通信,这会给设备用户带来实际开支。正因为会产生这些费用,所以设备常常要求用户允许进行此类连接,即使 MIDP 规范不要求它们这么做。如果用户减少了,就会引发一些异常,尽管程序可能不清楚为什么其连接尝试会被拒绝。

MIDP 2.0 具有更灵活的安全模型,该模型使用保护域权限 来控制哪个应用程序有权限访问受保护的 API。这两个概念紧密关联。

权限 是系统的守门人,在允许应用程序调用受保护的 API 之前系统都要对其进行检查。每个权限看起来都像是一个 Java 包名称。实际上,权限的第一部分必须匹配它保护的 API 的包名称。如果 API 包含在单个类中,类名称也是权限的一部分。例如,MIDP 2.0 定义 javax.microedition.io.Connector.http 权限来控制对 HTTP 连通性的访问。

在权限被授予给应用程序之前,您必须将它们放入 MIDlet 套件的应用程序描述符中。应用程序必需的权限放入到 MIDlet-Permissions 属性,可选的权限放入到 MIDlet-Permissions-Opt 属性。理论上来说,应在应用程序安装期间检测权限问题:如果应用程序管理软件无法授予必需的权限,安装将失败。如果安装成功了,但是虚拟机1发现在运行期间没有能力去授予所需的权限,则会引发 SecurityException

保护域 是一组允许 权限和用户 权限。允许权限被自动授予给保护域中的所有应用程序。用户权限在授予应用程序之前需要先得到用户确认。保护域外的权限完全不授予应用程序。

用户权限也有与之相关联的交互模式,控制询问用户为受保护的操作授予权限的频率。MIDP 标准定义了三种模式:oneshot,每次调用时提示用户,session,每次运行应用程序时提示用户一次,blanket,在设备上程序保持安装的整个时间段内仅提示用户一次。

MIDP 2.0 为非信任应用程序定义了一个特殊的非信任 保护域。该域允许应用程序调用由 MIDP 1.0 规范定义的任何 API,但有一个限定条件:用户必须确认对 HTTP 或 HTTPS 通信的任何尝试。

不信任域仅是一个需要 MIDP 设备支持的域,但是信任域 通常都是可用的。例如,移动电话通常具有由设备制造商、无线运营商或这两者定义的保护域。将应用程序与给定的保护域相关联现在已经实现,但是 MIDP 规范推荐基于数字签名和公钥证书的体系结构,称为 X.509 公钥基础设施(Public Key Infrastructure,PKI)。

签名和证书

X.509 PKI 定义的标准适用于基于证书的认证。HTTPS 使用 X.509 证书来识别会话的一个或两个端点,通常是针对 Web 服务器验证。要实现这个目标,Web 浏览器需要在称为证书存储区 的特殊存储库中安装一个或多个根证书。web 服务器将其证书发送到浏览器,后者然后对照根证书来检查证书以查看其是否由授权的实体所生成,该实体通常称为证书授权中心,授权验证 Web 服务器的身份。

使用 X.509 的 MIDP 安全也根据根证书进行解析。设备有一个或多个证书存储区。例如,在 GSM 移动电话上,设备自身有一个存储区,SIM 卡上的那个用于激活移动电话。这些存储区中的证书通常由设备制造商或无线运营商提供。每个证书都与保护域相关联。在安装时,对照存储区中的证书来验证应用程序。如果发现匹配证书,关联关系便确定应用程序的保护域和应用程序为信任的 MIDlet 套件,可以使用其保护域授予给它的任何权限。注意,如果应用程序无法通过验证则安装失败。

实际认证是使用 MIDlet 套件的应用程序描述符中的证书来完成的。这些以 base64 格式编码的证书有序地存储在名为 MIDlet-Certificate-<n>-<m> 的属性中,其中 <n> 是指证书路径<m> 是指路径中的证书。证书路径是最终通向根证书完成最后授权的证书序列。根证书本身从不包含在证书路径中;它只能在设备自身上找到。然后,带有单个证书(最低限度)的 MIDlet 套件定义属性 MIDlet-Certificate-1-1

证书仅凭其自身还不够。只有其应用程序描述符包含属性 MIDlet-Jar-RSA-SHA1 的 MIDlet 套件才能考虑用来认证。base64 编码的属性值是 MIDlet 套件 JAR 文件的数字签名,用于验证 JAR 文件本身是否完整。如果没有显示属性,则不执行认证并且 MIDlet 套件是安装在不信任域中。使用您维护的私钥生成签名,然后在应用程序描述符中的证书中嵌入对应的公钥,这样它就可以用于验证数字签名。

使用 NetBeans 给您的代码签名

正如您所见,构建信任 MIDlet 套件过程中有许多 详细信息。枚举应用程序需要的权限并不困难。关键是生成和编码必需的签名 -- 如果您没有获得任何帮助。?幸运地,NetBeans 使您能够方便地这个任务。

第一步是列出应用程序必需的和可选的权限。双击项目节点并从弹出菜单选择 Properties,打开项目属性对话框。打开左侧窗格的 Application Descriptor 节点并选择 API Permissions 项。

图 2:设置权限

现在添加权限:对于每个权限,按 Add 按钮并从下拉列表中选择。

图 3:添加权限

对于这个简单的测试,选择 javax.microedition.io.Connector.httpjavax.microedition.io.Connector.https 权限。添加它们后,取消选中 HTTPS 权限的 Required 复选标记,以表示您的应用程序需要 HTTP 但将 HTTPS 支持作为可选。

图 4:标记必需和可选的权限。

NetBeans 会自动将适当的 MIDlet-PermissionsMIDlet-Permissions-Opt 属性插入到项目的应用程序描述符。

您需要向 MIDlet 添加一些代码以测试 HTTP 连通性。您所需要的只是从 Command 事件调用的类似这样的一些简单代码:

...
HttpConnection conn = null;
final String url = "http://localhost/"; // dummy URL
        
Runnable r = new Runnable(){
    public void run(){
        HttpConnection conn;
                
        System.out.println( "Connecting to:" + url );
            
        try {
            conn = (HttpConnection) Connector.open( url );
            InputStream in = conn.openInputStream();
            in.close();
            conn.close();

            System.out.println( "Connection successful for " + url );
        }
        catch( IOException e ){
            System.out.println( "Connection error for " + url );
        }
        catch( SecurityException e ){
            System.out.println( "Security error for " + url );
        }
    }
};
        
new Thread( r ).start();
...
 

注意,该代码尝试连接到本地机器上运行的 Web 服务器 -- 但是你不需要 Web 服务器运行;您只是想查看是否引发安全异常。代码尝试独立线程上的连接,以避免死锁 MIDP 事件线程,然而在其他方面却相当简单。有关更多详细信息,请参见 J2ME 技术提示“Understanding MIDP System Threads”。

测试 MIDlet 后,再次打开项目的属性对话框,展开 Build 节点,并选择 Signing 项。这是你让 NetBeans 对 MIDlet 套件进行数字签名的地方。在右侧,选择 Sign Distribution 复选标记并将 Alias 下拉框设置为 untrusted 证书。

图 5:指定数字签名

在继续以下操作之前,先按 Export Key into J2ME SDK/Platform/Emulator 打开第二个对话框。在该对话框中,按 Export 按钮将证书信息从 NetBeans 内置键存储导出到 J2ME Wireless Toolkit 的键存储,将它与“不信任”安全域相关联。

图 6:导出安全信息

在导出键之后关闭对话框。现在,在项目属性对话框中选择 Running 项,然后选择 Execute through OTA 单选按钮。关闭对话框并运行项目。使用由 J2ME Wireless Toolkit 提供的 OTA Provisioning 仿真进行项目运行。仿真器启动后,激活 MIDlet 并使用适当的命令调用 HTTP 连接。因为你使用映射到不信任域的证书签署了 MIDlet,仿真器将询问您是否允许 MIDlet 通过 HTTP 进行连接。

图 7:获取建立连接的用户权限

如果选择“No”,则 Connector.open() 引发 SecurityException。否则,允许连接。以下是一些示例输出:

Starting emulator in execution mode
Running with storage root DefaultColorPhone
Storage name:#Vendor_#Security#Test_
Execution completed.
1288874 bytecodes executed
6 thread switches
739 classes in the system (including system classes)
5933 dynamic objects allocated (222144 bytes)
1 garbage collections (0 bytes collected)
Running with storage root DefaultColorPhone
Connecting to http://localhost/
Security error for http://localhost/

通过用不同的证书签署应用程序(这些证书映射到其他两个由 J2ME Wireless Toolkit 定义的保护域),来重复该测试。例如 minimal 域,拒绝所有权限,这样 OTA 安装将失败:

Starting emulator in execution mode
Running with storage root DefaultColorPhone
** Error installing suite (49): The suite is not authorized 
for javax.microedition.io.Connector.http Execution completed.

通过比较,信任域授予所有权限而没有用户干涉。

最后注意

在现实世界中,您需要获取用户运营商的适当安全证书或类似 Java 认证计划的第三方证书。开发人员证书有时可从设备制造商获取,尽管通常不适合用于一般应用程序分发。将这些证书安装在键存储中并使用它们给 MIDlet 套件签名。

有关权限和 MIDlet 安全性的详细信息,请参见 Mobile Information Device Profile 2.0 规范的第 3 章和第 4 章。文章“Understanding MIDP 2.0's Security Architecture”提供了完整摘要。

关于作者

 

你可能感兴趣的:(使 NetBeans 开发的移动应用程序更安全)