摘要:該文構建了一個基于PKI的電子商務交易系統,分析了其交易過程,同時針對網上交易中數據安全和防欺詐行為的發生,提出了基于Java2體系的信息安全思想,給出了一個基于Java的具體實現。
關鍵詞:電子商務;交易系統;信息安全;PKI;Java
中圖分類號:TP311.52文獻標識碼:A文章編號:1009-3044(2008)35-2323-04
A Business System of Electronic Commence Based on PKI and the Realization of Information Security
LI Bin1,CHEN Bo2
(1.The People's Bank of China Yinchuan Sub-branch, Yinchuan 750001, China; 2.Nanjing Urban Planning Research Center, Nanjing 210029, China)
Abstract: This paper established an Electronic Commence system based on PKI and analysed the process of the business. In order to prevent the business via Internet from being cheated and protect the data's security, the paper put forward an idea about information security based on Cryptography Architecture of Java and illustrated the implementation based on Java.
Key words: electronic commence; business system; information security; PKI; Java
1 引言
電子商務活動主要涉及信息的交流、電子數據的交換和電子轉帳。其中,電子交易中的安全問題已經成為發展可信賴電子商務環境的瓶頸。要保障電子交易活動中的安全,必須滿足以下四個條件:1) 信息的有效性。交易的一方能夠對另一方進行有效地身份認證,防止第三方假冒。2) 信息的完整性。防止傳輸過程中信息被任何非授權篡改。3) 信息的機密性。信息只能為交易雙方所閱讀,對第三方保密。4)交易雙方的不可否認性。使交易雙方事后無法否認其參與了此次交易活動。對此,目前最有效的解決方案就是建立在公匙基礎設施上的PKI技術。本文提出了一個基于PKI的電子商務交易系統,分析了其交易實現過程。同時對該系統中商務實踐的安全問題給出了可行方案。模擬運行表明,切實可行。
2 PKI與PKI協議
PKI是以公鑰加密為基礎,創建、管理、存儲分發和撤銷證書所需的一組硬件、軟件、人、策略和過程。其主要涉及兩個方面:1) 公鑰密碼技術。該技術使用兩個不同的密鑰(公開密鑰與私有密鑰)分別用于對數據的加密與解密。發送信息時用對方的公開密鑰加密,收信者用自己的私用密鑰進行解密。公開密鑰加密算法的核心是運用單向陷門函數,即從一個方向求值是容易的,但其逆向計算卻很困難,從而在實際上成為不可行的。所有網上安全服務都是建立在公私鑰密碼基礎之上。2) 可信賴的數字身份。為實現網上交易雙方對可信賴身份的需求,頒發給每一個公私鑰持有者以證明其身份的數字證書。為此,PKI必須建立一個權威的認證機構CA,在公鑰加密技術基礎之上完成證書的生成、管理、存儲分發和撤銷工作。
SSL是最著名最廣泛使用的基于PKI的協議,它主要采用公開密鑰體制和X.509數字證書技術在客戶和服務器之間建立安全的網絡,實現兩個應用間通信的保密性、完整性和可靠性。SSL有兩個子協議:
1) 記錄協議:提供分塊、壓縮、加密和完整性服務。
2) 握手協議:執行客戶和服務器之間會話的創建、協商或重新協商參數。
握手協議較記錄協議更高層,必須先執行握手協議后才能實現記錄協議中的加密和完整性校驗等服務。握手協議的工作過程如下(圖1):
① 客戶通過向服務器發出客戶Hello消息來提出SSL連接請求。
② 服務器用服務器Hello消息響應。
③ 服務器把自己的證書發送給客戶,客戶驗證服務器證書的有效性。
④ 服務器請求客戶證書(可選的),發送Hello Done消息給客戶,至此服務器初始協商過程結束。
⑤ 客戶發送證書,服務器驗證證書有效性。客戶生成一個次主密鑰,用服務器公鑰加密,并把密文放在客戶密鑰交換消息中發送給服務器。
⑥ 服務器用私鑰解密次主密鑰,雙方通過對次主密鑰、客戶Hello隨機數、服務器Hello隨機數計算一系列散列值把次主密鑰轉化為主密鑰。主密鑰用來生成記錄協議中所需的加密算法和MAC算法密鑰。
⑦ 雙方改變密鑰規范協議完成整個握手協議。
3 基于PKI的電子商務交易系統
3.1 系統模型及實現條件
圖2為本文構造的電子商務交易系統,主要參與者有:企業客戶、電子商務站點、商家、銀行與支付網關以及可信的第三方。
系統采用證書的形式實現對客戶和服務器身份驗證,通過SSL建立瀏覽器和服務器間的安全傳輸通道,利用公鑰技術和數字簽名保證數據的完整性,結合時間戳服務實現訂單的不可否認性。最后,采用簽名加密的S/MIME電子郵件形式分別發送訂單通告和確認信息給生產廠家和消費客戶。
3.2 系統實現過程(不包含在線支付部分)
假設在交易之前,企業客戶已經申請獲得了電子商務站點所頒發的客戶消費證書。
1) 客戶瀏覽電子商務站點,初始化請求,客戶端認證
客戶通過HTTP連接到電子商務站點,為了保證瀏覽器和Web服務器之間的通信安全,Web服務器通過SSL連接建立過程。在此過程中,客戶要求服務器出示服務器證書(此證書只是用于建立瀏覽器的SSL連接)。同時,站點服務器向客戶端發送認證請求,客戶從瀏覽器的證書列表中選擇零件倉庫客戶證書,發送給站點服務器。
服務器檢驗客戶證書的有效性,包括驗證用戶證書,檢查證書是否過期或被撤消,并且確認證書的簽名者是否在站點的可信簽名列表中。
2) 驗證客戶身份,確定客戶購買權限,返回應答請求,用戶進入購買狀態。
為確定用戶是否被授權消費,電子商務應用程序從客戶證書中獲取用戶身份,檢查證書中命名的用戶ID (Customer-ID)是否有權訪問電子定單應用程序。然后查詢站點的用戶組成員中央LDAP(輕量級目錄訪問協議)庫找到用戶消費權限。
服務器將用戶消費權限信息(CR)通過Hash函數得到數字摘要Hs(CR),然后用服務器私鑰加密摘要形成數字簽名Signature(Hs(CR)),最后用客戶端公匙加密客戶消費權限信息P(CR),將它們一起發送給客戶。
客戶收到信息對其進行驗證,首先用客戶的公鑰解密信息,得到信息原文,然后用Hash函數獲得原文摘要Hc(CR),比較Hc(CR)與Hs(CR)是否匹配,如果相等,客戶進入購買狀態。如圖3所示。
3) 完成購物,發送定單。
客戶選擇購買的商品后,通過電子定單應用程序完成訂單。發送訂單這一過程對安全性要求很高。客戶首先對訂單作用一個Hash函數,形成訂單摘要Hc(OI),保證其傳輸過程中的完整性。然后通過客戶的私鑰形成數字簽名SIGNc(Hc(OI))。客戶還要用對稱密鑰(DES)加密訂單Key(OI),同時用服務器公鑰加密對稱密鑰Ps(Key),將二者合在一起形成數字信封Envilope(Ps(Key),Key(OI))。客戶將數字簽名、數字信封,一起發送給電子商務站點。
4) 服務器收到訂單,請求獲取安全時間戳,記錄交易信息
服務器收到訂單后,通過客戶公鑰獲得訂單摘要,站點服務器用外部時間戳加密摘要,發送給時間戳服務器以獲取一個安全時間戳。安全時間戳是一個對定單數據、當前日期和時間的摘要H(OI,Date,Time),通過它確定交易的具體時間,實現交易的不可否認性。時間戳服務器用其私鑰對摘要簽名,連同證書發送給站點服務器。
站點服務器驗證時間戳服務器證書,獲得時間戳。在日志文件中對訂單及安全時間戳進行記錄。接著驗證交易簽名,即用Hash函數獲取訂單摘要Hs(OI),驗證Hs(OI)與Hc(OI)是否匹配。如果匹配,則在訂單數據庫中記錄交易。至此客戶完成了與電子商務網站之間的交易活動。如圖4所示。
5) 向商家發送訂單通知,確認交易成功信息
電子商務站點生成新的訂單通告,以簽名并加密的S/MIME電子郵件形式發送訂單通告給生產者。接著,站點生成一個帶有交易確認信息的經簽名并加密的e-Mail,用S/MIME協議把它發送給企業客戶,整個網上交易活動結束。
4 系統信息安全的實現
上述電子商務系統基本上是通過瀏覽器和e-mail實現的。Netscape和Internet Explorer 兩種瀏覽器均支持公鑰技術。它們不僅支持密鑰和證書的存儲,還支持密鑰對的生成和證書請求的創建。本文主要討論基于Java實現本系統的交易信息安全。Java 平臺的基本語言和庫擴展都提供了用于編寫安全應用程序的極佳基礎。在JDK 1.4 之前,許多安全性功能必須作為擴展添加到基本Java 代碼分發版中。現在,新的寬松法規使安全性特性和基本語言更緊密的集成成為可能。下列軟件包(在1.4 發行版之前作為擴展使用)現在集成到了JDK 1.4 中:
JCE(Java 密碼術擴展)
JSSE(Java 安全套接字擴展)
JAAS(Java 認證和授權服務)
JDK 1.4 還引入了兩種新功能:
JGSS(Java 通用安全性服務(Java General Security Service))
CertPath API(Java 證書路徑API (Java Certification Path API))
Java安全API數字簽名的方法集中在java.security軟件包中,在程序開始部分要引入該軟件包(import java.security)。以下介紹密鑰生成、數據簽名及驗證的步驟。
4.1 對于一個用戶來講首先要生成他的密鑰對,并且分別保存
生成一個KeyPairGenerator實例
java.security.KeyPairGeneratorkeygen=java.security.KeyPairGenerator.getInstance(\"DSA\");
如果設定隨機產生器就用如相代碼初始化
SecureRandom secrand=new SecureRandom();
secrand.setSeed(\"tttt\".getBytes()); //初始化隨機產生器
keygen.initialize(512,secrand); //初始化密鑰生成器
否則
keygen.initialize(512);
生成密鑰公鑰pubkey和私鑰prikey
KeyPair keys=keygen.generateKeyPair(); //生成密鑰組
PublicKey pubkey=keys.getPublic();
PrivateKey prikey=keys.getPrivate();
//分別保存在myprikey.dat和mypubkey.dat中,以便下次不再生成
生成密鑰對的時間比較長
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(\"myprikey.dat\"));
out.writeObject(prikey);
out.close();
out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(\"mypubkey.dat\"));
out.writeObject(pubkey);
out.close();
4.2 用他私人密鑰(prikey)對他所確認的信息(info)進行數字簽名產生一個簽名數組
從文件中讀入私人密鑰(prikey)
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(\"myprikey.dat\"));
PrivateKey myprikey=(PrivateKey)in.readObject();
in.close();
初始一個Signature對象,并用私鑰對信息簽名
java.security.Signature signet=java.security.Signature.getInstance(\"DSA\");
signet.initSign(myprikey);
signet.update(myinfo.getBytes());
byte[] signed=signet.sign();
把信息和簽名保存在一個文件中(myinfo.dat)
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream(\"myinfo.dat\"));
out.writeObject(myinfo);
out.writeObject(signed);
out.close();
把他的公鑰的信息及簽名發給其它用戶
4.3 其他用戶用他的公共密鑰(pubkey)和簽名(signed)和信息(info)進行驗證是否由他簽名的信息
讀入公鑰
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream(\"mypubkey.dat\"));
PublicKey pubkey=(PublicKey)in.readObject();
in.close();
讀入簽名和信息
in=new java.io.ObjectInputStream(new java.io.FileInputStream(\"myinfo.dat\"));
String info=(String)in.readObject();
byte[] signed=(byte[])in.readObject();
in.close();
初始一個Signature對象,并用公鑰和簽名進行驗證
java.security.Signature signetcheck=java.security.Signature.getInstance(\"DSA\");
signetcheck.initVerify(pubkey);
signetcheck.update(info.getBytes());
if (signetcheck.verify(signed)) { System.out.println(\"簽名正常\");}
對于密鑰的保存本文是用對象流的方式保存和傳送的,也可可以用編碼的方式保存.注意
import java.security.spec.*
import java.security.*
具體說明如下
public key是用X.509編碼的,例碼如下:
byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成編碼
//傳送二進制編碼
//以下代碼轉換編碼為相應key對象
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance(\"DSA\");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
對于Private key是用PKCS#8編碼,例碼如下:
byte[] bPKCS=myprikey.getEncoded();
//傳送二進制編碼
//以下代碼轉換編碼為相應key對象
PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);
KeyFactory keyf=KeyFactory.getInstance(\"DSA\");
PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);
4.4 常用API
java.security.KeyPairGenerator 密鑰生成器類
public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException
以指定的算法返回一個KeyPairGenerator 對象
參數: algorithm 算法名.如:\"DSA\",\"RSA\"
public void initialize(int keysize)
以指定的長度初始化KeyPairGenerator對象,如果沒有初始化系統以1024長度默認設置
參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64 的倍數
public void initialize(int keysize, SecureRandom random)
以指定的長度初始化和隨機發生器初始化KeyPairGenerator對象
參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64 的倍數
random 一個隨機位的來源(對于initialize(int keysize)使用了默認隨機器
public abstract KeyPair generateKeyPair()
產生新密鑰對
java.security.KeyPair 密鑰對類
public PrivateKey getPrivate()
返回私鑰
public PublicKey getPublic()
返回公鑰
java.security.Signature 簽名類
public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException
返回一個指定算法的Signature對象
參數 algorithm 如:\"DSA\"
public final void initSign(PrivateKey privateKey)
throws InvalidKeyException
用指定的私鑰初始化
參數:privateKey 所進行簽名時用的私鑰
public final void update(byte data)
throws SignatureException
public final void update(byte[] data)
throws SignatureException
public final void update(byte[] data, int off, int len)
throws SignatureException
添加要簽名的信息
public final byte[] sign()
throws SignatureException
返回簽名的數組,前提是initSign和update
public final void initVerify(PublicKey publicKey)
throws InvalidKeyException
用指定的公鑰初始化
參數:publicKey 驗證時用的公鑰
public final boolean verify(byte[] signature)
throws SignatureException
驗證簽名是否有效,前提是已經initVerify初始化
參數: signature 簽名數組
5 總結
PKI不僅向廣大用戶提供了在所需范圍內實現密鑰傳遞的方法,而且還提供了對公共密鑰進行加密操作的簡易應用程序,使電子郵件、電子商務和網絡系統獲得了安全保障。Java安全API提供了加密、信息融合、密鑰管理、認證、存取控制和數字簽名等功能,允許開發者進行低層和高層的安全應用。
參考文獻:
[1] 張勇,馮玉才.XML數字簽名技術及其在Java中的實現[J].計算機應用.2003,9.
[2] 王尚平,王育民,張亞玲.基于DSA及RSA的證實數字簽名方案[J].軟件學報,2003,9.
[3] Andrew Nash.公鑰技術設施實現和管理電子安全[M].北京:清華大學出版社,2002,12.
[4] Bruce Schneier.應用密碼學[M].北京:機械工業出版社,1999,12.
[5] 黃煒曼,王文東,劉向武.PKI策略及其在PKI系統中的實現[J].計算機工程與應用,2003,32.
[6] 白娟,周林.數字簽名技術及在Java中的一種實現[J].微計算機信息,2004,20.
[7] Jess Garms,著.龐南,譯.Java安全性編程指南[M].北京:電子工業出版社,2003,2.