JAVA安全體系結(jié)構(gòu)分析

? 下圖顯示了 JAVA 安全體系結(jié)構(gòu)的標(biāo)準(zhǔn)組件。在圖的下半部分,是 JAVA2 安全體系結(jié)構(gòu)的核心和 JAVA 加密體系結(jié)構(gòu)( JCA Java Cryptography Architecture ),兩者構(gòu)成 JAVA2 平臺(tái)所帶的 JAVA2 安全平臺(tái)。在圖的上半部分,是獨(dú)立于 JAVA2 平臺(tái)而又與 JAVA2 平臺(tái)的不同方面相關(guān)的 JAVA 安全擴(kuò)展。 ?

???????? 從上圖可以看出,Java平臺(tái)為安全和加密服務(wù)提供了兩組API:JCA和JCE。?JCA?(Java?Cryptography?Architecture)提供基本的加密框架,如證書、數(shù)字簽名、消息摘要和密鑰對(duì)產(chǎn)生器;?JCE在JCA的基礎(chǔ)上作了擴(kuò)展,包括加密算法、密鑰交換、密鑰產(chǎn)生和消息鑒別服務(wù)等接口。

JCA與JCE? ?? ?
??? JCA/JCE并不執(zhí)行各種算法,它們只是連接應(yīng)用和實(shí)際算法實(shí)現(xiàn)程序的一組接口。軟件開發(fā)商根據(jù)JCE接口,將各種算法實(shí)現(xiàn)后,打包成一個(gè)Provider,可以動(dòng)態(tài)地加到Java運(yùn)行環(huán)境中。由于美國(guó)出口控制規(guī)定,JCA?是可出口的(JCA和一個(gè)Sun的默認(rèn)實(shí)現(xiàn)包括在Java2中),但是JCE對(duì)部分國(guó)家是限制出口的。因此,要實(shí)現(xiàn)一個(gè)完整的安全結(jié)構(gòu),就需要一個(gè)或多個(gè)第三方廠商提供的JCE產(chǎn)品,稱為安全供應(yīng)者。
BouncyCastle JCE 就是其中的一個(gè) 安全供應(yīng)者。

???? 安全供應(yīng)者是承擔(dān)特定安全機(jī)制的實(shí)現(xiàn)的第三方。有些供應(yīng)者是完全免費(fèi)的,而另一些供應(yīng)者則需要付費(fèi)。提供安全供應(yīng)者的公司有IBM,Bouncy Castle等,Sun提供了如何實(shí)現(xiàn)開發(fā)人員自己供應(yīng)者的細(xì)節(jié)。Bouncy Castle提供了可以在J2ME/J2EE/J2SE平臺(tái)得到支持的API,而且他的API是免費(fèi)的。詳情參見:http://www.bouncycastle.org/

安裝BouncyCastleJCE

安裝 BouncyCastle JCE應(yīng)該按照如下步驟:

1) 下載提供者

如上, BouncyCastle 可以從 www.bouncycastle.org 下載,下載后請(qǐng)將它解

壓縮。

2) JAR 文件拷貝到適當(dāng)位置

如果想讓 JCE 類可以被所有的 JAVA 應(yīng)用程序使用,我們需要把 JAR 文件安裝為一個(gè)擴(kuò)展。 BouncyCastle 提供者沒有用于安裝為擴(kuò)展的 JAR 文件,但它容易構(gòu)建。首先將下載的文件展開到 JDK classes 目錄下,然后運(yùn)行下面的命令將這些文件打包:

c:\> jar cvf bouncycastle.jar javax org

Windows 中, Java 通常安裝在兩個(gè)目錄下。一個(gè)目錄用于開發(fā),包括所有的 JDK 工具,另一個(gè)僅僅是運(yùn)行環(huán)境。 JDK 本身通常位于 c:\jdk1.3 這樣的目 錄下,而JDK運(yùn)行環(huán)境通常位于c:\Program files\JavaSoft\JRE1.3這樣的目錄下,它們都有庫(kù)的擴(kuò)展目錄。分別是:c:\jdk1.3.1\lib\ext和c:\Program files\JavaSoft\JRE\1.3\lib\ext,把所要的JAR文件放于對(duì)應(yīng)的目錄下。

3) 配置安全屬性文件

安全屬性文件java.security位于和\lib\ext平行的另一個(gè)目錄\lib\security下,它定義了當(dāng)前可以使用的加密提供者。如您看到下面的語句:

