JAVA安全體系結構分析

? 下圖顯示了 JAVA 安全體系結構的標準組件。在圖的下半部分,是 JAVA2 安全體系結構的核心和 JAVA 加密體系結構( JCA Java Cryptography Architecture ),兩者構成 JAVA2 平臺所帶的 JAVA2 安全平臺。在圖的上半部分,是獨立于 JAVA2 平臺而又與 JAVA2 平臺的不同方面相關的 JAVA 安全擴展。 ?

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

JCA與JCE? ?? ?
??? JCA/JCE并不執行各種算法,它們只是連接應用和實際算法實現程序的一組接口。軟件開發商根據JCE接口,將各種算法實現后,打包成一個Provider,可以動態地加到Java運行環境中。由于美國出口控制規定,JCA?是可出口的(JCA和一個Sun的默認實現包括在Java2中),但是JCE對部分國家是限制出口的。因此,要實現一個完整的安全結構,就需要一個或多個第三方廠商提供的JCE產品,稱為安全供應者。
BouncyCastle JCE 就是其中的一個 安全供應者。

???? 安全供應者是承擔特定安全機制的實現的第三方。有些供應者是完全免費的,而另一些供應者則需要付費。提供安全供應者的公司有IBM,Bouncy Castle等,Sun提供了如何實現開發人員自己供應者的細節。Bouncy Castle提供了可以在J2ME/J2EE/J2SE平臺得到支持的API,而且他的API是免費的。詳情參見:http://www.bouncycastle.org/

安裝BouncyCastleJCE

安裝 BouncyCastle JCE應該按照如下步驟:

1) 下載提供者

如上, BouncyCastle 可以從 www.bouncycastle.org 下載,下載后請將它解

壓縮。

2) JAR 文件拷貝到適當位置

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

c:\> jar cvf bouncycastle.jar javax org

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

3) 配置安全屬性文件

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

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

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

它表明本虛擬機有兩個加密提供者以及他們的優先級和訪問時使用的名稱。當需要用到一個加密算法時,虛擬機會依次訪問這里列出的提供者,尋找想要的算法,并按這里的優先級順序使用第一個找到的算法。

我們應該在文件中插入如下行,把新的提供者加入進去:

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

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

4)測試安裝好的程序

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

安裝成功了,讓我們進入用 BouncyCastle JCE 實現安全功能的有趣天地吧 !

對稱密鑰的產生

