摘要
:
在本文中,我用詳細(xì)的語(yǔ)言和大量的
圖片
及完整的
程序
源碼向你展示了在
JAVA
中如何實(shí)現(xiàn)通過(guò)消息摘要、消息驗(yàn)證碼達(dá)到
安全
通信、以及用Java的工具生成數(shù)字證書(shū),和用程序給數(shù)字證書(shū)簽名、以及用簽名后的數(shù)學(xué)證書(shū)簽名applet突破applet的訪問(wèn)權(quán)限的過(guò)程,給出了全部例子的詳細(xì)代碼。
通過(guò)本文中你可以學(xué)到以下知識(shí):
●
程序間如何安全通信
●
什么是 及 如何生成消息摘要
●
什么是 及 如何生成消息驗(yàn)證碼
●
如何使用 Java工具生成和維護(hù)數(shù)字證書(shū)庫(kù)
●
如何用程序給數(shù)字證書(shū)驗(yàn)證簽名
●
如何利用數(shù)字證書(shū)給 applet簽名突破applet的訪問(wèn)權(quán)限
關(guān)鍵字
:
消息摘要、消息驗(yàn)證碼、指紋、加密、安全、 Java、數(shù)字簽名、applet、數(shù)字證書(shū)
一、基礎(chǔ)知識(shí)
計(jì)算機(jī)安全通信過(guò)程中,常使用消息摘要和消息驗(yàn)證碼來(lái)保證傳輸?shù)臄?shù)據(jù)未曾被第三方修改。
消息摘要是對(duì)原始數(shù)據(jù)按照一定算法進(jìn)行計(jì)算得到的結(jié)果,它主要檢測(cè)原始數(shù)據(jù)是否被修改過(guò)。消息摘要與加密不同,加密是對(duì)原始數(shù)據(jù)進(jìn)行變換,可以從變換后的數(shù)據(jù)中獲得原始數(shù)據(jù),而消息摘要是從原始數(shù)據(jù)中獲得一部分信息,它比原始數(shù)據(jù)少得多,因此消息摘要可以看作是原始數(shù)據(jù)的指紋。
例:下面一段程序計(jì)算一段字符串的消息摘要
package com.messagedigest;
import java.security.*; public class DigestPass { public static void main(String[] args) throws Exception{ String str="Hello,I sent to you 80 yuan."; MessageDigest md = MessageDigest.getInstance("MD5");//常用的有MD5,SHA算法等 md.update(str.getBytes("UTF-8"));//傳入原始字串 byte[] re = md.digest();//計(jì)算消息摘要放入byte數(shù)組中 //下面把消息摘要轉(zhuǎn)換為字符串 String result = ""; for(int i=0;i<re.length;i++){ result += Integer.toHexString((0x000000ff&re[i])|0xffffff00).substring(6); } System.out.println(result); } } |
當(dāng)我們有時(shí)需要對(duì)一個(gè)文件加密時(shí),以上方式不再適用。
又例:下面一段程序計(jì)算從輸入(出)流中計(jì)算消息摘要。
package com.messagedigest;
import java.io.*; import java.security.*; public class DigestInput { public static void main(String[] args) throws Exception{ String fileName = "test.txt"; MessageDigest md = MessageDigest.getInstance("MD5"); FileInputStream fin = new FileInputStream(fileName); DigestInputStream din = new DigestInputStream(fin,md);//構(gòu)造輸入流 //DigestOutputStream dout = new DigestOutputStream(fout,md); //使用輸入(出)流可以自己控制何時(shí)開(kāi)始和關(guān)閉計(jì)算摘要 //也可以不控制,將全過(guò)程計(jì)算 //初始時(shí)是從開(kāi)始即開(kāi)始計(jì)算,如我們可以開(kāi)始時(shí)關(guān)閉,然后從某一部分開(kāi)始,如下: //din.on(false); int b; while((b=din.read())!=-1){ //做一些對(duì)文件的處理 //if(b=='$') din.on(true); //當(dāng)遇到文件中的符號(hào)$時(shí)才開(kāi)始計(jì)算 } byte[] re = md.digest();//獲得消息摘要 //下面把消息摘要轉(zhuǎn)換為字符串 String result = ""; for(int i=0;i<re.length;i++){ result += Integer.toHexString((0x000000ff&re[i])|0xffffff00).substring(6); } System.out.println(result); } } |
當(dāng)A和B通信時(shí),A將數(shù)據(jù)傳給B時(shí),同時(shí)也將數(shù)據(jù)的消息摘要傳給B,B收到后可以用該消息摘要驗(yàn)證A傳的消息是否正確。這時(shí)會(huì)產(chǎn)生問(wèn)題,即若傳遞過(guò)程中別人修改了數(shù)據(jù)時(shí),同時(shí)也修改了消息摘要。B就無(wú)法確認(rèn)數(shù)據(jù)是否正確。消息驗(yàn)證碼可以解決這一問(wèn)題。
使用消息驗(yàn)證碼的前提是 A和B雙方有一個(gè)共同的密鑰,這樣A可以將數(shù)據(jù)計(jì)算出來(lái)的消息摘要加密后發(fā)給B,以防止消息摘要被改。由于使用了共同的密鑰,所以稱(chēng)為“驗(yàn)證碼”。
例、下面的程序即可利用共同的密鑰來(lái)計(jì)算消息摘要的驗(yàn)證碼
package com.mac;
import java.io.*; import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; public class MyMac { public static void main(String[] args) throws Exception{ //這是一個(gè)消息摘要串 String str="TestString"; //共同的密鑰編碼,這個(gè)可以通過(guò)其它算法計(jì)算出來(lái) byte[] kb={11,105,-119,50,4,-105,16,38,-14,-111,21,-95,70,-15,76,-74, 67,-88,59,-71,55,-125,104,42}; //獲取共同的密鑰 SecretKeySpec k = new SecretKeySpec(kb,"HMACSHA1"); //獲取Mac對(duì)象 Mac m = Mac.getInstance("HmacMD5"); m.init(k); m.update(str.getBytes("UTF-8")); byte[] re = m.doFinal();//生成消息碼 //下面把消息碼轉(zhuǎn)換為字符串 String result = ""; for(int i=0;i<re.length;i++){ result += Integer.toHexString((0x000000ff&re[i])|0xffffff00).substring(6); } System.out.println(result); } } |
使用以上兩種技術(shù)可以保證數(shù)據(jù)沒(méi)有經(jīng)過(guò)改變,但接收者還無(wú)法確定數(shù)據(jù)是否確實(shí)是某個(gè)人發(fā)來(lái)的。盡管消息碼可以確定數(shù)據(jù)是某個(gè)有同樣密鑰的人發(fā)來(lái)的,但這要求雙方具有共享的密鑰,若有一組用戶共享,我們就無(wú)法確定數(shù)據(jù)的來(lái)源了。
數(shù)字簽名可以解決這一問(wèn)題。數(shù)字簽名利用非對(duì)稱(chēng)加密技術(shù),發(fā)送者使用私鑰加密數(shù)據(jù)產(chǎn)生的消息摘要(簽名),接收者使用發(fā)送者的公鑰解密消息摘要以驗(yàn)證簽名是否是某個(gè)人的。由于私鑰只有加密者才有,因此如果接收者用某個(gè)公鑰解密了某個(gè)消息摘要,就可以確定這段消息摘要必然是對(duì)應(yīng)的私鑰持有者發(fā)來(lái)的。
使用數(shù)字簽名的前提是接收數(shù)據(jù)者能夠確信驗(yàn)證簽名時(shí)(用發(fā)送者的私鑰加密消息摘要)所用的公鑰確實(shí)是某個(gè)人的 (因?yàn)橛锌赡苡腥思俑婀€)。數(shù)字證書(shū)可以解決這個(gè)問(wèn)題。
數(shù)字證書(shū)含有兩部分?jǐn)?shù)據(jù):一部分是對(duì)應(yīng)主體(單位或個(gè)人)的信息,另一部分是這個(gè)主體所對(duì)應(yīng)的公鑰。即數(shù)字證書(shū)保存了主體和它的公鑰的一一對(duì)應(yīng)關(guān)系。同樣,數(shù)字證書(shū)也有可能被假造,如何判定數(shù)字證書(shū)的內(nèi)容的真實(shí)性呢?所以,有效的數(shù)字證書(shū)必須經(jīng)過(guò)權(quán)威 CA的簽名,即權(quán)威CA驗(yàn)證數(shù)字證書(shū)的內(nèi)容的真實(shí)性,然后再在數(shù)字證書(shū)上使用自己的私鑰簽名(相當(dāng)于在證書(shū)加章確認(rèn))。
這樣,當(dāng)用戶收到這樣的數(shù)字證書(shū)后,會(huì)用相應(yīng)的權(quán)威 CA的公鑰驗(yàn)證該證書(shū)的簽名(因?yàn)闄?quán)威的CA的公鑰在
操作系統(tǒng)
中己經(jīng)安裝)。根據(jù)非對(duì)稱(chēng)加密的原理,如果該證書(shū)不是權(quán)威CA簽名的,將不能通過(guò)驗(yàn)證,即該證書(shū)是不可靠的。
若通過(guò)驗(yàn)證,即可證明此證書(shū)含的信息(發(fā)信人的公鑰和信息)是無(wú)誤的。于是可以信任該證書(shū),便可以通過(guò)該證書(shū)內(nèi)含的公鑰來(lái)確認(rèn)數(shù)據(jù)確實(shí)是發(fā)送者發(fā)來(lái)的。
于是,雙方通信時(shí), A把數(shù)據(jù)的消息摘要用自己的私鑰加密(即簽名),然后把自己的數(shù)字證書(shū)和數(shù)據(jù)及簽名后的消息摘要一起發(fā)送給B,B處查看A的數(shù)字證書(shū),如果A的數(shù)字證書(shū)是經(jīng)過(guò)權(quán)威CA驗(yàn)證可靠的,便信任A,便可使用A的數(shù)字證書(shū)中附帶的A的公鑰解密消息摘要(這一過(guò)程同時(shí)確認(rèn)了發(fā)送數(shù)據(jù)的人又可以解密消息摘要),然后通過(guò)解密后的消息摘要驗(yàn)證數(shù)據(jù)是否正確無(wú)誤沒(méi)被修改。
利用這一原理,我們可以突破 java的applet小程序在瀏覽器中的權(quán)限,由于默認(rèn)的applet權(quán)限控制不允許它訪問(wèn)操作系統(tǒng)級(jí)的一切。于是我們可以用我們數(shù)字證書(shū)來(lái)給 applet簽名,然后客戶端收到該applet時(shí),系統(tǒng)會(huì)自動(dòng)查看給該applet簽名的數(shù)字證書(shū)并提供給終端用戶判定是否信認(rèn)該數(shù)字證書(shū),如果用戶信認(rèn),則該applet便有了訪問(wèn)系統(tǒng)的權(quán)限。
?
二、 Java中的數(shù)字證書(shū)的生成及維護(hù)方法
Java中的keytool.exe可以用來(lái)創(chuàng)建數(shù)字證書(shū),所有的數(shù)字證書(shū)是以一條一條(采用別名區(qū)別)的形式存入證書(shū)庫(kù)的中,證書(shū)庫(kù)中的一條證書(shū)包含該條證書(shū)的私鑰,公鑰和對(duì)應(yīng)的數(shù)字證書(shū)的信息。證書(shū)庫(kù)中的一條證書(shū)可以導(dǎo)出數(shù)字證書(shū)文件,數(shù)字證書(shū)文件只包括主體信息和對(duì)應(yīng)的公鑰。
每一個(gè)證書(shū)庫(kù)是一個(gè)文件組成,它有訪問(wèn)密碼,在首次創(chuàng)建時(shí),它會(huì)自動(dòng)生成證書(shū)庫(kù),并要求指定訪問(wèn)證書(shū)庫(kù)的密碼。
在創(chuàng)建證書(shū)的的時(shí)候,需要填寫(xiě)證書(shū)的一些信息和證書(shū)對(duì)應(yīng)的私鑰密碼。這些信息包括 CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx,它們的意思是:
CN(Common Name名字與姓氏)
OU(Organization Unit組織單位名稱(chēng))
O(Organization組織名稱(chēng))
L(Locality城市或區(qū)域名稱(chēng))
ST(State州或省份名稱(chēng))
C(Country國(guó)家名稱(chēng))
可以采用交互式讓工具提示輸入以上信息,也可以采用參數(shù)
-dname "CN=xx,OU=xx,O=xx,L=xx,ST=xx,C=xx"來(lái)自動(dòng)創(chuàng)建。
1、示例
如下所示一句采用交互式創(chuàng)建一個(gè)證書(shū),指定證書(shū)庫(kù)為 abnerCALib,創(chuàng)建別名為abnerCA的一條證書(shū),它指定用RSA算法生成,
且指定密鑰長(zhǎng)度為 1024,證書(shū)有效期為3650天:
C:/j2sdk1.4.1_01/mykeystore>keytool -genkey -alias abnerCA -keyalg RSA -keysize 1024 -keystore abnerCALib -validity 3650 |
如下圖所示:
|
上圖中最后一步,我們輸入的是 CN,代表中國(guó)的縮寫(xiě),也可以直接輸入“中國(guó)”兩個(gè)字。
2、證書(shū)的操作方法
●
證書(shū)的顯示
如:
keytool –list –keystore abnerCALib |
將顯示 abnerCALib證書(shū)庫(kù)的的所有證書(shū)列表:如下圖示:
|
又如: keytool -list -alias abnerCA -keystore abnerCALib
將顯示 abnerCALib證書(shū)庫(kù)中別名為abnerCA的證書(shū)的信息。如下圖所示:
|
又如: keytool -list -v -alias abnerCA -keystore abnerCALib
將顯示證書(shū)的詳細(xì)信息( -v參數(shù))如下圖所示:
●
將證書(shū)導(dǎo)出到證書(shū)文件
如: keytool -export -alias abnerCA -file abnerCA.cer -keystore abnerCALib
將把證書(shū)庫(kù) abnerCALib中的別名為abnerCA的證書(shū)導(dǎo)出到abnerCA.cer證書(shū)文件中,
它包含證書(shū)主體的信息及證書(shū)的公鑰,不包括私鑰,可以公開(kāi),如下圖所示 :
|
上面導(dǎo)出的證書(shū)文件是以二進(jìn)制編碼文件,無(wú)法用文本編輯器正確顯示,因此不利用公布證書(shū),可以加上 -rfc參數(shù)以一種可打印的編者編碼輸出。
如:
keytool -export -alias abnerCA -file abnerCA.cer -keystore abnerCALib -storepass 100200 –rfc |
這個(gè)命令在命令行中指定了證書(shū)庫(kù)的訪問(wèn)密碼,同時(shí)指定以可查看編碼的方式輸出。
3、通過(guò)證書(shū)文件查看證書(shū)的信息
通過(guò)命令 :keytool –printcert –file abnerCA.cer可以查看證書(shū)文件的信息。
也可以在 windows中雙擊產(chǎn)生的證書(shū)文件直接查看。
●
證書(shū)條目的刪除
keytool的命令行參數(shù)-delete可以刪除密鑰庫(kù)中的條目,如:
keytool -delete -alias abnerCA -keystore abnerCALib |
這條命令將 abnerCALib庫(kù)中的abnerCA這一條證書(shū)刪除了。
●
證書(shū)條目口令的修改
如:
keytool –keypasswd –alias abnerCA –keystore abnerCALib |
可以以交互的方式修改 abnerCALib證書(shū)庫(kù)中的條目為abnerCA的證書(shū)。
Keytool –keypasswd –alias abnerCA –keypass 123456 –new 200100 –storepass 1002 00 –keystore abnerCALib |
這一行命令以非交互式的方式修改庫(kù)中別名為 abnerCA的證書(shū)的密碼為新密碼123456,行中的200100是指該條證書(shū)的原密碼, 1002 00是指證書(shū)庫(kù)的密碼。
?
三、數(shù)字證書(shū)的簽發(fā)(簽名)
我們?cè)谏厦鎰?chuàng)建好了數(shù)字證書(shū),但這些數(shù)字證書(shū)還沒(méi)有經(jīng)過(guò)權(quán)威 CA的證實(shí)(即簽名)。一般情況下,我們需要將這些證書(shū)發(fā)送給權(quán)威的CA,并申請(qǐng)其簽名以確認(rèn)數(shù)字證書(shū)讓客戶信任。
下面我們將模仿自己是一個(gè)權(quán)威的數(shù)字證書(shū)認(rèn)證機(jī)構(gòu) CA,這個(gè)機(jī)構(gòu)將采用自己的私鑰來(lái)簽發(fā)其它的證書(shū)。這個(gè)簽發(fā)過(guò)程是這樣的:我們自己是CA,我們自己有一個(gè)自簽的數(shù)字證書(shū)存入數(shù)字證書(shū)庫(kù)中。在數(shù)字證書(shū)庫(kù)中的這個(gè)我們的CA數(shù)字證書(shū),它含有私鑰,公鑰和我們這個(gè)CA的主體信息。下面這一個(gè)指令可以創(chuàng)建一個(gè)CA的自簽的數(shù)字證書(shū):
keytool –genkey –dname “CN=美森系統(tǒng)軟件有限公司,OU=美森系統(tǒng)軟件有限公司,O=美森系統(tǒng)軟件有限公司,L=成都市,ST=四川省,C=中國(guó)” –alias MissionCA –keyalg RSA –keysize 1024 –keystore abnerCALib –keypass 200100 –storepass 100200 –validity 3650
上面,我們?cè)?abnerCALib這個(gè)數(shù)字證書(shū)庫(kù)中創(chuàng)建了一個(gè)別名為:missionCA、有效期為3650天、算法為RSA且密鑰長(zhǎng)度為1024的數(shù)字證書(shū),這條證書(shū)的私鑰密碼為:200100,證書(shū)庫(kù)的訪問(wèn)密碼為:100200。這條別名為missionCA的證書(shū)代表我們自己的權(quán)威CA即:美森系統(tǒng)軟件有限公司這個(gè)權(quán)威CA。以后我們將用這個(gè)證書(shū)來(lái)簽名其它的數(shù)字證書(shū)。
現(xiàn)在我要給自己申請(qǐng)一個(gè)數(shù)字證書(shū),我可以這么做:先在數(shù)字證書(shū)庫(kù)中創(chuàng)建一條證書(shū):
keytool –genkey –dname “CN=柴政,OU=美森系統(tǒng)軟件有限公司,O=美森系統(tǒng)軟件有限公司,L=成都市,ST=四川省,C=中國(guó)” –alias abnerCA –keyalg RSA –keysize 1024 –keystore abnerCALib –keypass 200100 –storepass 100200 –validity 3650
這樣創(chuàng)建了一個(gè)別名為 abnerCA的數(shù)字證書(shū),我們可以將它導(dǎo)出為cer文件(見(jiàn)前)。
接著,我們可以用上一步生成的 CA的自簽證書(shū)來(lái)簽名我這個(gè)數(shù)字證書(shū)了。
CA簽名數(shù)字證書(shū)的過(guò)程需用以下程序來(lái)進(jìn)行,這個(gè)程序是自解釋的:
package com.security;
import java.io.*; import java.security.*; import java.security.cert.*; import java.util.*; import java.math.*; import sun.security.x509.*; /** * <p>Description: 該程序根據(jù)簽發(fā)者(CA)的證書(shū)信息(即CA的私鑰)來(lái)對(duì)被簽發(fā)者 * 的證書(shū)進(jìn)行簽名,過(guò)程即是使用CA的證書(shū)和被簽證書(shū)來(lái)重構(gòu)形成一個(gè)新的證書(shū)</p> * @author abnerchai * @version 1.0 */ public class SignCert { public static void main(String[] args) throws Exception{ char[] storepass = "100200".toCharArray(); //存放CA證書(shū)和被簽證書(shū)的證書(shū)庫(kù)的訪問(wèn)密碼 char[] cakeypass = "200100".toCharArray();//CA數(shù)字證書(shū)條目的訪問(wèn)密碼 String alias = "missionCA"; //CA證書(shū)在證書(shū)庫(kù)中的別名,這個(gè)CA的證書(shū)用來(lái)簽名其它的證書(shū) String name = "abnerCALib";//存放CA證書(shū)和被簽證書(shū)的證書(shū)庫(kù)的名字 String newLib = "SignedLib"; //新證書(shū)庫(kù)的名字,如果需要將簽名后的證書(shū)放入新庫(kù),這是新庫(kù)的名字 char[] newLibPass = "100200".toCharArray();//設(shè)置新庫(kù)的訪問(wèn)密碼 String cerFileName = "abnerCA.cer";//被簽證書(shū)的證書(shū)文件名 String aliasName = "abnerCA";//被簽證書(shū)在證書(shū)庫(kù)中的alias別名 char[] namePass = "200100".toCharArray(); //被簽證書(shū)的條目在證書(shū)庫(kù)的私鑰密碼 int n =3; //被簽證書(shū)的有效期,以年為單位,以當(dāng)前時(shí)間開(kāi)始計(jì)算 int sn = 200406001; //序列號(hào)可自己定義,這里定義的意義為2004年6月簽發(fā),是本年度CA簽發(fā)的第多少個(gè)以001計(jì)算,要求唯一 String afteraliasName = "abnerCA_Signed"; //簽名后新產(chǎn)生的被簽過(guò)名的證書(shū)在庫(kù)中的別名 char[] afterNewPass = "200100".toCharArray(); //簽名后新產(chǎn)生的被簽過(guò)名的證書(shū)在庫(kù)的條目的私鑰的密碼 //裝載證書(shū)庫(kù) FileInputStream in = new FileInputStream(name); KeyStore ks = KeyStore.getInstance("JKS");//JKS為證書(shū)庫(kù)的類(lèi)型 ks.load(in,storepass); //從證書(shū)庫(kù)中讀出簽發(fā)者(CA)的證書(shū) java.security.cert.Certificate cl = ks.getCertificate(alias); //讀出一個(gè)CA證書(shū),這里的l是字母l不是數(shù)據(jù)字1 PrivateKey privateKey = (PrivateKey)ks.getKey(alias,cakeypass); //根據(jù)別名和證書(shū)密碼讀出CA證書(shū)的私鑰 in.close(); //從證書(shū)庫(kù)中讀出的簽發(fā)者(CA)的證書(shū)中提取簽發(fā)者的信息 byte[] encodl = cl.getEncoded();//提取證書(shū)的編碼,這里是字母l不是數(shù)據(jù)字1 X509CertImpl cimpl = new X509CertImpl(encodl); //這里是字母l不是數(shù)據(jù)字1,根據(jù)證書(shū)的編碼創(chuàng)建X509CertImpl類(lèi)型的對(duì)象 //根據(jù)上面的對(duì)象獲得X509CertInfo類(lèi)型的對(duì)象,該對(duì)象封裝了證書(shū)的全部?jī)?nèi)容。 X509CertInfo cinfo_first = (X509CertInfo)cimpl.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); //然后獲得X500Name類(lèi)型的簽發(fā)者信息 X500Name issuer = (X500Name) cinfo_first.get(X509CertInfo.SUBJECT+"."+CertificateIssuerName.DN_NAME); //獲取待簽發(fā)的證書(shū),即獲取被簽發(fā)者的證書(shū) //可從密鑰庫(kù)中獲取,也可從導(dǎo)出的證書(shū)文件中獲取,這里給出兩種方式 //////////////////////////////////////////////////////////////////////// //方式一、采用從導(dǎo)出的cer文件中獲取 start /////////////////////////////////////////////////////////////////////////////// /* CertificateFactory cf = CertificateFactory.getInstance("X.509"); //X.509是使用最多的一種數(shù)字證書(shū)標(biāo)準(zhǔn) FileInputStream in2 = new FileInputStream(cerFileName);//被簽證書(shū)文件 java.security.cert.Certificate c2 = cf.generateCertificate(in2); //生成需要被簽的證書(shū) in2.close(); byte[] encod2 = c2.getEncoded(); X509CertImpl cimp2 = new X509CertImpl(encod2); //獲得被簽證書(shū)的詳細(xì)內(nèi)容,然后根據(jù)這個(gè)證書(shū)生成新證書(shū) X509CertInfo cinfo_second = (X509CertInfo)cimp2.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); */ /////////////////////////////////////////////////////////////////////////////// //end 方式一 /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //方式二、從證書(shū)庫(kù)中讀出被簽的證書(shū) start /////////////////////////////////////////////////////////////////////////////// java.security.cert.Certificate c3 = ks.getCertificate(aliasName); //從證書(shū)庫(kù)中讀出被簽證書(shū),然后生成新的證書(shū) byte[] encod3 = c3.getEncoded(); X509CertImpl cimp3 = new X509CertImpl(encod3); X509CertInfo cinfo_second = (X509CertInfo)cimp3.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); /////////////////////////////////////////////////////////////////////////// //end方式二 /////////////////////////////////////////////////////////////////////////// //設(shè)置新證書(shū)的有效期,使之為當(dāng)前向后n年有效,新證書(shū)的 //截止日期不能超過(guò)CA證書(shū)的有效日期 Date beginDate = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(beginDate); cal.add(cal.YEAR,n); Date endDate = cal.getTime(); CertificateValidity cv = new CertificateValidity(beginDate,endDate); cinfo_second.set(X509CertInfo.VALIDITY,cv); //設(shè)置新證書(shū)的序列號(hào) CertificateSerialNumber csn = new CertificateSerialNumber(sn); cinfo_second.set(X509CertInfo.SERIAL_NUMBER,csn); //設(shè)置新證書(shū)的簽發(fā)者 cinfo_second.set(X509CertInfo.ISSUER+"."+CertificateIssuerName.DN_NAME,issuer); //新的簽發(fā)者是CA的證書(shū)中讀出來(lái)的 //設(shè)置新證書(shū)的算法,指定CA簽名該證書(shū)所使用的算法為md5WithRSA AlgorithmId algorithm = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); cinfo_second.set(CertificateAlgorithmId.NAME+"."+ CertificateAlgorithmId.ALGORITHM,algorithm); //創(chuàng)建新的簽名后的證書(shū) X509CertImpl newcert = new X509CertImpl(cinfo_second); //簽名,使用CA證書(shū)的私鑰進(jìn)行簽名,簽名使用的算法為MD5WithRSA newcert.sign(privateKey,"MD5WithRSA");//這樣便得到了經(jīng)過(guò)CA簽名后的證書(shū) //把新證書(shū)存入證書(shū)庫(kù) //把新生成的證書(shū)存入一個(gè)新的證書(shū)庫(kù),也可以存入原證書(shū)庫(kù), //存入新證書(shū)庫(kù),則新證書(shū)庫(kù)中不僅包含原證書(shū)庫(kù)中的所有條目, //而且新增加了一個(gè)這次產(chǎn)生的條目。注意,這時(shí),新產(chǎn)生的簽名后的證書(shū)只 //包括公鑰和主體信息及簽名信息,不包括私鑰信息。這里給出兩種方式。 /////////////////////////////////////////////////////////////////////////// //方式一:存入新密鑰庫(kù) /////////////////////////////////////////////////////////////////////////// /* ks.setCertificateEntry(afteraliasName,newcert); FileOutputStream out = new FileOutputStream(newLib); //存入新庫(kù)signedLib,并設(shè)置新庫(kù)的庫(kù)訪問(wèn)密碼 ks.store(out,newLibPass); out.close(); */ /////////////////////////////////////////////////////////////////////////// //end 方式一 /////////////////////////////////////////////////////////////////////////// //也可以采用另外一種方式,存入原證書(shū)庫(kù)中 //存入原庫(kù)中,即在原證書(shū)庫(kù)中增加一條證書(shū),這個(gè)證書(shū)是原證書(shū)經(jīng)過(guò)簽名后的證書(shū) //這個(gè)新證書(shū)含有私鑰和私鑰密碼 /////////////////////////////////////////////////////////////////////////// //方式二,存入原密鑰庫(kù) /////////////////////////////////////////////////////////////////////////// //先在原庫(kù)中讀出被簽證書(shū)的私鑰 PrivateKey prk = (PrivateKey)ks.getKey(aliasName,namePass); java.security.cert.Certificate[] cchain = {newcert}; //存入原來(lái)的庫(kù),第二個(gè)參數(shù)為原證書(shū)的私鑰,第三個(gè)參數(shù)為新證書(shū)的私鑰密碼,第三個(gè)參數(shù)為新證書(shū) ks.setKeyEntry(afteraliasName,prk,afterNewPass,cchain); //用新密鑰替代原來(lái)的沒(méi)有簽名的證書(shū)的密碼 FileOutputStream out2 = new FileOutputStream(name); ks.store(out2,storepass);//存入原來(lái)的庫(kù)中,第二個(gè)參數(shù)為該庫(kù)的訪問(wèn)密碼 /////////////////////////////////////////////////////////////////////////// //end 方式二 /////////////////////////////////////////////////////////////////////////// } } |
運(yùn)行以上程序,即可運(yùn)用 MissionCA證書(shū)來(lái)簽發(fā)abnerCA證書(shū),運(yùn)行后在abnerCALib中增加一條別名為abnerCA_Signed的數(shù)字證書(shū),我們將它導(dǎo)出為cer文件(導(dǎo)出方法見(jiàn)前)。
至此,我們己經(jīng)用 CA的證書(shū)以我們的數(shù)字證書(shū)簽名了。在windows中,雙擊導(dǎo)出的abnerCA_Signend.cer文件,出現(xiàn)如下圖所示:
|
上圖中證書(shū)信息一欄顯示“不能驗(yàn)證該證書(shū)”,原因是因?yàn)椋覀兊倪@個(gè)數(shù)字證書(shū)的簽發(fā)者 missionCA證書(shū)沒(méi)有安裝到系統(tǒng)中。我們可以將證書(shū)庫(kù)中別名為missionCA的自簽數(shù)字證書(shū)導(dǎo)出為cer文件,然后安裝到系統(tǒng)中。再次查雙擊看此證書(shū),如下圖所示:
到此,我們己經(jīng)獲得了一個(gè)由我們自己的 CA簽名頒發(fā)的個(gè)人數(shù)字證書(shū)。并且將我們自己的CA證書(shū)安裝到系統(tǒng)中成為系統(tǒng)信任的根證書(shū)。于是,以后只要是由我們的這個(gè)CA證書(shū)簽名頒發(fā)的數(shù)字證書(shū)都會(huì)受到系統(tǒng)的信任。
?
?
四、利用數(shù)字證書(shū)給 applet簽名
現(xiàn)在假設(shè)我們公司給 xx公司做一個(gè)項(xiàng)目,這個(gè)項(xiàng)目中需要用到applet,且這些applet需要特權(quán)以實(shí)現(xiàn)一些特殊的功能(如讀出客戶端用戶系統(tǒng)中C:/winNT /system.ini文件中的內(nèi)容并顯示)。那么我們可以頒發(fā)一個(gè)數(shù)字證書(shū),并給這個(gè)數(shù)字證書(shū)簽名,然后用簽名后的這個(gè)數(shù)字證書(shū)來(lái)簽名我們的 applet,使客戶信任。具體過(guò)程如下:
1、生成一個(gè)用于此項(xiàng)目簽名 applet 的數(shù)字證書(shū),別名定為: mission_water
生成一個(gè)用于此項(xiàng)目簽名的數(shù)字證書(shū)如下:
keytool –genkey –dname “CN=美森軟件-水公司項(xiàng)目,OU=美森系統(tǒng)軟件有限公司,O=美森系統(tǒng)軟件有限公司,L=成都市,ST=四川省,C=中國(guó)” –alias Mission_Water –keyalg RSA –keysize 1024 –keystore abnerCALib –keypass 200100 –storepass 100200 –validity 3650
2、用我們的 CA(missinCA) 來(lái)簽發(fā)這個(gè)數(shù)字證書(shū)
然后,運(yùn)行我們?cè)谇懊娴谌?jié)中給定的程序,注意:運(yùn)行此程序前,請(qǐng)修改以下參數(shù):
String cerFileName = "Mission_Water.cer";
String aliasName = "Mission_Water"; String afteraliasName = "Mission_Water_Signed"; |
然后運(yùn)行,程序會(huì)在 abnerCALib證書(shū)庫(kù)中產(chǎn)生一個(gè)別名為:Mission_Water_Signed的數(shù)字證書(shū),這個(gè)證書(shū)是經(jīng)過(guò)我們的CA(MissionCA)簽發(fā)的。
下面,我們用以下指令導(dǎo)出這個(gè)證書(shū):
keytool -export -alias Mission_Water_Signed -file Mission_Water_Signed.cer -keystore abnerCALib -rfc |
會(huì)生成一個(gè) Mission_Water_Signed.cer 文件。
3、用簽發(fā)后的數(shù)字證書(shū)來(lái)簽名我們的 applet
我們現(xiàn)在來(lái)做一個(gè)簡(jiǎn)單的 applet,它的代碼如下:
package com.applet;
import java.awt.*; import java.awt.event.*; import java.applet.*; import javax.swing.*; import java.io.*; public class ShowFileApplet extends JApplet { private boolean isStandalone = false; private String content = "文件的內(nèi)容是:"; //自定義的提示信息 private String fileName = "C://WINNT//system.ini";//讀出這個(gè)文件的內(nèi)容 private TextArea ta = new TextArea(10,80);//自定義的輸出框 public String getParameter(String key, String def) { return isStandalone ? System.getProperty(key, def) : (getParameter(key) != null ? getParameter(key) : def); } public ShowFileApplet() { } public void init() { try { jbInit(); myInit();//自己定義的方法 } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { this.setSize(new Dimension(400,300)); } /** * 自定義的初始化方法,讀入系統(tǒng)中的一個(gè)文件的內(nèi)容并保存起來(lái),然后,增加一個(gè) * 可視化的輸出框 */ private void myInit(){ String s; BufferedReader in; try { in = new BufferedReader(new FileReader(fileName)); while ( (s = in.readLine()) != null) { content +=s + "/n"; } }catch (IOException ex) { ex.printStackTrace(); } System.out.println(content); ta.setText(content); getContentPane().add(ta); } /* *重載的方法,輸出內(nèi)容 **/ public void paint(Graphics g){ ta.setText(content); } public String getAppletInfo() { return "Applet Information"; } public String[][] getParameterInfo() { return null; } //static initializer for setting look & feel static { try { } catch(Exception e) { } } } |
好了,這個(gè) applet寫(xiě)好了,下面我們來(lái)把這個(gè)applet編譯打包成jar文件。
編譯此 applet文件,會(huì)在當(dāng)前目錄(當(dāng)前目錄為classes目錄)下生成一個(gè)com/applet的目錄結(jié)構(gòu),在applet目錄下有一個(gè)ShowFileApplet.class,進(jìn)入當(dāng)前目錄,執(zhí)行:
jar cvf myapplet.jar com/applet/*.* |
于是在當(dāng)前目錄下產(chǎn)生一個(gè) myapplet.jar文件。
然后再在當(dāng)前目錄(當(dāng)前目錄為 classes目錄)下新建一個(gè)applet目錄,專(zhuān)門(mén)存放applet的jar文件,把前面生成的數(shù)字證書(shū)庫(kù)abnerCALib這個(gè)文件也copy到 applet目錄下面來(lái),同時(shí)把剛才生成的myapplet.jar文件也移到applet目錄下面來(lái)。然后進(jìn)入該目錄執(zhí)行:
jarsigner -keystore abnerCALib myapplet.jar Mission_Water_Signed
Enter Passphrase for keystore: 100200 Enter key password for Mission_Water_Signed: 200100 |
即用 Mission_Water_Signed這個(gè)我們的CA簽發(fā)的數(shù)字證書(shū)給這個(gè)applet簽名了。
4、運(yùn)行我們的 applet
我們來(lái)寫(xiě)一個(gè) html文件來(lái)運(yùn)行這個(gè)簽名后的applet,內(nèi)容如下:
<!-- ShowFileApplet.html -->
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB2312"> <title>HTMLTestPage</title> </head><body> applet將會(huì)顯示,如果你的瀏覽器支持Java<br> <applet archive = "./applet/myapplet.jar" code = "com.applet.ShowFileApplet.class" name = "TestApplet" width = "400" height = "300" hspace = "0" vspace = "0" align = "middle" > </applet> </body> </html> |
這個(gè) HTML文件可以運(yùn)行applet,但如果瀏覽器不支持Java,即沒(méi)有安裝JRE,它不會(huì)提示用戶去下載安裝。我們可以用Java自帶的 htmlconverter工具轉(zhuǎn)換一下這個(gè)HTML文件,轉(zhuǎn)換后的文件可以在支持JAVA2的瀏覽器中(不管該瀏覽器是否設(shè)置了使用java2運(yùn)行 applet,它都會(huì)在Java2環(huán)境中運(yùn)行applet,如果瀏覽器不支持Java2,則會(huì)自動(dòng)下載所需的文件。
在 DOS方式下運(yùn)行htmlconverter,彈出如下圖所示畫(huà)框,按圖中所示選擇剛才的那個(gè)HTML文件,如下圖所示:
|
點(diǎn)“轉(zhuǎn)換”,將會(huì)在當(dāng)前目錄下生成一個(gè) HTML文件,并把原來(lái)的HTML文件備份了。
生成后的 HTML文件內(nèi)容如下所示:
<!-- ShowFileApplet.html -->
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GB2312"> <title>HTMLTestPage</title> </head> <body> applet將會(huì)顯示,如果你的瀏覽器支持Java<br> <OBJECT classid = "clsid:CAFEEFAC-0014-0001-0001-ABCDEFFEDCBA" codebase = "http://java.sun.com/products/plugin/autodl/jinstall-1_4_1_01-windows-i586.cab#Version=1,4,1,1" WIDTH = "400" HEIGHT = "300" NAME = "TestApplet" ALIGN = "middle" VSPACE = "0" HSPACE = "0" > <PARAM NAME = CODE VALUE = "com.applet.ShowFileApplet.class" > <PARAM NAME = ARCHIVE VALUE = "./applet/myapplet.jar" > <PARAM NAME = NAME VALUE = "TestApplet" > <PARAM NAME = "type" VALUE = "application/x-java-applet;jpi-version=1.4.1_01"> <PARAM NAME = "scriptable" VALUE = "false"> <COMMENT> <EMBED type = "application/x-java-applet;jpi-version=1.4.1_01" CODE = "com.applet.ShowFileApplet.class" ARCHIVE = "./applet/myapplet.jar" NAME = "TestApplet" WIDTH = "400" HEIGHT = "300" ALIGN = "middle" VSPACE = "0" HSPACE = "0" scriptable = false pluginspage = "http://java.sun.com/products/plugin/index.html#download"> <NOEMBED> </NOEMBED> </EMBED> </COMMENT> </OBJECT> </body> </html> |
雙擊打開(kāi)運(yùn)行這個(gè)文件或把這個(gè)文件及 applet目錄發(fā)布到WEB Server中去,可以訪問(wèn)運(yùn)行這個(gè)applet。運(yùn)行時(shí),彈出如下圖所示對(duì)話框:
|
如果此時(shí)你點(diǎn)“是”,則在這次會(huì)話過(guò)程中,此 applet具有訪問(wèn)本地文件系統(tǒng)的權(quán)限,但下次運(yùn)行時(shí)還要提示此信息。如果你點(diǎn)“總是有效”則以后每次訪問(wèn)此類(lèi)含有由 Mission_Water_Signed數(shù)字證書(shū)簽發(fā)的applet頁(yè)面,都不會(huì)再?gòu)棾龃诉x擇框。因?yàn)椋耗氵x擇了總是有效,這樣,Java會(huì)在Java Plug-in中記錄這個(gè)信任的數(shù)字證書(shū),除非你把Java Plug-in中記錄的這個(gè)信任證書(shū)刪除。在win2k中,可以在“控制面版”->Java Plug-in中看到。如下圖所示:
|
點(diǎn)選擇“總是有效”后,該程序運(yùn)行的結(jié)果如下圖所示:
|
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