security.provider.1=sun.security.provider.Sun

security.provider.2=com.sun.rsajca.Provider

它表明本虛擬機(jī)有兩個(gè)加密提供者以及他們的優(yōu)先級(jí)和訪問時(shí)使用的名稱。當(dāng)需要用到一個(gè)加密算法時(shí),虛擬機(jī)會(huì)依次訪問這里列出的提供者,尋找想要的算法,并按這里的優(yōu)先級(jí)順序使用第一個(gè)找到的算法。

我們應(yīng)該在文件中插入如下行,把新的提供者加入進(jìn)去:

security.provider.3=org.bouncycastle.jce.provider.BouncyCastleProvider

當(dāng)然了,你也可以將前面的加密提供者配置刪除,這樣就不能使用jdk默認(rèn)的加密提供者了。

4)測(cè)試安裝好的程序

我們可以自己寫一個(gè)測(cè)試程序(用JCE包的API),來判斷我們的安裝是否成功。

安裝成功了,讓我們進(jìn)入用 BouncyCastle JCE 實(shí)現(xiàn)安全功能的有趣天地吧 !

對(duì)稱密鑰的產(chǎn)生

??? 對(duì)稱加密采用了對(duì)稱密碼編碼技術(shù),它的特點(diǎn)是文件加密和解密使用相同的密鑰,即加密密鑰也可以用作解密密鑰。這種方法在密碼學(xué)中叫做對(duì)稱加密算法,對(duì)稱加密算法使用起來簡(jiǎn)單快捷,密鑰較短,且破譯困難,除了數(shù)據(jù)加密標(biāo)準(zhǔn)(DES),另一個(gè)對(duì)稱密鑰加密系統(tǒng)系統(tǒng)是國(guó)際數(shù)據(jù)加密算法(IDEA),它比DES的加密性好,而且對(duì)計(jì)算機(jī)功能要求也沒有那么高。IDEA加密標(biāo)準(zhǔn)由PGP(Pretty Good Privacy)系統(tǒng)使用。
//首先要import javax.crypto.*;
SecretKey key=null;
???? try
{
//指定算法,這里為DES;如果想用Blowfish算法,則用 getInstance("Blowfish")
//BouncyCastle基本上支持所有通用標(biāo)準(zhǔn)算法
KeyGenerator keygen=KeyGenerator.getInstance("DES");
//指定密鑰長(zhǎng)度,長(zhǎng)度越高,加密強(qiáng)度越大
keygen.init(56);
//產(chǎn)生密鑰
key=keygen.generateKey();
//構(gòu)造輸出文件,這里的目錄是動(dòng)態(tài)的,根據(jù)用戶名稱來構(gòu)造目錄
???? ObjectOutputStream keyFile=new ObjectOutputStream(new FileOutputStream
???????????? ("c:\\安全文件\\"+misClass.username+"\\對(duì)稱\\對(duì)稱密鑰\\yhb.des"));
???? keyFile.writeObject(key);
???? keyFile.close();
???? }
???? catch(NoSuchAlgorithmException e5)
{
// generateKey()拋出的異常
???? System.out.print("no such algorithm");
???? System.exit(0);
???? }
???? catch(IOException e4)
???? {
???? System.out.print("error when generate the des key");
???? System.exit(0);
}
非對(duì)稱密鑰的產(chǎn)生
1976年,美國(guó)學(xué)者Dime和Henman為解決信息公開傳送和密鑰管理問題,提出一種新的密鑰交換協(xié)議,允許在不安全的媒體上的通訊雙方交換信息,安全地達(dá)成一致的密鑰,這就是“公開密鑰系統(tǒng)”。相對(duì)于“對(duì)稱加密算法”這種方法也叫做“非對(duì)稱加密算法”。
與對(duì)稱加密算法不同,非對(duì)稱加密算法需要兩個(gè)密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對(duì),如果用公開密鑰對(duì)數(shù)據(jù)進(jìn)行加密,只有用對(duì)應(yīng)的私有密鑰才能解密;如果用私有密鑰對(duì)數(shù)據(jù)進(jìn)行加密,那么只有用對(duì)應(yīng)的公開密鑰才能解密。因?yàn)榧用芎徒饷苁褂玫氖莾蓚€(gè)不同的密鑰,所以這種算法叫作非對(duì)稱加密算法。
//密鑰對(duì)
??? KeyPair keys=null;
??? try
{
//指定算法
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
//指定長(zhǎng)度
??? kpg.initialize(1024);
keys=kpg.genKeyPair();
//公鑰
byte[] key1=keys.getPublic().getEncoded();
//私鑰
??? byte[] key2=keys.getPrivate().getEncoded();
?
??? //構(gòu)造公鑰文件并寫入公鑰
FileOutputStream keyFile1=new FileOutputStream
?????? ("c:\\安全文件\\"+misClass.username+"\\非對(duì)稱\\本人公私鑰\\yhb.public");
???? keyFile1.write(key1);
???? keyFile1.close();
??? //構(gòu)造私鑰文件并寫入私鑰
??? keyFile1=new FileOutputStream
???? ("c:\\安全文件\\"+misClass.username+"\\非對(duì)稱\\本人公私鑰\\yhb.private");
??? keyFile1.write(key2);
??? keyFile1.close();
??? }
??? catch(NoSuchAlgorithmException e8)
{
//算法異常
???? System.out.print("no such algorithm");
???? System.exit(0);
???? }
???? catch(IOException e9)
???? {
???? System.out.print("error when generate the rsa key");
???? System.exit(0);
???? }

