基于J2ME的金融POS应用安全方案设计

摘要:传统金融POS作为一种嵌入式设备,在符合PCI POS-PED安全标准上面临困难。J2ME是适合于嵌入式设备的Java平台,在此基础上扩展POS应用基础类库实现的POS应用方案,综合运用类属性、类编译、类控件和下载管理等安全策略,满足PCI POS-PED安全标准的各项要求。此方案已在NL8320金融POS上试用并通过安全测试。

关键词:金融POS;J2ME;安全

Abstract:As an embedded device, traditional financial POS has difficulty to meet PCI POS-PED security standard. J2ME is the Java platform that is suitable for embedded device. The POS application scheme which realized by expanding the POS application basic class libraries on J2ME, with security strategies of class property, class compiling, class widget and download control, meets all requirements of PCI POS-PED security standard. This scheme had been used in NL8320 financial POS and passed the security testing.

Keyword: financial POS;J2ME;security

1引言

POS(Point Of Sale)即销售点终端。金融POS是专门用于支持金融电子交易的嵌入式终端设备,一般安装在银行卡的特约商户和受理网点,能实现电子资金转帐功能,通常支持消费、预授权、查询、转帐等功能,具有方便快捷、安全可靠等特点。由于金融POS涉及到客户和银行的资金安全,因此设备的安全性是衡量金融POS的最关键指标。中国银联作为国内最大的金融POS用户,已将符合PCI POS-PED[1] [2] [3]安全标准作为金融POS的强制性要求。

传统金融POS在设备安全性方面做了大量工作,对于设备的物理安全、逻辑安全、生产运输等采用了许多成熟的设计方案,但这些并不能保证都能符合PCI POS-PED的安全要求。其中应用开发接口(API:Application Program Interface)就是一项软肋。传统POS的应用程序开发采用编译性的C语言,应用对各种外设及数据存储的访问通过API来实现。由于API不具备访问权限控制,因此在敏感设备、敏感数据、关键存储的安全管理等方面具有很大的局限性。如果开放基本设备存储操作,则难以阻挡非法程序的攻击;屏蔽这些底层功能,又给应用开发增加复杂程度。特别是恶意程序的指针窥探、缓冲区溢出等攻击破坏手段,将直接危害到整个系统的安全。

Java是由SUN公司推出的新一代面向对象的程序设计语言,是一种与平台无关、分布式、解释性、健壮安全、结构中立、可移植性强的多线程动态语言。由于POS属于小型嵌入式设备,资源有限,因此在POS设备上实现Java的方案是移植Java的Micro版本―J2ME[4] [5] [6] [7]。在J2ME的基础上,通过对应用基础类属性、应用编译库、关键应用控件等方面的安全设计,结合应用下载的安全管理,形成一套符合PCI POS-PED安全标准的金融POS应用方案。此方案已在NL8320金融POS上成功试用并通过了PCI应用案例的测试。

本文介绍了基于J2ME的金融POS应用安全方案的技术背景和方案概述,着重描述了为提高系统安全性而采取的各项安全策略,并以实际应用案例和测试结果证明该方案在金融POS的安全设计上是成功的。

2基于J2ME的

金融POS应用安全方案概述

Java最初被设计为面向Internet的分布式跨平台开发语言,但随着其流行与推广,从企业级平台到嵌入式设备,Java变得越来越无处不在。POS属于小型嵌入式设备,资源有限,适用于POS设备的是Java的Micro版本——J2ME[8]。

J2ME的应用程序虚拟机称为KVM[9](Kilobyte Virtual Machine),基本运行空间仅在128K左右,符合嵌入式系统的资源条件。根据硬件的差异,J2ME的基本配置分为两类:CDC(Connected Device Configuration,连接设备配置)和CLDC[10](Connected Limited Device Configuration,有限连接设备配置)。CDC面向高端的嵌入式信息家电如机顶盒、网络电视等,CLDC面向低端的嵌入式信息设备如移动电话、手持式PDA等。配置层之上是简表,针对某简表开发的应用程序可以在支持同样简表的任何设备上运行。目前主要规范了五种简表,面向不同的设备和配置。

