基于JAAS 和智能卡的通用身份認(rèn)證模塊的實現(xiàn)
文章出處:http://56733.cn 作者:曾海 人氣: 發(fā)表時間:2011年09月28日
一、概述
傳統(tǒng)的軟件在實現(xiàn)身份認(rèn)證功能過程中,往往自行開發(fā)專用的認(rèn)證模塊。由于不同的軟件開發(fā)人員在信息安全方面水平參差不齊,實現(xiàn)的認(rèn)證模塊安全性往往得不到保證。認(rèn)證和授權(quán)應(yīng)當(dāng)同業(yè)務(wù)邏緝分離一個通用的身份認(rèn)證模塊不僅能減輕軟件開發(fā)人員的負(fù)擔(dān),更能保證認(rèn)證的強度,確保在當(dāng)認(rèn)證方式發(fā)生改變時,應(yīng)用程序不受影響。
二 JAAS認(rèn)證和授權(quán)服務(wù)
認(rèn)證和授權(quán)是兩種最基本的安全機制,認(rèn)證是簡單地對一個實體的身份進(jìn)行判斷。而授權(quán)則是向?qū)嶓w授予對數(shù)據(jù)資源和信息訪問權(quán)限的決策過程 Java 提供了一項提供認(rèn)證和授權(quán)功能的標(biāo)準(zhǔn)服務(wù)。該服務(wù)即 JAAS (Java Authentication AutIlorization Service)。JAAS 強調(diào)通過驗證誰在運行代碼以及用戶應(yīng)該有的權(quán)限來保護(hù)系統(tǒng)不受攻擊。使用 JAAS 可以把一些標(biāo)準(zhǔn)驗證服務(wù)。如LDAP(輕量目錄存取協(xié)議)和 Kerberos 等通過一種通用的,可配置的方式集成到系統(tǒng)中。
認(rèn)證機制需要參數(shù)是為了確定用戶的身份(對用戶進(jìn)行標(biāo)識)。授權(quán)在認(rèn)證之后進(jìn)行,JAAS框架對由配置文件指定的認(rèn)證模塊進(jìn)行了包裝。如果認(rèn)證成功。就會返回一個包含驗證信息的用戶標(biāo)識,認(rèn)證機制所返回的這個驗證信息會用于授權(quán)過程。JAAS 體系結(jié)構(gòu)如圖一所示。其中驗證模塊可以通過配置模塊替換。
圖一
從身份認(rèn)證的強度來看,基于PKI的智能卡身份驗證提供了更好的安全性。因此通用的登錄模塊不使用Java提供的通用模塊,而是實現(xiàn)了java.security.a(chǎn)uth.spi.LoginMoudle接口的自定義模塊,通過OCF框架存取智能卡,通過智能 實現(xiàn)驗證過程。
三、基于智能卡的身份認(rèn)證實現(xiàn)
智能卡是一種芯片卡,計算芯片鑲嵌在一張名片大小的塑料卡片上,從而完成數(shù)據(jù)的存儲與計算??梢酝ㄟ^讀卡器訪問智能卡中的數(shù)據(jù)。智能卡分為接觸式卡和非接觸式卡,接觸式卡中又分為存儲卡和微處理卡,后者更適合基于PKI的身份驗證。
使用智能卡進(jìn)行身份驗證的可用方式包括基于微軟平臺的CSP(Cryptographic Service Provider)、PKCS#11和基于Java的智能卡開發(fā)平臺OCF(Open card Framework)。OCF定義了從Java應(yīng)用環(huán)境到智能卡問的通信標(biāo)準(zhǔn)。
從通用的角度看,OCF框架優(yōu)點明顯,它是是一套基于Java的應(yīng)用程序編程接口,把不同的供應(yīng)商的與讀卡器交互的細(xì)節(jié)隱藏起來以簡化編程,主流智能卡廠商均支持OCF框架,Java是跨平臺的語言.因此為驗證模塊的運行提供了巨大的靈活性。身份認(rèn)證使用了OCF的 CardTerminal 和CardService 層服務(wù)。前者提供了讀卡器的接口API。后者使用File Access Card Service存取卡上文件,使用Signature Card Service提建立和驗證數(shù)字簽名.將證書導(dǎo)人智能卡,公鑰導(dǎo)出智能卡。這是身份認(rèn)證模塊的核心功能之一。
OCF框架下的身份認(rèn)證過程如圖二:
圖二
在卡和登錄模塊間的驗證方式使用了傳統(tǒng)的使用公鑰加密體制的挑戰(zhàn)/應(yīng)答模式??ㄉ嫌凶约荷傻腞SA密鑰對。登錄模塊給出一個隨機數(shù)。智能卡對它進(jìn)行簽名。而登錄模塊驗證后就可獲知用戶身份
在實現(xiàn)過程中用戶和公鑰關(guān)聯(lián)以及用戶公鑰分發(fā)均由PKI應(yīng)用系統(tǒng)中的CA完成。CA簽發(fā)數(shù)字證書進(jìn)行了用戶和公鑰關(guān)聯(lián)。證書可以放在用戶卡上。也可以存在LDAP服務(wù)器中。登錄模塊必須有用戶公鑰才能進(jìn)行驗證。登錄交互過程如圖三所示。
圖三
模塊具體實現(xiàn)的核心方法是:
private boolean verifySign () throws java.security.Invalid·KeyExeepfion
本方法的主要功能是:
(1)取得智能卡上公私鑰對的存儲位置。
執(zhí)行數(shù)字簽名的時候需要使用私鑰。雖然智能卡上的私鑰不可導(dǎo)出,但可以取得一個指向私鑰的引用。通過引用進(jìn)行簽名操作。某具體智能卡的密鑰對存儲在3F00:0600:0606,首先得到了密鑰對位置:
CardFilePath EF_key=new CardFilePath(“:3F00:0600:0606”);
CardFilePath DF=new CardFilePatll (“:3F00:0600”);
智能卡中密鑰對只能通過智能卡操作系統(tǒng)引用,而不能使用普通的密鑰對引用方式,只能使用GPKSignatureKeyFile取到卡上密鑰對引用:
GPKSignatureKeyFile KeyFfle = new GPKSignatureKeyFile
(ef_key,512,GPKRSAKeyFile.UNCERTIFIED_KEY);
(2)生成用于挑戰(zhàn)的隨機數(shù)。隨機數(shù)生成利用了ScureRandom類,本類是一個高強度偽隨機數(shù)生成器,即PRNG(pseudo-random number generator),它保證每次輸出的值沒有任何規(guī)律, 即符合RFC 1750 (Randomness Recommendations for Security)。使用時比較簡單:
SecureRandom random=new SecureRandom();
Random.nextBytes(dataToSign);
(3)利用卡上的簽名功能對隨機數(shù)進(jìn)行簽名
a.取得智能卡上的簽名服務(wù)引用GPKSignatureService(GPKSignatureService)sm.getCardServiee(SignatureCardService.class,false);
b.調(diào)用簽名方法signData,其中參數(shù)分別是密鑰文件(在步驟a中取得)、智能卡支持的簽名方法、智能卡支持的填充方式和待簽數(shù)據(jù);
byte 口signature1=ArtificalCardVerifys.signData (KeyFile,GPKStandardNames.MD5_RSA.GPKStandardNames.PKCS_PADDING,data To Sign);
其中PKCS padding標(biāo)準(zhǔn)的定義是:必須在被加密的數(shù)據(jù)后添加額外的字節(jié),使加密后的數(shù)據(jù)長度為加密塊長度的整數(shù)倍。第二個參數(shù)指明了采用PKCS填充方式:簽后的數(shù)據(jù)存放在signaturel里,即挑戰(zhàn)得到的響應(yīng)。
(4)驗證用戶數(shù)字證書是否正確
a.取得用戶數(shù)字證書
本設(shè)計中CA簽名的證書存放在3F00:0500:0503位置上,直接使用GPK智能卡的文件系統(tǒng)服務(wù)GPKFileAccessService中read方法:facs.read(file.getPath(),0,file.getLength())
讀出證書文件后,強制轉(zhuǎn)換為java.security.cert.Certificate,形成證書對象cardSignedCert(1ltiiE書由CA向用戶頒發(fā).可以從智能卡內(nèi)獲得,也可以從LDAP服務(wù)器中取得):
b.取得CA數(shù)字證書
本設(shè)計中CA證書存放在LDAP服務(wù)器的根節(jié)點上,通過JNDI操作取得證書對象iava.security.cert.Certificate對象cac;
c.從CA證書中取得公鑰(get Public Key方法):
d.使用被驗證證書對象的verify()方法,驗證cardSignedCert是否由cac簽發(fā),如果驗證失敗則拋出異常返回;
(5)驗證挑戰(zhàn),響應(yīng)的結(jié)果是否正確
第3步工作得到響應(yīng)數(shù)據(jù)signaturel.第4步工作驗證了用戶公鑰和用戶名是對應(yīng)的。原始的挑戰(zhàn)數(shù)據(jù)在dataToSign中;這里需要驗證響應(yīng)數(shù)據(jù)是否正確.工作步驟如下:
a.取得原始挑戰(zhàn)數(shù)據(jù)dataToSign:
b.取得簽名signaturel:
e.取得客戶的公鑰,取得方法是eardSignedCert.getPublicK.ey();
d.調(diào)用方法verifySignedData(),此方法帶四個參數(shù),第一個是智能卡上存公鑰的位置,第二個是簽名算法名,第三個是原始數(shù)據(jù),第四個是簽名數(shù)據(jù),方法拋出openeard.core.service.Card.ServiceException異常和InvalidKeyException異常。調(diào)用的核心語句是:
vefifySignedData (KeyFile,MD5withRSA,dataToSign,signature1)
調(diào)用返回true表示挑戰(zhàn)/響應(yīng)過程成功??梢赞D(zhuǎn)向 commit 方法提交用戶身份
四、智能卡認(rèn)證結(jié)果的傳遞
驗證模塊APPlet 嵌入在ASP或者ASP.NET制作的登錄網(wǎng)頁中,該模塊驗證成功后應(yīng)該把登錄信息傳遞給業(yè)務(wù)系統(tǒng)首頁。這里使用JSObjec類來解決 Applet 的信息向動態(tài)網(wǎng)頁傳遞的問題。
圖四
(1)Applet 向登錄網(wǎng)頁傳參數(shù)
從APPlet 的角度來看,驗證模塊調(diào)用login方法成功后取得Subject(同時取得了用戶名和登錄成功的狀態(tài))。這里把用戶名和登錄狀態(tài)先傳給JSObject.JSObjeet的方法允許Applet與文檔對象交互,并調(diào)用javascript函數(shù)。在Applet中加入下列代碼:Drivate JSObject win;//說明一個JSObject對象win=JSObject.getWindow(this):/ 和當(dāng)前腳本環(huán)境建立聯(lián)系JSObject可以調(diào)用腳本環(huán)境中的函數(shù),利用此特性可以把用戶名和驗證成功的狀態(tài)傳遞給腳本函數(shù)fillin.調(diào)用時使用的代碼如下:
args[0]=username;
agrs[1]=“success”;
win.call(“fillin”,args);
(2)網(wǎng)頁接受Applet的參數(shù)并填人表單域
從腳本角度來看,需要定義一個函數(shù)fillin接受來自Applet的參數(shù),該函數(shù)帶有兩個參數(shù)分別對應(yīng)用戶名和驗證狀態(tài),函數(shù)的代碼實現(xiàn)如下:
function fiUin(namel,status1)
{artiticatCardVerify.LoginName.value=name1;artificalCardVerify.status.value=statusl;)
其中artificalCardVerify是驗證網(wǎng)頁中的表單名.LoginName是用戶名,在網(wǎng)頁上不能修改;status是狀態(tài)域,屬性為隱藏,它用于業(yè)務(wù)網(wǎng)頁判斷來源是否合法:表單提交后轉(zhuǎn)向業(yè)務(wù)網(wǎng)頁:
<form method=“POST”action=“artificalCardVerify_login_redirect.a(chǎn)sp”name=“artificalCardVerify”>
(3)業(yè)務(wù)網(wǎng)頁視圖
業(yè)務(wù)網(wǎng)頁用ASP或者ASP.NET編寫,接受來自登錄網(wǎng)頁的提交。業(yè)務(wù)網(wǎng)頁首先檢查是否登錄成功(status域為Success、用戶名不為空),從業(yè)務(wù)權(quán)限表取得該用戶對應(yīng)的業(yè)務(wù)權(quán)限后,進(jìn)入正常業(yè)務(wù)邏緝。
五、結(jié)語:
在具有完備PKI 基礎(chǔ)的應(yīng)用環(huán)境中,實現(xiàn)基于JAAS 和智能卡的通用身份認(rèn)證模塊能夠?qū)I(yè)務(wù)系統(tǒng)中的認(rèn)證模塊分離出來。提高驗證的安全級別,方便開發(fā)者使用和維護(hù)。作為一個可撥插模塊,任何 Web 應(yīng)用程序都可以將此模塊植入。使用的代碼極少,而安全級別很高。隨著智能卡技術(shù)的不斷發(fā)展,使用Java 智能卡將能進(jìn)一步提高驗證的安全性和靈活性。