對(duì)稱加密的實(shí)現(xiàn)
?? 加密可提高終端和網(wǎng)絡(luò)通訊的物理安全,有三種方法加密傳輸數(shù)據(jù):?
*?鏈接加密:在網(wǎng)絡(luò)節(jié)點(diǎn)間加密,在節(jié)點(diǎn)間傳輸加密,傳送到節(jié)點(diǎn)后解密,不同節(jié)點(diǎn)對(duì)間用不同密碼.?
*?節(jié)點(diǎn)加密:與鏈接加密類似,不同的只是當(dāng)數(shù)據(jù)在節(jié)點(diǎn)間傳送時(shí),不用明碼格式傳送,而是用特殊??的加密硬件進(jìn)行解密和重加密,這種專用硬件通常旋轉(zhuǎn)在安全保險(xiǎn)箱中.?
*?首尾加密:對(duì)進(jìn)入網(wǎng)絡(luò)的數(shù)據(jù)加密,然后待數(shù)據(jù)從網(wǎng)絡(luò)傳送出后再進(jìn)行解密.網(wǎng)絡(luò)本身并不會(huì)知?道正在傳送的數(shù)據(jù)是加密數(shù)據(jù).這一方法的優(yōu)點(diǎn)是,網(wǎng)絡(luò)上的每個(gè)用戶(通常是每個(gè)機(jī)器的一個(gè)??用戶)可有不同的加密關(guān)鍵詞,并且網(wǎng)絡(luò)本身不需增添任何專門的加密設(shè)備.缺點(diǎn)是每個(gè)系統(tǒng)必?須有一個(gè)加密設(shè)備和相應(yīng)的軟件(管理加密關(guān)鍵詞)或者每個(gè)系統(tǒng)必須自己完成加密工作(當(dāng)數(shù)?據(jù)傳輸率是按兆位/秒的單位計(jì)算時(shí),加密任務(wù)的計(jì)算量是很大的)
? 本文采用首尾加密,代碼如下:
//從密鑰文件中讀密鑰
?? SecretKey key=null;
?? try
?? {
?? //從密鑰文件讀取密鑰
ObjectInputStream keyFile=new ObjectInputStream(
??? ?new FileInputStream("c:\\安全文件\\"+misClass.username+"\\對(duì)稱\\對(duì)稱密鑰\\yhb.des"));
??? key=(SecretKey)keyFile.readObject();
??? keyFile.close();
??? }
??? catch(FileNotFoundException ey1)
??? {
??? System.out.println("Error when read keyFile");
??? System.exit(0);
??? }
??? catch(Exception ey2)
??? {
??? System.out.println("error when read the keyFile");
??? System.exit(0);
??? }
??? //用key產(chǎn)生Cipher
??? Cipher cipher=null;
??? try
{
//加密要用Cipher來實(shí)現(xiàn)
cipher=Cipher.getInstance("DES");
//設(shè)置加密模式
???? cipher.init(Cipher.ENCRYPT_MODE,key);
???? }catch(Exception ey3)
???? {
???? System.out.println("Error when create the cipher");
???? System.exit(0);
???? }
???? //從對(duì)話框中取得要加密的文件
???? File file=new File(dirstring,string1);
???? String filename=file.getName();
???? //讀入并加密文件
???? try
{
//輸入流
BufferedInputStream in=new BufferedInputStream(new FileInputStream(file));
//輸出流
????? CipherOutputStream out=new CipherOutputStream(new BufferedOutputStream(
??????????? new FileOutputStream("c:\\安全文件\\文件\\"+filename+".yhb")),cipher);
?? ???int i;
??????? do{
??????? i=in.read();
??????? if(i!=-1) out.write(i);
???????? }while(i!=-1);
????? in.close();
????? out.close();
????? }
????? catch(Exception ey5)
????? {
????? System.out.println("Error when encrypt the file");
????? System.exit(0);
????? }
?