MIDP(Mobile information devices profile)——主要面向移动电话

PDAP(Personal digital assistant profile)——主要面向Palm PDA

FP(Foundation profile)——用于不需要GUI的CDC设备

PF(Personal profile)——替代Personal Java的CDC简表

RMIP(RMI profile)——支持RMI的CDC简表

POS设备按配置适用于CLDC,但无论是MIDP还是PDAP由于主要面向手机、PDA等个人设备,对于运行金融应用的POS而言并不适合。因此,在POS上移植J2ME,首先需要移植基于CLDC配置的KVM,而后需要根据POS金融应用的特点定制一套合适的简表,从而能支持POS应用程序的开发。

移植CLDC[4]一般而言不存在困难,尤其是支持嵌入式Linux等平台的设备上。移植工作的重点在于如何设计一套适合于金融POS特点及金融应用需求的功能简表[6]。金融POS设备的特点一是外设比较多,除了键盘、显示屏等标准输入输出之外,还有磁条设备、打印设备、IC/SIM卡设备、通讯设备(有线Modem、无线Modem、以太网、RS232/485等)。二是安全要求高,尤其对于密钥的存储保护方面。

设计金融POS的功能简表时,基于以下几条原则和方法,将有利于提升设计质量和效果,更能有效地满足今后金融应用开发的需求。

● 设备功能独立化

物理外设各自独立,互不影响和串联。特殊功能通过封装实现虚拟设备化。

● 驱动接口同一化

所有设备(包括虚拟设备)采用同样的接口,实现访问一致性,具有良好的扩充性。

● 应用流程消息化

用消息机制来驱动应用程序流程,既简化了应用开发,又提升了系统效率。

● 应用界面控件化

应用界面完全由控件形成,控件支持继承派生。控件化不仅有利于简化程序,更能有效地实现敏感关键过程的封装。

● 支持设备并发、线程并发

在技术上实现多设备、多线程的并发功能,将大大提升应用程序的整体性能。

根据上述最终实现的金融POS系统软件结构如图1所示。

3金融POS应用安全策略

通过移植J2ME实现金融POS的功能简表只是实现金融POS应用安全方案的基础。为了满足PCI POS-PED安全标准的各项规范和要求,必须在应用方案的各个层面都贯彻安全第一的中心思想[5]。在J2ME的基础平台上,应用开发方案还需要重点实施以下几项应用安全策略:

3.1 类属性的安全设计

J2ME虚拟机的解释执行特性只是隔离了应用程序代码和底层固件代码,POS功能简表提供的基础类还需运用一定的访问权限控制技术。J2ME支持完整的Java语法,而Java语法中对于Java类、类属变量、类方法均有访问权限类型修饰符[8]。访问类型修饰符共有四种,分别为:

a)Public——开放类型,其修饰的类、类属变量及方法,包内及包外的任何类均可以访问

b)Protected——保护类型,其修饰的类、类属变量及方法,包内的任何类,及包外的那些继承了此类的子类才能访问

c)Private——私有类型,其修饰的类、类属变量及方法,包内包外的任何类均不能访问

d)Friendly——友善类型,其修饰的类、类属变量及方法,包内的任何类都可以访问它,而包外的任何类都不能访问它,包括包外继承了此类的子类

另外,还有 final、static 等属性可以综合利用,配合上述修饰符共同实现有效的访问权限控制。充分利用Java语法的访问类型修饰符,可以有针对性地保护和屏蔽基础类中不适宜向外界开放的功能,提升了基础类库的安全性。

在基础类库的设计中,首先要保证基础类包和应用程序类包的充分隔离。然后在此基础上,通过设定不同的访问权限修饰符,规范各个基础类的开放性。例如某基础类用于操作某关键设备,一般将该类设定为friendly属性。该属性允许在其之上派生高一层的基础类,同时又禁止应用程序继承该基础类。该类的内属变量通常设为“protected”,既实现了类自身的封装性,又不影响继承类的操作。对于面向应用程序开放的基础类,可将其属性设定为“public final”,既不影响应用程序的对该类的使用,又能避免应用程序对该类恶意继承造成内部数据的泄露和破坏。

