基于JAAS 和智能卡的通用身份认证模块的实现
文章出处:http://www.nexussmartsolutions.com 作者:曾海 人气: 发表时间:2011年09月28日
一、概述
传统的软件在实现身份认证功能过程中,往往自行开发专用的认证模块。由于不同的软件开发人员在信息安全方面水平参差不齐,实现的认证模块安全性往往得不到保证。认证和授权应当同业务逻缉分离一个通用的身份认证模块不仅能减轻软件开发人员的负担,更能保证认证的强度,确保在当认证方式发生改变时,应用程序不受影响。
二 JAAS认证和授权服务
认证和授权是两种最基本的安全机制,认证是简单地对一个实体的身份进行判断。而授权则是向实体授予对数据资源和信息访问权限的决策过程 Java 提供了一项提供认证和授权功能的标准服务。该服务即 JAAS (Java Authentication AutIlorization Service)。JAAS 强调通过验证谁在运行代码以及用户应该有的权限来保护系统不受攻击。使用 JAAS 可以把一些标准验证服务。如LDAP(轻量目录存取协议)和 Kerberos 等通过一种通用的,可配置的方式集成到系统中。
认证机制需要参数是为了确定用户的身份(对用户进行标识)。授权在认证之后进行,JAAS框架对由配置文件指定的认证模块进行了包装。如果认证成功。就会返回一个包含验证信息的用户标识,认证机制所返回的这个验证信息会用于授权过程。JAAS 体系结构如图一所示。其中验证模块可以通过配置模块替换。
图一
从身份认证的强度来看,基于PKI的智能卡身份验证提供了更好的安全性。因此通用的登录模块不使用Java提供的通用模块,而是实现了java.security.auth.spi.LoginMoudle接口的自定义模块,通过OCF框架存取智能卡,通过智能 实现验证过程。
三、基于智能卡的身份认证实现
智能卡是一种芯片卡,计算芯片镶嵌在一张名片大小的塑料卡片上,从而完成数据的存储与计算。可以通过读卡器访问智能卡中的数据。智能卡分为接触式卡和非接触式卡,接触式卡中又分为存储卡和微处理卡,后者更适合基于PKI的身份验证。
使用智能卡进行身份验证的可用方式包括基于微软平台的CSP(Cryptographic Service Provider)、PKCS#11和基于Java的智能卡开发平台OCF(Open card Framework)。OCF定义了从Java应用环境到智能卡问的通信标准。
从通用的角度看,OCF框架优点明显,它是是一套基于Java的应用程序编程接口,把不同的供应商的与读卡器交互的细节隐藏起来以简化编程,主流智能卡厂商均支持OCF框架,Java是跨平台的语言.因此为验证模块的运行提供了巨大的灵活性。身份认证使用了OCF的 CardTerminal 和CardService 层服务。前者提供了读卡器的接口API。后者使用File Access Card Service存取卡上文件,使用Signature Card Service提建立和验证数字签名.将证书导人智能卡,公钥导出智能卡。这是身份认证模块的核心功能之一。
OCF框架下的身份认证过程如图二:
图二
在卡和登录模块间的验证方式使用了传统的使用公钥加密体制的挑战/应答模式。卡上有自己生成的RSA密钥对。登录模块给出一个随机数。智能卡对它进行签名。而登录模块验证后就可获知用户身份
在实现过程中用户和公钥关联以及用户公钥分发均由PKI应用系统中的CA完成。CA签发数字证书进行了用户和公钥关联。证书可以放在用户卡上。也可以存在LDAP服务器中。登录模块必须有用户公钥才能进行验证。登录交互过程如图三所示。
图三
模块具体实现的核心方法是:
private boolean verifySign () throws java.security.Invalid·KeyExeepfion
本方法的主要功能是:
(1)取得智能卡上公私钥对的存储位置。
执行数字签名的时候需要使用私钥。虽然智能卡上的私钥不可导出,但可以取得一个指向私钥的引用。通过引用进行签名操作。某具体智能卡的密钥对存储在3F00:0600:0606,首先得到了密钥对位置:
CardFilePath EF_key=new CardFilePath(“:3F00:0600:0606”);
CardFilePath DF=new CardFilePatll (“:3F00:0600”);
智能卡中密钥对只能通过智能卡操作系统引用,而不能使用普通的密钥对引用方式,只能使用GPKSignatureKeyFile取到卡上密钥对引用:
GPKSignatureKeyFile KeyFfle = new GPKSignatureKeyFile
(ef_key,512,GPKRSAKeyFile.UNCERTIFIED_KEY);
(2)生成用于挑战的随机数。随机数生成利用了ScureRandom类,本类是一个高强度伪随机数生成器,即PRNG(pseudo-random number generator),它保证每次输出的值没有任何规律, 即符合RFC 1750 (Randomness Recommendations for Security)。使用时比较简单:
SecureRandom random=new SecureRandom();
Random.nextBytes(dataToSign);
(3)利用卡上的签名功能对随机数进行签名
a.取得智能卡上的签名服务引用GPKSignatureService(GPKSignatureService)sm.getCardServiee(SignatureCardService.class,false);
b.调用签名方法signData,其中参数分别是密钥文件(在步骤a中取得)、智能卡支持的签名方法、智能卡支持的填充方式和待签数据;
byte 口signature1=ArtificalCardVerifys.signData (KeyFile,GPKStandardNames.MD5_RSA.GPKStandardNames.PKCS_PADDING,data To Sign);
其中PKCS padding标准的定义是:必须在被加密的数据后添加额外的字节,使加密后的数据长度为加密块长度的整数倍。第二个参数指明了采用PKCS填充方式:签后的数据存放在signaturel里,即挑战得到的响应。
(4)验证用户数字证书是否正确
a.取得用户数字证书
本设计中CA签名的证书存放在3F00:0500:0503位置上,直接使用GPK智能卡的文件系统服务GPKFileAccessService中read方法:facs.read(file.getPath(),0,file.getLength())
读出证书文件后,强制转换为java.security.cert.Certificate,形成证书对象cardSignedCert(1ltiiE书由CA向用户颁发.可以从智能卡内获得,也可以从LDAP服务器中取得):
b.取得CA数字证书
本设计中CA证书存放在LDAP服务器的根节点上,通过JNDI操作取得证书对象iava.security.cert.Certificate对象cac;
c.从CA证书中取得公钥(get Public Key方法):
d.使用被验证证书对象的verify()方法,验证cardSignedCert是否由cac签发,如果验证失败则抛出异常返回;
(5)验证挑战,响应的结果是否正确
第3步工作得到响应数据signaturel.第4步工作验证了用户公钥和用户名是对应的。原始的挑战数据在dataToSign中;这里需要验证响应数据是否正确.工作步骤如下:
a.取得原始挑战数据dataToSign:
b.取得签名signaturel:
e.取得客户的公钥,取得方法是eardSignedCert.getPublicK.ey();
d.调用方法verifySignedData(),此方法带四个参数,第一个是智能卡上存公钥的位置,第二个是签名算法名,第三个是原始数据,第四个是签名数据,方法抛出openeard.core.service.Card.ServiceException异常和InvalidKeyException异常。调用的核心语句是:
vefifySignedData (KeyFile,MD5withRSA,dataToSign,signature1)
调用返回true表示挑战/响应过程成功。可以转向 commit 方法提交用户身份
四、智能卡认证结果的传递
验证模块APPlet 嵌入在ASP或者ASP.NET制作的登录网页中,该模块验证成功后应该把登录信息传递给业务系统首页。这里使用JSObjec类来解决 Applet 的信息向动态网页传递的问题。
图四
(1)Applet 向登录网页传参数
从APPlet 的角度来看,验证模块调用login方法成功后取得Subject(同时取得了用户名和登录成功的状态)。这里把用户名和登录状态先传给JSObject.JSObjeet的方法允许Applet与文档对象交互,并调用javascript函数。在Applet中加入下列代码:Drivate JSObject win;//说明一个JSObject对象win=JSObject.getWindow(this):/ 和当前脚本环境建立联系JSObject可以调用脚本环境中的函数,利用此特性可以把用户名和验证成功的状态传递给脚本函数fillin.调用时使用的代码如下:
args[0]=username;
agrs[1]=“success”;
win.call(“fillin”,args);
(2)网页接受Applet的参数并填人表单域
从脚本角度来看,需要定义一个函数fillin接受来自Applet的参数,该函数带有两个参数分别对应用户名和验证状态,函数的代码实现如下:
function fiUin(namel,status1)
{artiticatCardVerify.LoginName.value=name1;artificalCardVerify.status.value=statusl;)
其中artificalCardVerify是验证网页中的表单名.LoginName是用户名,在网页上不能修改;status是状态域,属性为隐藏,它用于业务网页判断来源是否合法:表单提交后转向业务网页:
<form method=“POST”action=“artificalCardVerify_login_redirect.asp”name=“artificalCardVerify”>
(3)业务网页视图
业务网页用ASP或者ASP.NET编写,接受来自登录网页的提交。业务网页首先检查是否登录成功(status域为Success、用户名不为空),从业务权限表取得该用户对应的业务权限后,进入正常业务逻缉。
五、结语:
在具有完备PKI 基础的应用环境中,实现基于JAAS 和智能卡的通用身份认证模块能够将业务系统中的认证模块分离出来。提高验证的安全级别,方便开发者使用和维护。作为一个可拨插模块,任何 Web 应用程序都可以将此模块植入。使用的代码极少,而安全级别很高。随着智能卡技术的不断发展,使用Java 智能卡将能进一步提高验证的安全性和灵活性。