by fleshwound ()
公司主營業(yè)務:成都做網(wǎng)站、成都網(wǎng)站建設、移動網(wǎng)站開發(fā)等業(yè)務。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)公司推出博白免費做網(wǎng)站回饋大家。
(注:這是我們的完整設計中的一部分,其它有些部分尚要求保密,希望這個拙文能給做J2EE項目的兄弟們帶來點幫助,有任何關于JAVA安全和密碼學理論和應用的問題可以來我們的論壇:)
近年來,隨著互連網(wǎng)和計算機的普及,電子商務和電子政務成為當今社會生活的重要組成部分,以網(wǎng)上訂購和網(wǎng)上在線支付的為主要功能的網(wǎng)店系統(tǒng)(Web Shop System)是目前電子商務的熱門技術。
JAVA以它“一次編譯,處處運行”的神奇魅力和強大的安全技術支持,很快成為WEB信息系統(tǒng)開發(fā)的首選語言,而J2EE就是為了WEB應用開發(fā)而誕生的。目前J2EE的應用大部份都是多層結(jié)構(gòu)的, 良好的分層可以帶來很多好處,例如可以使得代碼結(jié)構(gòu)清晰,方便組件復用,可以快速適應應用的新需求。同時,JAVA還提供了強大的安全技術(例如:JCA,HTTPS,JSSA等)。對于電子商務系統(tǒng)而言,系統(tǒng)平臺的安全性和效率是其中的核心問題,而這些正好是J2EE及其相關技術的強項。
0 系統(tǒng)中所要使用的API及其特點介紹
該系統(tǒng)中主要使用的技術和特點如下:
(1)EJB :主要是作為J2EE中間層,完成商業(yè)邏輯。目前主要有三種類型的EJB: 會話 Bean (Session Bean)、實體Bean (Entity Bean)、消息驅(qū)動的Bean(MDB);
(2)JAAS:在J2EE 中用于處理認證和授權服務,進行資源控制;
(3)JSP和Java Servlets:用于J2EE的表示層,生成用戶界面;
(4)JDBC:用于數(shù)據(jù)庫(資源層)的連接和與數(shù)據(jù)庫進行交互;
(5)JNDI:Java命名和目錄接口,該API實際上是用來訪問J2EE的所有資源;
(6)JMS:Java消息傳輸服務,配合MDB使用。
1 Session的安全問題與解決方案
在項目中,保存Session一般有兩種方法,一是分別放在客戶端,一是集中放在服務器端。在客戶端保存Session是指將Session的狀態(tài)串行化,然后嵌入到返回給客戶的HTML頁面中。當Session中的信息很少時,這樣實現(xiàn)比較容易,另外這種方法還消除了跨越多個服務器復制狀態(tài)的問題。
但是在客戶端保存Session狀態(tài)時,必須考慮到由此帶來的安全問題,因為黑客可能通過嗅探攻擊(Sniffer)獲取敏感信息。為了不讓敏感信息數(shù)據(jù)暴露,解決的方法是對數(shù)據(jù)進行加密或者使用HTTPS,采用SSL技術。
如果是要保存大量Session狀態(tài)的應用,最好的方法是將Session狀態(tài)統(tǒng)一放在服務器端。當狀態(tài)被保存在服務器上時,不會有客戶端Session管理的大小和類型限制。此外,還避免了由此帶來的安全問題,而且也不會遇到由于在每個請求間傳送Session狀態(tài)帶來的性能影響,但是對服務器的性能要求比較高。網(wǎng)店系統(tǒng)的安全性要求較高,因此Session還是集中放在中間層服務器端,同時對客戶端到服務器端采用SSL連接。
2客戶端的緩存安全設計
大部分顧客使用的WEB瀏覽器將瀏覽過的頁面緩存在磁盤上,這樣我們?yōu)g覽網(wǎng)頁的時候不需要重新向服務器發(fā)出HTTP請求,對于普通的網(wǎng)頁不存在安全問題。但是對于需要保密的WEB應用,會帶來安全隱患和泄漏隱私,因此對于客戶端緩存,也必須做適當?shù)奶幚?。最好的方法就是禁止使用緩存,但是對于大部分顧客而言,要求在客戶端不用緩存是不現(xiàn)實的,因此我們必須在中間層解決該問題,方法是采用Servlet過濾器技術。該技術是Servlet2.3以后才出現(xiàn)的,在J2EE中的應用很廣泛。要使用該技術,需要執(zhí)行以下步驟:
(1) 編寫一個Servlet過濾器,實現(xiàn)javax.servlet.Filter接口;
(2) 修改Web.xml文件,使容器知道過濾器在什么時候被調(diào)用。
Javax.servlet.Filter主要有3個方法:
(1)init(FilterConfig cfg) :當開始使用 servlet 過濾器服務時,容器調(diào)用此方法一次。傳送給此方法的 FilterConfig 參數(shù)包含 servlet 過濾器的初始化參數(shù);
(2)destroy() :當不再使用 servlet 過濾器服務時,容器調(diào)用此方法;
(3)doFilter(ServletRequest req, ServletResponse res, FilterChain chain): 容器為每個映射至此過濾器的 servlet 請求調(diào)用此方法,然后才調(diào)用該 servlet 本身。傳送至此方法的 FilterChain 參數(shù)可用來調(diào)用過濾器鏈中的下一個過濾器。當鏈中的最后一個過濾器調(diào)用 chain.doFilter() 方法時,將運行最初請求的 servlet。因此,所有過濾器都應該調(diào)用 chain.doFilter() 方法。如果過濾器代碼中的附加認證檢查導致故障,則不需要將原始 servlet 實例化。在這種情況下,不需要調(diào)用 chain.doFilter() 方法,相反,可將其重定向至其它一些錯誤頁面。
如果 servlet 映射至許多 servlet 過濾器,則按照應用程序的部署描述符(web.xml)中的先后出現(xiàn)的次序來調(diào)用 servlet 過濾器。這一部分的主要代碼如下:
//要引入的類庫
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.*;
//設置servlet過濾代碼段
public class CacheFilter implements Filter {
protected FilterConfig filterConfig;
private String cachetp;
//初始化
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
cachetp=config.getInitParameter("CacheControlType");
if (cachetp==null)
{
throw new ServletException("沒有定義Cache控制類型");
}
}
//
public void destroy()
{
this.filterConfig = null;
}
//執(zhí)行過濾器部分
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException, ServletException {
if (response instanceof HttpServletResponse )
{
HttpServletResponse resp=(HttpServletResponse) response;
resp.addHeader("Cache-Control",cachetp);
}
else
{
throw new ServletException("非法相應!");
}
chain.doFilter(request, response);
}
}
以下是在Web.xml中添加的對應的內(nèi)容
CacheFilter
CacheFilter
Cache filter
CacheControlType
no-store
CacheFilter
/cachecontrol
3視圖訪問的安全設置
所有用戶都必須登陸,只有登陸才可以看到用戶的角色和權限相對應的視圖。因此一個重要的問題就是如何防止一個視圖或者部分的視圖被一個未被授權的客戶直接訪問。
在一些情況下,資源被限制為完全不允許某些用戶訪問,例如:管理后臺就不應該讓普通顧客會員訪問。有幾個方法可以做到這一點。一個方法是加入應用邏輯到處理控制器或者視圖的程序中,禁止某些用戶訪問。另一個方案是設置運行時的系統(tǒng),對于一些資源,僅允許經(jīng)由另一個應用資源內(nèi)部調(diào)用。在這種情形,對于這些資源的訪問必須被通過另一個表現(xiàn)層的應用資源進行,例如一個servlet控制器。對于這些受限制的資源不允許通過瀏覽器直接調(diào)用。
在J2EE中,可以利用Web容器中內(nèi)置的安全技術來進行角色訪問資源的控制。根據(jù)最新版本的servlet和EJB規(guī)范,安全限制在web.xml的配置描述文件中描述,我們可以通過配置web.xml來控制角色訪問,修改配置描述文件web.xml就可以達到快速修改安全策略的目的。
安全限制允許使用編程的方法根據(jù)用戶的角色來控制訪問。資源可以被某些角色的用戶訪問,同時禁止其它的角色訪問。另外,某個視圖的一部分也可以根據(jù)用戶的角色來限制其訪問。如果某些資源完全不允許來自于瀏覽器的直接訪問,那么這些資源可以配置只允許一些特殊的安全角色訪問,而這些安全角色不分配給任何一個用戶。這樣只要不分配這個安全角色,那么以這種方式配置的資源將禁止所有的瀏覽器直接訪問。下面一個例子就是web.xml配置文件的一部分,它定義了一個安全的角色以限制直接的瀏覽器訪問。角色的名字是“vip”,受限制資源的名字是specialgood1.jsp、specialgood2.jsp、specialgood3.jsp和bookinfo.jsp。除非一個用戶或者組被分配到“vip”角色,否則這些客戶都不可以直接訪問這些JSP頁面。不過,由于內(nèi)部的請求并不受這些安全的限制,一個初始時由某servlet控制器處理的請求將會導向到這些受限制的頁面,這樣它們就可以間接訪問這些JSP頁面。
<security-constraint>
<web-resource-collection>
<web-resource-name>specialgood </web-resource-name>
<description>special good infomation</description>
<url-pattern>/shop/jsp/a1/specialgood1.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/specialgood2.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/specialgood3.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/bookinfo.jsp</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>vip</role-name>
</auth-constraint>
</security-constraint>
3 各層次間的耦合問題與解決策略
表現(xiàn)層的數(shù)據(jù)結(jié)構(gòu),例如HttpServletRequest,應該被限制在表現(xiàn)層上。如果將這些細節(jié)放到其它層(主要是業(yè)務邏輯層)中,將大大降低了代碼的的重用性,令代碼變得復雜,并且增加了層間的耦合。解決方法一個常用方法是不讓表現(xiàn)層的數(shù)據(jù)結(jié)構(gòu)和商業(yè)層共享,而是拷貝相關的狀態(tài)到一個更常見的數(shù)據(jù)結(jié)構(gòu)中再共享。你也可以選擇由表現(xiàn)層數(shù)據(jù)結(jié)構(gòu)中將相關的狀態(tài)分離出來,作為獨立的參數(shù)共享。另外在域?qū)ο蟊┞侗憩F(xiàn)層的數(shù)據(jù)結(jié)構(gòu),如果將諸如HttpServletRequest的請求處理數(shù)據(jù)結(jié)構(gòu)和域?qū)ο蠊蚕恚@樣做也會增加了應用中兩個不同方面的耦合。域?qū)ο髴撌强芍赜玫慕M件,如果它們的實現(xiàn)依賴協(xié)議或者層相關的細節(jié),它們可重用性就很差,同時維護和調(diào)試高耦合的應用更加困難。成熟的解決方案是不通過傳送一個HttpServletRequest對象作為一個參數(shù),而是拷貝request對象的狀態(tài)到一個更為常用的數(shù)據(jù)結(jié)構(gòu)中,并且將這個對象共享給域?qū)ο蟆D阋部梢赃x擇由HttpServletRequest對象中將相關的狀態(tài)分離出來,并且將每一個的狀態(tài)作為一個獨立的參數(shù)提供給域?qū)ο蟆?/p>
4 EJB的安全設計與控制
EJB的執(zhí)行過程一般是這樣的:(1)客戶端通過JNDI檢索Home對象的引用;(2)JNDI返回Home對象的引用;(3)請求創(chuàng)建一個新的EJB對象;(4)創(chuàng)建EJB對象;(5)返回EJB對象;(6)調(diào)用商務方法;(7)調(diào)用Enterprise Bean.引起EJB的安全問題原因主要存在三個方面:
(1)用包嗅探器(Packet Sniffer)獲取用戶憑證信息并直接調(diào)用會話Bean;(2)對實體Bean進行未授權訪問;(3)對消息驅(qū)動的Bean的無效訪問(發(fā)布惡意或者虛假的消息).
以上安全問題可導致客戶端或者服務端欺騙攻擊和DDOS攻擊。解決問題(1)的方法是使用JAVA中SSL技術來保護通訊,解決(2)的方法是對于實體Bean全部采用本地接口或者采用JAAS(文獻[1]),對于(1)和(2),我們可以同時采取以下措施:讓容器完成認證并傳輸用戶憑證信息,另外使用聲明性或者程序設計的安全驗證角色。對于問題(3),J2EE并沒有提供一個很好的方案,我們的解決方案是采用數(shù)字簽名技術來保證信息來自可信任的源。該方法的結(jié)合代碼簡要說明如下,消息采用JMS傳遞:
//客戶端,要用到消息發(fā)送者的私鑰進行簽名
...
message.setString("userid",userid);
message.setString("useritem",useritem);
message.setInt("usersn",serialnum);//包含一個序列號
message.setString("usercertid",certid);
String signature=getSignature(userid+":"+useritem+":"+serialnum+":"+certid);
//進行簽名,其中getSignature為簽名函數(shù),要用到消息發(fā)送者的私鑰進行簽名,具體密碼學技術可參考文獻[2];
message.setString("signature",signature);
sendmessage(message);//發(fā)送信息
...
//服務器端
String checkstr=userid+":"+message.getString("useritem")+":"+
message.getInt("usersn")+":"+usercertid;
boolean b_check=checkSignature(checkstr,msg.getString("signature"),
usercertid,userid);
//進行驗證,其中checkSignature為驗證函數(shù),要用到消息發(fā)送者的公鑰進行驗證,具體密碼學技術可參考文獻[2];
5 CA中心與證書的生成
前面我們已經(jīng)提出在客戶端要使用HTTPS和SSL,因此要建立一個自己的CA中心來管理分發(fā)證書,加強客戶端到中間層服務器端通訊的安全性.建立CA中心的第一步是利用JAVA工具包中的Keytool生成一個X509證書,然后將該證書交由權威CA中心Vertsign簽名,再將該證書設置為根證書,建立自己的CA.每次有新用戶注冊交易的時候,都必須簽發(fā)一個用戶獨一無二的證書,關鍵的過程是如何簽發(fā)證書.簽發(fā)證書的過程如下:
(1)從中間層CA服務器的密鑰庫中讀取CA的證書:
FileInputStream in=new FileInputStream(ShopCAstorename);
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass);
java.security.cert.Certificate c1=ks.getCertificate(alias);
(2)獲得CA的私鑰:
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass);
(3)從CA的證書中提取簽發(fā)者信息:
byte[] encod1=c1.getEncoded();
X509CertImpl shopcimp1=new X509CertImpl(encod1);
X509CertInfo shopcinfo1=(X509CertInfo)shopcimp1.get(X509CertImpl.NAME+
"."+X509CertImpl.INFO);
X500Name issuer=(X500Name)shopcinfo1.get(X509CertInfo.SUBJECT+
"."+CertificateIssuerName.DN_NAME);
(4)獲取待簽發(fā)的證書相關信息,與(3)類似;
(5)設置新證書的有效期、序列號、簽發(fā)者和簽名算法:
//設置新證書有效期為1年
Date begindate =new Date();
Date enddate =new Date(begindate.getTime()+3000*24*360*60*1000L); CertificateValidity cv=new CertificateValidity(begindate,enddate);
cinfo2.set(X509CertInfo.VALIDITY,cv);
//設置新證書序列號
int sn=(int)(begindate.getTime()/1000);
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
//設置新證書簽發(fā)者
cinfo2.set(X509CertInfo.ISSUER+"."+
CertificateIssuerName.DN_NAME,issuer);
//設置新證書算法
AlgorithmId algorithm =
new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+
"."+CertificateAlgorithmId.ALGORITHM, algorithm);
(6)創(chuàng)建證書并簽發(fā):
// 創(chuàng)建證書
X509CertImpl newcert=new X509CertImpl(cinfo2);
// 簽名
newcert.sign(caprk,"MD5WithRSA");
(7)將新證書提供給注冊用戶,并提示安裝,一般的做法是在用戶注冊成功后系統(tǒng)立即返回一個證書對象給中間層某個Servlet,由其返回給用戶。
參考文獻
[1]沈耀,陳昊鵬,李新顏.EJB容器中基于JAAS 的安全機制的實現(xiàn).[J]:計算機應用與軟件 2004.9 16~18
[2](美)Jess Garms著,龐南等譯. Java安全性編程指南[M].北京:電子工業(yè)出版社 2002
[3]
[4] 蔡劍,景楠. Java 網(wǎng)絡程序設計:J2EE(含1.4最新功能)[M].北京: 清華大學出版社 2003
[5](美)John Bell Tony Loton. Java Servlets 2.3編程指南[M].北京: 電子工業(yè)出版社 2002
[6](美)Joseph J.Bambara等著,劉堃等譯. J2EE技術內(nèi)幕[M].北京:機械工業(yè)出版社 2002
[7](美)Li Gong著.JAVA 2平臺安全技術——結(jié)構(gòu)、API設計和實現(xiàn)[M].北京: 機械工業(yè)出版社 2000
[8](英)Danny Ayers等著,曾國平等譯. Java服務器高級編程[M].北京:機械工業(yè)出版社 2005
[9]
[10]
1.權限的設置:審核人員看不到操作模塊;操作人員看不到審核模塊
2.記錄表中應該有一個狀態(tài)的字段:操作人員錄入信息后該條記錄應該為待審核(或者是提交審核)狀態(tài),(如果輸入了二級密碼,狀態(tài)直接為審核通過);審核模塊中直接查詢狀態(tài)為待審核(或提交審核)的記錄,同意后更新該記錄的狀態(tài)為審核通過;
3.你至少需要以下幾個action:
①操作員提交數(shù)據(jù)的action(包括修改,可以公用這個action)
②審核員查詢數(shù)據(jù)的action
③審核員更新記錄狀態(tài)的action
(這里沒考慮權限的問題,如果考慮權限問題,你的工作量還有很多很多)
一:需要包含的包
import java.security.*;
import java.io.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import sun.security.x509.*
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
二:從文件中讀取證書
用keytool將.keystore中的證書寫入文件中,然后從該文件中讀取證書信息
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in=new FileInputStream("out.csr");
Certificate c=cf.generateCertificate(in);
String s=c.toString();
三:從密鑰庫中直接讀取證書
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
java.security.cert.Certificate c=ks.getCertificate(alias);//alias為條目的別名
四:JAVA程序中顯示證書指定信息
System.out.println("輸出證書信息:\n"+c.toString());
System.out.println("版本號:"+t.getVersion());
System.out.println("序列號:"+t.getSerialNumber().toString(16));
System.out.println("主體名:"+t.getSubjectDN());
System.out.println("簽發(fā)者:"+t.getIssuerDN());
System.out.println("有效期:"+t.getNotBefore());
System.out.println("簽名算法:"+t.getSigAlgName());
byte [] sig=t.getSignature();//簽名值
PublicKey pk=t.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公鑰");
for(int i=0;ipkenc.length;i++)System.out.print(pkenc[i]+",");
五:JAVA程序列出密鑰庫所有條目
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
Enumeration e=ks.aliases();
while(e.hasMoreElements())
java.security.cert.Certificate c=ks.getCertificate((String)e.nextElement());
六:JAVA程序修改密鑰庫口令
String oldpass="123456";
String newpass="654321";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,oldpass.toCharArray());
in.close();
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,newpass.toCharArray());
output.close();
七:JAVA程序修改密鑰庫條目的口令及添加條目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
Certificate [] cchain=ks.getCertificate(alias);獲取別名對應條目的證書鏈
PrivateKey pk=(PrivateKey)ks.getKey(alias,oldkeypass.toCharArray());獲取別名對應條目的私鑰
ks.setKeyEntry(alias,pk,newkeypass.toCharArray(),cchain);向密鑰庫中添加條目
第一個參數(shù)指定所添加條目的別名,假如使用已存在別名將覆蓋已存在條目,使用新別名將增加一個新條目,第二個參數(shù)為條目的私鑰,第三個為設置的新口令,第四個為該私鑰的公鑰的證書鏈
FileOutputStream output=new FileOutputStream("another");
ks.store(output,storepass.toCharArray())將keystore對象內(nèi)容寫入新文件
八:JAVA程序檢驗別名和刪除條目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
ks.containsAlias("sage");檢驗條目是否在密鑰庫中,存在返回true
ks.deleteEntry("sage");刪除別名對應的條目
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,storepass.toCharArray())將keystore對象內(nèi)容寫入文件,條目刪除成功
九:JAVA程序簽發(fā)數(shù)字證書
(1)從密鑰庫中讀取CA的證書
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
java.security.cert.Certificate c1=ks.getCertificate("caroot");
(2)從密鑰庫中讀取CA的私鑰
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass.toCharArray());
(3)從CA的證書中提取簽發(fā)者的信息
byte[] encod1=c1.getEncoded(); 提取CA證書的編碼
X509CertImpl cimp1=new X509CertImpl(encod1); 用該編碼創(chuàng)建X509CertImpl類型對象
X509CertInfo cinfo1=(X509CertInfo)cimp1.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 獲取X509CertInfo對象
X500Name issuer=(X500Name)cinfo1.get(X509CertInfo.SUBJECT+"."+CertificateIssuerName.DN_NAME); 獲取X509Name類型的簽發(fā)者信息
(4)獲取待簽發(fā)的證書
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("user.csr");
java.security.cert.Certificate c2=cf.generateCertificate(in);
(5)從待簽發(fā)的證書中提取證書信息
byte [] encod2=c2.getEncoded();
X509CertImpl cimp2=new X509CertImpl(encod2); 用該編碼創(chuàng)建X509CertImpl類型對象
X509CertInfo cinfo2=(X509CertInfo)cimp2.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 獲取X509CertInfo對象
(6)設置新證書有效期
Date begindate=new Date(); 獲取當前時間
Date enddate=new Date(begindate.getTime()+3000*24*60*60*1000L); 有效期為3000天
CertificateValidity cv=new CertificateValidity(begindate,enddate); 創(chuàng)建對象
cinfo2.set(X509CertInfo.VALIDITY,cv); 設置有效期
(7)設置新證書序列號
int sn=(int)(begindate.getTime()/1000); 以當前時間為序列號
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
(8)設置新證書簽發(fā)者
cinfo2.set(X509CertInfo.ISSUER+"."+CertificateIssuerName.DN_NAME,issuer);應用第三步的結(jié)果
(9)設置新證書簽名算法信息
AlgorithmId algorithm=new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+"."+CertificateAlgorithmId.ALGORITHM,algorithm);
(10)創(chuàng)建證書并使用CA的私鑰對其簽名
X509CertImpl newcert=new X509CertImpl(cinfo2);
newcert.sign(caprk,"MD5WithRSA"); 使用CA私鑰對其簽名
(11)將新證書寫入密鑰庫
ks.setCertificateEntry("lf_signed",newcert);
FileOutputStream out=new FileOutputStream("newstore");
ks.store(out,"newpass".toCharArray()); 這里是寫入了新的密鑰庫,也可以使用第七條來增加條目
十:數(shù)字證書的檢驗
(1)驗證證書的有效期
(a)獲取X509Certificate類型對象
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in1=new FileInputStream("aa.crt");
java.security.cert.Certificate c1=cf.generateCertificate(in1);
X509Certificate t=(X509Certificate)c1;
in2.close();
(b)獲取日期
Date TimeNow=new Date();
(c)檢驗有效性
try{
t.checkValidity(TimeNow);
System.out.println("OK");
}catch(CertificateExpiredException e){ //過期
System.out.println("Expired");
System.out.println(e.getMessage());
}catch((CertificateNotYetValidException e){ //尚未生效
System.out.println("Too early");
System.out.println(e.getMessage());}
(2)驗證證書簽名的有效性
(a)獲取CA證書
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("caroot.crt");
java.security.cert.Certificate cac=cf.generateCertificate(in2);
in2.close();
(c)獲取CA的公鑰
PublicKey pbk=cac.getPublicKey();
(b)獲取待檢驗的證書(上步已經(jīng)獲取了,就是C1)
(c)檢驗證書
boolean pass=false;
try{
c1.verify(pbk);
pass=true;
}catch(Exception e){
pass=false;
System.out.println(e);
}
通過JAVA來讀取數(shù)字證書的方法獲取20位指紋:
CARead.java文件代碼:
public class CARead extends JPanel {
private String CA_Name;
private String CA_ItemData[][] = new String[9][2];
private String[] columnNames = { "證書字段標記", "內(nèi)容" };
public CARead(String CertName) {
CA_Name = CertName;
/* 三個Panel用來顯示證書內(nèi)容 */
JTabbedPane tabbedPane = new JTabbedPane();
JPanel panelNormal = new JPanel();
tabbedPane.addTab("普通信息", panelNormal);
JPanel panelAll = new JPanel();
panelAll.setLayout(new BorderLayout());
tabbedPane.addTab("所有信息", panelAll);
JPanel panelBase64 = new JPanel();
panelBase64.setLayout(new BorderLayout());
tabbedPane.addTab("Base64編碼形式的信息", panelBase64);
/* 讀取證書常規(guī)信息 */
Read_Normal(panelNormal);
/* 讀取證書文件字符串表示內(nèi)容 */
Read_Bin(panelAll);
/* 以Base64編碼形式讀取證書文件的信息 */
Read_Raw(panelBase64);
tabbedPane.setSelectedIndex(0);
setLayout(new GridLayout(1, 1));
add(tabbedPane);
}
private int Read_Normal(JPanel panel) {
String Field;
try {
CertificateFactory certificate_factory = CertificateFactory
.getInstance("X.509");
FileInputStream file_inputstream = new FileInputStream(CA_Name);
X509Certificate x509certificate = (X509Certificate) certificate_factory
.generateCertificate(file_inputstream);
Field = x509certificate.getType();
CA_ItemData[0][0] = "類型";
CA_ItemData[0][1] = Field;
Field = Integer.toString(x509certificate.getVersion());
CA_ItemData[1][0] = "版本";
CA_ItemData[1][1] = Field;
Field = x509certificate.getSubjectDN().getName();
CA_ItemData[2][0] = "標題";
CA_ItemData[2][1] = Field;
Field=x509certificate.getNotBefore().toString();//得到開始有效日期
CA_ItemData[3][0] = "開始有效日期";
CA_ItemData[3][1] = Field;
Field=x509certificate. getNotAfter().toString();//得到截止日期
CA_ItemData[4][0] = "截止日期";
CA_ItemData[4][1] = Field;
Field=x509certificate.getSerialNumber().toString(16);//得到序列號
CA_ItemData[5][0] = "序列號";
CA_ItemData[5][1] = Field;
Field=x509certificate.getIssuerDN().getName();//得到發(fā)行者名
CA_ItemData[6][0] = "發(fā)行者名";
CA_ItemData[6][1] = Field;
Field=x509certificate.getSigAlgName();//得到簽名算法
CA_ItemData[7][0] = "簽名算法";
CA_ItemData[7][1] = Field;
Field=x509certificate.getPublicKey().getAlgorithm();//得到公鑰算法
CA_ItemData[8][0] = "公鑰算法";
CA_ItemData[8][1] = Field;
//關閉輸入流對象
file_inputstream.close();
final JTable table = new JTable(CA_ItemData, columnNames);
TableColumn tc = null; //表格列控制
tc = table.getColumnModel().getColumn(1);//得到表頭
tc.setPreferredWidth(600);//設置寬度
panel.add(table);//增加到布局面板
} catch (Exception exception) {
exception.printStackTrace(); //異常捕獲、
return -1;
}
return 0;
}
//讀取二進制指紋文件
private int Read_Bin(JPanel panel) {
try {
FileInputStream file_inputstream = new FileInputStream(CA_Name);
DataInputStream data_inputstream = new DataInputStream(
file_inputstream);
CertificateFactory certificatefactory = CertificateFactory
.getInstance("X.509");
byte[] bytes = new byte[data_inputstream.available()];
data_inputstream.readFully(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
JEditorPane Cert_EditorPane;
Cert_EditorPane = new JEditorPane();
X509Certificate cert=null;
//遍歷得到所有的證書屬性
if (bais.available() 0)
{
cert = (X509Certificate) certificatefactory .generateCertificate(bais);
Cert_EditorPane.setText(cert.toString());
}
Cert_EditorPane.disable();
JScrollPane edit_scroll = new JScrollPane(Cert_EditorPane);
panel.add(edit_scroll);
file_inputstream.close();
data_inputstream.close();
} catch (Exception exception) {
exception.printStackTrace();
return -1;
}
return 0;
}
private int Read_Raw(JPanel panel) {
try {
JEditorPane Cert_EditorPane = new JEditorPane();
StringBuffer strBuffer =new StringBuffer();
File inputFile = new File(CA_Name);
FileReader in = new FileReader(inputFile);
char[] buf = new char[2000];
int len = in.read(buf, 0, 2000);
for (int i = 1; i len; i++) {
strBuffer.append(buf[i]);
}
in.close();
Cert_EditorPane.setText(strBuffer.toString());
Cert_EditorPane.disable();
JScrollPane edit_scroll = new JScrollPane(Cert_EditorPane);
panel.add(edit_scroll);
} catch (Exception exception) {
exception.printStackTrace();
return -1;
}
return 0;
}
}
openssl genrsa -des3 -out server.key 1024運行時會提示輸入密碼,此密碼用于加密key文件(參數(shù)des3便是指加密算法,當然也可以選用其他你認為安全的算法.),以后每當需讀取此文件(通過openssl提供的命令或API)都需輸入口令.如果覺得不方便,也可以..
PKI 目前使用最多的非對稱算法是 RSA。對于基于 RSA 的 PKI 而言,CA 應有一對 RSA 的公私鑰對,私鑰是 CA 的生命,嚴格保密,而公鑰則發(fā)布給使用方。CA 簽發(fā)一張證書的話,主要是使用 CA 的 RSA 私鑰對證書進行簽名,并將簽名結(jié)果保存在證書當中。使用者通過 CA 發(fā)布的公鑰來驗證證書中的簽名值,就可以確定該證書是否是由該 CA 簽發(fā)的。自己要做的就是從證書中提取簽名數(shù)據(jù)和用于簽名的原始數(shù)據(jù),再使用 CA 的公鑰驗證這個簽名就可以了。