例如屏幕控件类的共同基类 UIBaseItem,示例代码如下:

packagecom.nl.epay ;

abstractclassUIBaseItemextendsUIBaseClsimplementsUIMessage,UIBaseText{

/**

*获取当前控件的父窗口句柄

*@return父窗口的 UIBaseForm 类型句柄

*/

protectedfinalUIBaseFormgetParentForm( ) ;

/**

*设置当前控件的文本信息

*@paramtext以 String 类型表示的文本信息

*@return是否设置成功

*/

publicbooleansetText( String text ) ;

/**

*获取当前控件的文本信息

*@return以 String 类型表示的文本信息

*/

publicStringgetText( ) ;

···

} ;

UIBaseItem的基本属性为abstract和friendly,表示其只能被同一个package内的类所继承,且其本身不可用于创建实例。类中的方法getParentForm被限定为protected final属性,既限定了使用该方法的对象只能是UIBaseItem的派生类,同时又防止了派生类对该方法的重载,保护了内部数据的安全性。在UIBaseItem上可以派生出如下控件类:

packagecom.nl.epay ;

publicclassUIItemLabelextendsUIBaseItem {// 标签控件

publicfinalbooleansetText( String text ) ;// final

publicfinalStringgetText( ) ;// final

···

} ;

publicclassUIItemButtonextendsUIBaseItem {// 按钮控件

publicfinalbooleansetText( String text ) ;// final

publicfinalStringgetText( ) ;// final

···

} ;

publicclassUIItemEditextendsUIBaseItem {// 编辑框控件

publicfinalbooleansetText( String text ) ;// final

publicStringgetText( ) ;// No final

···

} ;

····

这些控件同属一个package,因此可以继承UIBaseItem。这些控件类均为public属性,应用程序可以通过new来创建实例并加以使用。每个控件都重载了setText和getText方法,实现了特定的功能。根据应用和安全需求,不应当再被重载的方法直接设置成final属性。

3.2 类编译的安全设计

由于Java本质上是一种解释性语言,虽然源程序会编译成二进制的.class字节码文件,但由于反编译极其容易,.class 实质上和 .java 一样透明。在实现金融POS应用简表的基础类后,需要向应用开发人员提供 .class 类库文件以支持其应用程序的编译。但考虑到这些基础类的 .class 可以轻易地被转换为 .java 源码,向应用开发人员提供编译环境反而产生了“泄密”的嫌疑。

为此,J2ME的类编译特性需要被充分利用起来。J2ME的应用程序由javac[8] [10]程序编译,并经由preverify[8] [10]程序进行预处理。在此过程中,基础类的 .class 文件是必须提供的,否则编译器无法解析应用程序对基础类的引用且preverify也无法判断该引用是否合法。但经过实践分析,javac和preverify对类引用的检查只关注引用名称的存在与否和类型是否匹配,而并不关注该引用的实际内容。基于此原理,解决 .class 信息泄露的方案可以如下实现。

首先,完整的基础类库仅用于POS设备的下载,依靠下载安全管理系统来保证其不被外界窥探和读取。其次,将基础类源码中的所有实际代码删除,仅保留最少的类方法名称,然后编译成面向应用开发人员的编译专用基础类库。这套类库即使反编译,除了能看到允许公开的public方法以外,实质性的代码一概没有。而用这套类库编译编译后的应用程序,下载到POS设备后,依然可以正确地引用完整的基础类库,实现真实的程序效果。

例如,组分密钥输入功能是一项关键性的操作,PCI POS-PED标准对其实施状态、输入数据、操作时间等具有明确而严格的规定。实现组分密钥输入功能的Java类是KeyGroupForm,其源码示例如下:

package com.nl.security ;

public final class KeyGroupForm extends BaseManageForm{

/**

* 参数数组

* 说明:

* m_params[0]密钥组号

* m_params[1]密钥模式

* m_params[2]返回代码

*/

protectedint[]m_params ;

/**

* 当前应用的运行步骤

*/

protectedintm_step ;

/**

* 存储组分口令和密钥的字符串数组(密文的Hex格式)

*/

protectedString[]m_pwkeys ;

/**

* KeyGroupForm类的构造函数

* @param params应用传递给本Form的参数数组

*/

publicKeyGroupForm( int[] params );

···

···

} ;