對(duì)稱解密的實(shí)現(xiàn)

對(duì)稱加密/解密算法在電子商務(wù)交易過程中存在幾個(gè)問題:

(1)?????? 要求提供一條安全的渠道使通訊雙方在首次通訊時(shí)協(xié)商一個(gè)共同的密鑰。直接的面對(duì)面協(xié)商可能是不現(xiàn)實(shí)而且難于實(shí)施的,所以雙方可能需要借助于郵件和電話等其它相對(duì)不夠安全的手段來進(jìn)行協(xié)商;

(2)?????? 密鑰的數(shù)目難于管理。因?yàn)閷?duì)于每一個(gè)合作者都需要使用不同的密鑰,很難適應(yīng)開放社會(huì)中大量的信息交流;

(3)?????? 對(duì)稱加密算法一般不能提供信息完整性的鑒別。它無法驗(yàn)證發(fā)送者和接受者的身份;

對(duì)稱密鑰的管理和分發(fā)工作是一件具有潛在危險(xiǎn)的和煩瑣的過程。對(duì)稱加密是基于共同保守秘密來實(shí)現(xiàn)的,采用對(duì)稱加密技術(shù)的貿(mào)易雙方必須保證采用的是相同的密鑰,保證彼此密鑰的交換是安全可靠的,同時(shí)還要設(shè)定防止密鑰泄密和更改密鑰的程序。

對(duì)稱解密的代碼實(shí)現(xiàn)如下:

//從密鑰文件中讀密鑰

?? SecretKey key=null;

?? try

?? {ObjectInputStream keyFile=new ObjectInputStream(

???? new FileInputStream("c:\\安全文件\\"+misClass.username+"\\對(duì)稱\\對(duì)稱密鑰\\yhb.des"));

??? key=(SecretKey)keyFile.readObject();

??? keyFile.close();

??? }

??? catch(FileNotFoundException ey1)

??? {

??? System.out.println("Error when read keyFile");

??? System.exit(0);

??? }

??? catch(Exception ey2)

??? {

??? System.out.println("error when read the keyFile");

??? System.exit(0);

??? }

?? //key產(chǎn)生Cipher

??? Cipher cipher=null;

??? try

{

//設(shè)置算法,應(yīng)該與加密時(shí)的設(shè)置一樣

cipher=Cipher.getInstance("DES");

//設(shè)置解密模式

???? cipher.init(Cipher.DECRYPT_MODE,key);

???? }catch(Exception ey3)

???? {

???? System.out.println("Error when create the cipher");

???? System.exit(0);

???? }

???? //從對(duì)話框中取得要解密的文件并解密

???? File file=new File(dirstring,string1);

???? String filename=file.getName();

??? try

{

//輸出流,請(qǐng)注意文件名稱的獲取

BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream(

????????? "c:\\安全文件\\文件\\"+filename.substring(0,filename.length()-4)));

???? //輸入流

????? CipherInputStream in=new CipherInputStream(new BufferedInputStream(

??????????? new FileInputStream(file)),cipher);

??? int thebyte=0;

??? while((thebyte=in.read())!=-1)

??? {

??? out.write(thebyte);

??? }

????? in.close();

????? out.close();

????? }

????? catch(Exception ey5)

????? {

????? System.out.println("Error when encrypt the file");

????? System.exit(0);

????? }


簽名的實(shí)現(xiàn)過程

1)讀取自己的私鑰

??? 對(duì)于自己的私鑰文件,要用File類來聲明。讀取時(shí),將用FileInputStream格式來作為輸入流。而讀出的密鑰是字節(jié)數(shù)組,所以應(yīng)該將讀出的密鑰用ByteArrayOutStream來保存,再用toByteArray格式來將它轉(zhuǎn)化為字節(jié)數(shù)組。