??? 對稱加密采用了對稱密碼編碼技術,它的特點是文件加密和解密使用相同的密鑰,即加密密鑰也可以用作解密密鑰。這種方法在密碼學中叫做對稱加密算法,對稱加密算法使用起來簡單快捷,密鑰較短,且破譯困難,除了數據加密標準(DES),另一個對稱密鑰加密系統系統是國際數據加密算法(IDEA),它比DES的加密性好,而且對計算機功能要求也沒有那么高。IDEA加密標準由PGP(Pretty Good Privacy)系統使用。
//首先要import javax.crypto.*;
SecretKey key=null;
???? try
{
//指定算法,這里為DES;如果想用Blowfish算法,則用 getInstance("Blowfish")
//BouncyCastle基本上支持所有通用標準算法
KeyGenerator keygen=KeyGenerator.getInstance("DES");
//指定密鑰長度,長度越高,加密強度越大
keygen.init(56);
//產生密鑰
key=keygen.generateKey();
//構造輸出文件,這里的目錄是動態的,根據用戶名稱來構造目錄
???? ObjectOutputStream keyFile=new ObjectOutputStream(new FileOutputStream
???????????? ("c:\\安全文件\\"+misClass.username+"\\對稱\\對稱密鑰\\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);
}
非對稱密鑰的產生
1976年,美國學者Dime和Henman為解決信息公開傳送和密鑰管理問題,提出一種新的密鑰交換協議,允許在不安全的媒體上的通訊雙方交換信息,安全地達成一致的密鑰,這就是“公開密鑰系統”。相對于“對稱加密算法”這種方法也叫做“非對稱加密算法”。
與對稱加密算法不同,非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那么只有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。
//密鑰對
??? KeyPair keys=null;
??? try
{
//指定算法
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
//指定長度
??? kpg.initialize(1024);
keys=kpg.genKeyPair();
//公鑰
byte[] key1=keys.getPublic().getEncoded();
//私鑰
??? byte[] key2=keys.getPrivate().getEncoded();
?
??? //構造公鑰文件并寫入公鑰
FileOutputStream keyFile1=new FileOutputStream
?????? ("c:\\安全文件\\"+misClass.username+"\\非對稱\\本人公私鑰\\yhb.public");
???? keyFile1.write(key1);
???? keyFile1.close();
??? //構造私鑰文件并寫入私鑰
??? keyFile1=new FileOutputStream
???? ("c:\\安全文件\\"+misClass.username+"\\非對稱\\本人公私鑰\\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);
???? }

對稱加密的實現
?? 加密可提高終端和網絡通訊的物理安全,有三種方法加密傳輸數據:?
*?鏈接加密:在網絡節點間加密,在節點間傳輸加密,傳送到節點后解密,不同節點對間用不同密碼.?
*?節點加密:與鏈接加密類似,不同的只是當數據在節點間傳送時,不用明碼格式傳送,而是用特殊??的加密硬件進行解密和重加密,這種專用硬件通常旋轉在安全保險箱中.?
*?首尾加密:對進入網絡的數據加密,然后待數據從網絡傳送出后再進行解密.網絡本身并不會知?道正在傳送的數據是加密數據.這一方法的優點是,網絡上的每個用戶(通常是每個機器的一個??用戶)可有不同的加密關鍵詞,并且網絡本身不需增添任何專門的加密設備.缺點是每個系統必?須有一個加密設備和相應的軟件(管理加密關鍵詞)或者每個系統必須自己完成加密工作(當數?據傳輸率是按兆位/秒的單位計算時,加密任務的計算量是很大的)
? 本文采用首尾加密,代碼如下:
//從密鑰文件中讀密鑰
?? SecretKey key=null;
?? try
?? {
?? //從密鑰文件讀取密鑰
ObjectInputStream keyFile=new ObjectInputStream(
??? ?new FileInputStream("c:\\安全文件\\"+misClass.username+"\\對稱\\對稱密鑰\\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產生Cipher
??? Cipher cipher=null;
??? try
{
//加密要用Cipher來實現
cipher=Cipher.getInstance("DES");
//設置加密模式
???? cipher.init(Cipher.ENCRYPT_MODE,key);
???? }catch(Exception ey3)
???? {
???? System.out.println("Error when create the cipher");
???? System.exit(0);
???? }
???? //從對話框中取得要加密的文件
???? 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);
????? }
?


對稱解密的實現

對稱加密/解密算法在電子商務交易過程中存在幾個問題:

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

(2)?????? 密鑰的數目難于管理。因為對于每一個合作者都需要使用不同的密鑰,很難適應開放社會中大量的信息交流;

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

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

對稱解密的代碼實現如下:

//從密鑰文件中讀密鑰

?? SecretKey key=null;

?? try

?? {ObjectInputStream keyFile=new ObjectInputStream(

???? new FileInputStream("c:\\安全文件\\"+misClass.username+"\\對稱\\對稱密鑰\\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產生Cipher

??? Cipher cipher=null;

??? try

{

//設置算法,應該與加密時的設置一樣

cipher=Cipher.getInstance("DES");

//設置解密模式

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

???? }catch(Exception ey3)

???? {

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

???? System.exit(0);

???? }

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

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

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

??? try

{

//輸出流,請注意文件名稱的獲取

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);

????? }


簽名的實現過程

1)讀取自己的私鑰

??? 對于自己的私鑰文件,要用File類來聲明。讀取時,將用FileInputStream格式來作為輸入流。而讀出的密鑰是字節數組,所以應該將讀出的密鑰用ByteArrayOutStream來保存,再用toByteArray格式來將它轉化為字節數組。

生成簽名要使用自己的私鑰,而私鑰使用PKCS8#編碼。所以我們還要將字節數組轉化為PKCS8#編碼形式。實現方法如下:

PKCS8EncodedKeySpec keyspec=new PKCS8EncodedKeySpec(keybytes);

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

syprivatekey=keyfactory.generatePrivate(keyspec);

其中keybytes是從原文中讀出的字節數組形式的密鑰。用KeyFactory對象的實例化方法來指定算法,并用generatePrivate方法產生PKCS8#編碼的私鑰。

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

該步驟的實現比較簡單,不做過多說明。

3)將文件內容讀取為字節數組格式

因為簽名時Signature類的Update()方法的參數是字節數組形式,所以要求

先將原文讀為字節數組。并且,在此處可以獲得原文的內容長度。

4)生成簽名

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

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

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

?????圖 數字簽名過程

代碼實現如下:

//讀取私鑰

???? PrivateKey syprivatekey=null;

????? File syfile=new File("c:\\安全文件\\"+misClass.username+"\\非對稱\\本人公私鑰\\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);

???? }

???? //從對話框中取得要簽名的文件

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

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

???? //首先將文件讀為byte[]對象

??? 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();

?????????? //寫入對象流中

?? 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);

??? }

?

?