由于该类中具有保存敏感数据的变量字段,如果直接向应用开发者提供该源码编译出来的KeyGroupForm.class文件,很容易被攻击者破解、猜测或利用。因此生成应用开发人员编译专用的基础类库应该采用删掉所有细节代码的空壳源码类,如下示例:

package com.nl.security ;

public final class KeyGroupForm extends BaseManageForm{

publicKeyGroupForm( int[] params );// 构造函数

} ;

上述源码已不包含细节代码,攻击者从该源码生成的KeyGroupForm.class文件中无法获取任何有用信息。同时,该class文件完全满足应用开发者的Java程序编译检查的需求,不会导致应用程序的编译错误或失败,也不影响应用程序在实际POS上的运行效果。

3.3 类控件的安全设计

PCI POS-PED安全的核心,在于个人密码(PIN[2] [3])和访问控制。如何保证PIN的安全,是通过PCI POS-PED认证的最大难题。对于嵌入式手持POS而言,PIN就在POS的键盘上直接输入。这里自然就产生了一个矛盾:应用程序肯定需要能读取POS键盘的输入,数字键用于表示金额,功能键用于切换菜单等等;但同时应用程序又绝对不能读取到PIN输入的数字键,即PIN的明文。

解决上述矛盾的常用方法有:键盘使用受控、键盘数据加密、多层权限管理等,但在实际使用中不是应用复杂度太大就是安全性多少有漏洞。分析PIN安全输入的核心,实质上是要求PIN的键盘输入和加密PIN Block的输出过程实现“原子”化,形成不可分割的过程封装,从而避免PIN明文的泄露。控件就是实现过程封装“原子”化的重要手段。

专门编制的PIN输入控件,内部封装了一系列PIN输入输出过程。当用户进入输入PIN状态时,会触发明显且独特的状态提示(如屏幕边框闪烁、特殊蜂鸣器声响、滚动的PIN输入提示信息等);用户输入的键码,由控件内部存储和转换;用户输入完毕,应用程序只能通过约定的共用接口从PIN控件中获取加密后的PIN Block数据。PIN控件内部实现了状态提示、键盘输入、PIN加密、PIN Block输出、敏感数据清除等一系列逻辑过程,而这些内部过程依靠Java访问权限修饰的私有属性设置,阻止了应用程序对控件内部的窥探和修改。为了提高整体效率,具体实现中将上述控件的逻辑过程直接置于底层固件中实现,而Java控件仅需对固件接口进行简单的调用。此方案不仅能提高系统的整体运行速度,而且由于Java控件中不需包含核心过程和加密算法代码,不需存储敏感数据明文,安全性得以大幅提高。即使控件的 .class 文件遭遇窃取和破译,也不至于导致系统安全方面的损害和知识产权方面的泄密。

通过将用户输入输出涉及的功能、过程等全面控件化,有效地解决了敏感数据(PIN)输入的安全性和非敏感数据输入的便利性两者之间的矛盾。PIN控件专门用于PIN输入过程,具有特殊的安全提示信息,输入显示为“*”号,输出只有PIN Block密文;普通Edit控件用于一般的字符输入,输入直接显示明文,输出也是明文信息;一般Password控件用于输入非关键性口令等,输入显示为“*”号,但安全提示信息明确指明其与PIN控件的区别,以确保用户不会错误输入PIN。应用程序只能通过上述控件接受用户的输入,无法通过其他途径伪造出PIN输入画面和效果从而套取用户PIN。

PIN输入控件的源码PINInputForm.java示例如下,其中使用了加密类Crypt和PIN控件类UIItemPIN。

packagecom.nl.security ;