生成簽名要使用自己的私鑰,而私鑰使用PKCS8#編碼。所以我們還要將字節(jié)數(shù)組轉(zhuǎn)化為PKCS8#編碼形式。實(shí)現(xiàn)方法如下:

PKCS8EncodedKeySpec keyspec=new PKCS8EncodedKeySpec(keybytes);

KeyFactory keyfactory=KeyFactory.getInstance("RSA");

syprivatekey=keyfactory.generatePrivate(keyspec);

其中keybytes是從原文中讀出的字節(jié)數(shù)組形式的密鑰。用KeyFactory對(duì)象的實(shí)例化方法來指定算法,并用generatePrivate方法產(chǎn)生PKCS8#編碼的私鑰。

2)從對(duì)話框中取得要簽名的文件

該步驟的實(shí)現(xiàn)比較簡(jiǎn)單,不做過多說明。

3)將文件內(nèi)容讀取為字節(jié)數(shù)組格式

因?yàn)楹灻麜r(shí)Signature類的Update()方法的參數(shù)是字節(jié)數(shù)組形式,所以要求

先將原文讀為字節(jié)數(shù)組。并且,在此處可以獲得原文的內(nèi)容長(zhǎng)度。

4)生成簽名

按照前面的描述,先用Signature類的getInstance()方法指定MD5WithRSA

算法,然后用前面得到的私鑰作為參數(shù)調(diào)用initSign()方法來初始化,最后用原文作為參數(shù)調(diào)用update()方法來傳送數(shù)據(jù),用字節(jié)數(shù)組形式的私鑰作為參數(shù)調(diào)用Sign()方法來產(chǎn)生簽名。

將生成的簽名按照前面設(shè)計(jì)的文件格式寫入文件流中,就完成了簽名的全部工作。簽名的實(shí)現(xiàn)過程可用下面的圖來表示:

?????圖 數(shù)字簽名過程

代碼實(shí)現(xiàn)如下:

//讀取私鑰

???? PrivateKey syprivatekey=null;

????? File syfile=new File("c:\\安全文件\\"+misClass.username+"\\非對(duì)稱\\本人公私鑰\\yhb.private");

????? try

???? {

???? FileInputStream fis=new FileInputStream(syfile);

???? ByteArrayOutputStream baos=new ByteArrayOutputStream();

?

???? int thebyte=0;

????? while((thebyte=fis.read())!=-1)

????? {baos.write(thebyte);

????? }

????? fis.close();

????? byte[] keybytes=baos.toByteArray();

????? baos.close();

?

????? PKCS8EncodedKeySpec keyspec=new PKCS8EncodedKeySpec(keybytes);

????? KeyFactory keyfactory=KeyFactory.getInstance("RSA");

????? syprivatekey=keyfactory.generatePrivate(keyspec);

??? }

??? catch(Exception e9)

???? {

???? System.out.print("error when read the rsa private key");

???? System.exit(0);

???? }

???? //從對(duì)話框中取得要簽名的文件

???? File file=new File(dirstring1,string1);

???? String filename=file.getName();

???? //首先將文件讀為byte[]對(duì)象

??? int len=(int)file.length();

??? if(len>100000000)

??? {System.out.println("the file length is too long!");

??? System.exit(0);

??? }

??? byte[] inbuf=new byte[len];

??? try{

??? FileInputStream instream=new FileInputStream(file);

??? int inbytes=instream.available();

??? //inbuf[]=new byte[inbytes];

??? int bytesread=instream.read(inbuf,0,inbytes);

??? instream.close();

??? //System.out.println(inbuf);

??? }

??? catch(Exception eq2)

??? {

??? System.out.println("error when change the file to byte[]");

??? System.exit(0);

??? }

??? //簽名的具體過程

??? try{

??? //byte[] signaturebytes=new byte[150];

??? Signature sig=Signature.getInstance("MD5WithRSA");

??? sig.initSign(syprivatekey);

??? sig.update(inbuf);

??? byte[] signaturebytes=sig.sign();

?????????? //寫入對(duì)象流中

?? DataOutputStream outfile=new DataOutputStream(new FileOutputStream(

????????????????? "c:\\安全文件\\文件\\"+filename+".yhb3"));

??? outfile.writeInt(signaturebytes.length);

??? outfile.write(signaturebytes);

??? outfile.writeInt(len);

??? outfile.write(inbuf);

??? outfile.close();

??? }

??? catch(Exception eh3)

??? {

??? System.out.println("error when generate the outfile");

??? System.exit(0);

??? }

?

?