classCrypt {

/**

*初始化密钥

*@paramiAppid应用ID

*@paramiGroupId密钥组ID

*@return是否创建成功

*/

staticfinalbooleanKeyInit(int iAppId , int iGroupId ){

/**

*明文加载密钥

*@paramiAppid应用ID

*@paramiGroupId密钥组ID

*@paramiKeyType密钥类型

*@paramiKeyLen密钥长度

*@paramPin1 认证口令1

*@paramPin2认证口令2

*@paramKey1密钥1

*@paramKey2密钥2

*@return是否成功加载密钥

*/

staticfinalintKeyPlainLoad(int iAppId, int iGroupId, int iKeyType,int iKeyLen, String Pin1, String Pin2, String Key1, String Key2) ;

} ;

classUIItemPin extendsUIItemEdit{

/**

*初始化密码控件

*@paramwid密码控件宽度

*@paramhig密码控件高度

*@paramtext主帐号

*@paramstyle密码控件风格

*/

UIItemPin( int wid , int hig , String text , int style ) ;

/**

*获取密码控件的密码密文

*@return以 String 类型表示的密码密文

*格式为密文的Hex表示方式

*/

publicfinalStringgetText( ) ;

/**

*设置Pin加密模式

*@paramtypePin加密模式(255用于Key,其余用于PIN)

*@return是否设置成功

*/

finalbooleanSetPinBlockType(int type ) ;

/**

*设置Pin密钥组ID

*@paramidPin密钥组ID

*@return是否设置成功

*/

finalbooleanSetPinGroupId(int id ) ;

/**

*设置Pin认证码

*@paramAuthCode认证码

*@return是否设置成功

*/

finalbooleanSetPinAuthCode(String AuthCode ) ;

} ;

public final class PINInputForm extends UIBaseForm{

/**

* PIN 控件

*/

protectedUIItemPinm_PIN ;

/**

* PINInputForm类的构造函数

* @param params参数数组,其中:

* params[0][in]标题栏

* params[1][in]密钥组号(int类型的字符串表示)

* params[2][in]加密类型(int类型的字符串表示)

* params[3][in]主帐号

* params[4][in]授权码

* params[5][out]PIN block

* @param lang语言集,0=汉语,1=英语

*/

publicPINInputForm( String[] params , int lang ) ;

} ;

3.4 应用下载的安全管理

设计得再怎么安全的Java基础类库,如果没有程序发布、安装、校验、授权等一系列外部机制的配合,一样会被攻击者轻易地篡改或绕过。配合金融POS应用方案,必须要有一套完善的应用程序发布机制来支撑,核心就是Java程序的下载安全管理机制。

下载到金融POS设备的Java程序分为两种,一种是基础类库包,一种是应用程序类包。如前所述,基础库类包涉及到关键设备、敏感数据、核心流程等与设备安全密切相关的部分,因此其安全类别应当等同于底层固件。应用程序类包只包含应用功能和流程,对敏感数据和关键设备的操作依靠基础类的控件,因此其安全级别属于应用级。因此,设计下载安全管理机制,其核心就在于如何严格区分这两种不同的下载级别。在实践中,可以采取如给不同下载级别分配不同的下载控制密钥、规定不同级别必须存储在不同的区域、不同级别的下载数据使用不同的签名和校验算法等,将两者严格地加以区分对待。

基础类包,在安全层面与底层固件处于同一水平。基础类包的开发、维护、升级、发布必须由拥有底层固件控制权限的人员实施,提交的版本必须加密并用固件私钥签名。执行基础类包安装下载前必须获得固件下载授权许可,实践中可采用口令、IC卡、U盾等方法和工具进行授权控制。下载到POS内部的基础类包必须经过严格的校验过程,通过检查数字签名来核实软件版本的真实有效性。只有通过层层审核的基础类包,才会最终被安装到正确的存储区域中,以供应用程序的继承和调用。

应用程序类包,其安全级别属于应用级。凡是被授权允许进行应用开发和下载的,都可以开发自己的应用并装载进POS。应用程序下载进POS设备时,首先必须核对其被授权内容和项目,应用程序的名称、功能、存储区域、入口参数等必须与授权书相匹配。应用程序同样需要加密和签名,但密钥均由授权书所规定,不同的应用必须使用不同的密钥组。

目前实现的金融POS安全下载管理软件是自主开发的NSync,采用了Window Shell技术,可以使得POS设备上的文件系统象U盘一样挂接到PC的资源管理器中。

NSyne的安全密钥体系结合了对称密钥和非对称密钥的特点,既保证了安全又不失效率。POS端的firmware将严格核对NSync的登录密钥,如果验证通过,则出现NSync窗口,显示出当前POS机内的文件目录。用户可以使用鼠标拖拉或剪贴/拷贝的方式将目标文件下载至当前下载权限许可的目录内。

4结论

基于J2ME的金融 POS应用安全方案经过长期的试验和论证,已经成功地在Newland公司的NL8320金融POS上应用成功。采用此方案开发的PCI POS-PED送检程序,经受了严格的安全测试。安全测试的项目参照PCI POS-PED SR v2.0的条目,测试用例主要参考DTRS中的测试准则和中国银联对安全POS的测试案例,采用DTRS的布局风格编写。PCI测试首先从审核PCI_POS_Eval_Vendor_Questionnaire[2](问卷调查表)和PCI_POS_Security_Requirements[3](PCI勾叉表)以及设计文档开始,共规划了安全自检测试、逻辑异常测试、检查固件升级认证管理、固件的远程升级测试、PIN输入期间显示测试、清除内部缓冲区测试、敏感服务的保护测试、敏感服务的限制测试、随机数测试、PIN穷举探测测试、密钥管理测试、加密算法测试、密钥专用性测试、密钥明文的安全性测试、交易控制测试、密钥替换测试、消息鉴别功能测试等共计 17 种项目62条测试案例。NL8320顺利地通过了上述全部测试项目。具体测试内容和结论参见“NL8320软件测试报告(适用于PCI 2.0认证)”。

纵观NL8320的测试表现,证明金融POS应用安全方案的设计是成功的。由于此方案从一开始便摆脱了传统基于C语言的API架构的束缚,引入J2ME作为开发平台,设计过程中充分考虑到PCI POS-PED安全标准的各项要求,综合运用了Java语法特点、J2ME编译环境、应用基础控件、下载管理器等各种手段方法,成功地构造了一个符合PCI POS-PED安全标准的金融POS应用安全平台。

参考文献

[1]刘岩.PCI标准及其应用现状的分析[J].信息安全与通信保密,2008,1(2) : 38-40

[2] VISA.Payment Card Industry(PCI): POS PIN Entry Device Evaluation Vendor Questionnaire Version 1.3[Z].2005.

[3] VISA.Payment Card Industry(PCI): POS PIN Entry Device Security Requirements Manual Version 1.3A[Z].2006.

[4]秦娟,杨文利,陈镘巨,郝庭柱,孙小薇,董吉虹.J2ME移植到嵌入式系统研究[J].天津理工大学学报.2006,22(6) : 35-38

[5]廖永刚,余东梅,张秋余.J2ME架构与安全机制的研究[J].计算机工程与设计,2006,27(4) : 575-577

(下转第68页)

[6]吴敏,刘萍.基于J2ME和J2EE的手机银行设计与实现[J].微计算机信息(嵌入式与SOC),2006,22(7-2) : 294-296

[7]吴庆,陆明泉,冯振明.基于J2ME的嵌入式系统的开发[J].计算机应用与软件,2005,22(2) : 35-36

[8][美]James Keogh.J2ME开发大全[M].潘颖,王磊译.北京 : 清华大学出版社,2004.

[9]Lindholm,Yellin T F.JavaTM virtual machine specification[EB/OL].Second Edition.Addison-Wesley : ftp://ftp.javasoft.com/docs/spec/vmspec.2nded.html.zip

[10]Sun Microsystems Inc.JSR-000139 Connected Limited Device Configuration 1.1 [EB/OL].https://jcp.org/aboutJava/communityprocess/final/jsr139/index.html

作者简介

刘蜂 ,中国科学技术大学96年毕业,福建新大陆电脑股份有限公司产品技术研究院 资深工程师。

推荐访问:方案设计 金融 J2ME pos