Linux 程式設計之JAVA上加密演算法的實現用例(轉)

ba發表於2007-08-11
Linux 程式設計之JAVA上加密演算法的實現用例(轉)[@more@]第1章 基礎知識

1.1. 單鑰密碼體制
單鑰密碼體制是一種傳統的加密演算法,是指資訊的傳送方和接收方共同使用同一把金鑰進行加解密。

通常,使用的加密演算法 比較簡便高效,金鑰簡短,加解密速度快,破譯極其困難。但是加密的安全性依靠金鑰保管的安全性,在公開的計算機網路上安全地傳送和保管金鑰是一個嚴峻的問題,並且如果在多使用者的情況下金鑰的保管安全性也是一個問題。

單鑰密碼體制的代表是美國的DES

1.2. 訊息摘要
一個訊息摘要就是一個資料塊的數字指紋。即對一個任意長度的一個資料塊進行計算,產生一個唯一指印(對於SHA1是產生一個20位元組的二進位制陣列)。

訊息摘要有兩個基本屬性:

兩個不同的報文難以生成相同的摘要
難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要
代表:美國國家標準技術研究所的SHA1和麻省理工學院Ronald Rivest提出的MD5

1.3. Diffie-Hellman金鑰一致協議
金鑰一致協議是由公開金鑰密碼體制的奠基人Diffie和Hellman所提出的一種思想。

先決條件,允許兩名使用者在公開媒體上交換資訊以生成"一致"的,可以共享的金鑰

代表:指數金鑰一致協議(Exponential Key Agreement Protocol)

1.4. 非對稱演算法與公鑰體系
1976年,Dittie和Hellman為解決金鑰管理問題,在他們的奠基性的工作"密碼學的新方向"一文中,提出一種金鑰交換協議,允許在不安全的媒體上透過通訊雙方交換資訊,安全地傳送秘密金鑰。在此新思想的基礎上,很快出現了非對稱金鑰密碼體制,即公鑰密碼體制。在公鑰體制中,加密金鑰不同於解密金鑰,加密金鑰公之於眾,誰都可以使用;解密金鑰只有解密人自己知道。它們分別稱為公開金鑰(Public key)和秘密金鑰(Private key)。

迄今為止的所有公鑰密碼體系中,RSA系統是最著名、最多使用的一種。RSA公開金鑰密碼系統是由R.Rivest、A.Shamir和L.Adleman俊教授於1977年提出的。RSA的取名就是來自於這三位發明者的姓的第一個字母

1.5. 數字簽名
所謂數字簽名就是資訊傳送者用其私鑰對從所傳報文中提取出的特徵資料(或稱數字指紋)進行RSA演算法操作,以保證發信人無法抵賴曾發過該資訊(即不可抵賴性),同時也確保資訊報文在經簽名後末被篡改(即完整性)。當資訊接收者收到報文後,就可以用傳送者的公鑰對數字簽名進行驗證。 

在數字簽名中有重要作用的數字指紋是透過一類特殊的雜湊函式(HASH函式)生成的,對這些HASH函式的特殊要求是:

接受的輸入報文資料沒有長度限制;
對任何輸入報文資料生成固定長度的摘要(數字指紋)輸出
從報文能方便地算出摘要;
難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要;
兩個不同的報文難以生成相同的摘要

代表:DSA

第2章 在JAVA中的實現

2.1. 相關
Diffie-Hellman金鑰一致協議和DES程式需要JCE工具庫的支援,可以到 http://java.sun.com/security/index.html 下載JCE,並進行安裝。簡易安裝把 jce1.2.1lib 下的所有內容複製到 %java_home%libext下,如果沒有ext目錄自行建立,再把jce1_2_1.jar和sunjce_provider.jar新增到CLASSPATH內,更詳細說明請看相應使用者手冊

2.2. 訊息摘要MD5和SHA的使用
使用方法:

首先用生成一個MessageDigest類,確定計算方法

java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");

新增要進行計算摘要的資訊

alga.update(myinfo.getBytes());

計算出摘要

byte[] digesta=alga.digest();

傳送給其他人你的資訊和摘要

其他人用相同的方法初始化,新增資訊,最後進行比較摘要是否相同

algb.isEqual(digesta,algb.digest())

相關AIP

java.security.MessageDigest 類

static getInstance(String algorithm)

返回一個MessageDigest物件,它實現指定的演算法

引數:演算法名,如 SHA-1 或MD5

void update (byte input)

void update (byte[] input)

void update(byte[] input, int offset, int len)

新增要進行計算摘要的資訊

byte[] digest()

完成計算,返回計算得到的摘要(對於MD5是16位,SHA是20位)

void reset()

復位

static boolean isEqual(byte[] digesta, byte[] digestb)

比效兩個摘要是否相同

程式碼:
import java.security.*;
public class myDigest {
public static void main(String[] args) {

myDigest my=new myDigest();
my.testDigest();

}
public void testDigest()
{
try {
String myinfo="我的測試資訊";

//java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");
java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");
alga.update(myinfo.getBytes());
byte[] digesta=alga.digest();
System.out.println("本資訊摘要是:"+byte2hex(digesta));
//透過某中方式傳給其他人你的資訊(myinfo)和摘要(digesta) 對方可以判斷是否更改或傳輸正常
java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");
algb.update(myinfo.getBytes());
if (algb.isEqual(digesta,algb.digest())) {
System.out.println("資訊檢查正常");
}
else
{
System.out.println("摘要不相同");
}

}
catch (java.security.NoSuchAlgorithmException ex) {
System.out.println("非法摘要演算法");
}

}
public String byte2hex(byte[] b) //二行制轉字串
{
String hs="";
String stmp="";
for (int n=0;n
{
stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length()==1) hs=hs+"0"+stmp;
else hs=hs+stmp;
if (n }
return hs.toUpperCase();
}

}



2.3. 數字簽名DSA

對於一個使用者來講首先要生成他的金鑰對,並且分別儲存
生成一個KeyPairGenerator例項
java.security.KeyPairGenerator keygen=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();




用他私人金鑰(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();
把他的公鑰的資訊及簽名發給其它使用者




其他使用者用他的公共金鑰(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);




常用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
用指定的私鑰初始化
引數rivateKey 所進行簽名時用的私鑰

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
用指定的公鑰初始化
引數ublicKey 驗證時用的公鑰

public final boolean verify(byte[] signature)
throws SignatureException
驗證簽名是否有效,前提是已經initVerify初始化
引數: signature 簽名陣列
*/
import java.security.*;
import java.security.spec.*;
public class testdsa {
public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {
testdsa my=new testdsa();
my.run();
}
public void run()
{

//數字簽名生成金鑰
//第一步生成金鑰對,如果已經生成過,本過程就可以跳過,對使用者來講myprikey.dat要儲存在本地
//而mypubkey.dat給釋出給其它使用者
if ((new java.io.File("myprikey.dat")).exists()==false) {
if (generatekey()==false) {
System.out.println("生成金鑰對敗");
return;
};
}
//第二步,此使用者
//從檔案中讀入私鑰,對一個字串進行簽名後儲存在一個檔案(myinfo.dat)中
//並且再把myinfo.dat傳送出去
//為了方便數字簽名也放進了myifno.dat檔案中,當然也可分別傳送
try {
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
PrivateKey myprikey=(PrivateKey)in.readObject();
in.close();

// java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);

//java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec
String myinfo="這是我的資訊"; //要簽名的資訊
//用私鑰對資訊生成數字簽名
java.security.Signature signet=java.security.Signature.getInstance("DSA");
signet.initSign(myprikey);
signet.update(myinfo.getBytes());
byte[] signed=signet.sign(); //對資訊的數字簽名
System.out.println("signed(簽名內容)="+byte2hex(signed));
//把資訊和數字簽名儲存在一個檔案中
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
out.writeObject(myinfo);
out.writeObject(signed);
out.close();
System.out.println("簽名並生成檔案成功");
}
catch (java.lang.Exception e) {
e.printStackTrace();
System.out.println("簽名並生成檔案失敗");
};

//第三步
//其他人透過公共方式得到此戶的公鑰和檔案
//其他人用此戶的公鑰,對檔案進行檢查,如果成功說明是此使用者釋出的資訊.
//
try {

java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
PublicKey pubkey=(PublicKey)in.readObject();
in.close();
System.out.println(pubkey.getFormat());

in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
String info=(String)in.readObject();
byte[] signed=(byte[])in.readObject();
in.close();

java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
signetcheck.initVerify(pubkey);
signetcheck.update(info.getBytes());
if (signetcheck.verify(signed)) {
System.out.println("info="+info);
System.out.println("簽名正常");
}
else System.out.println("非簽名正常");
}
catch (java.lang.Exception e) {e.printStackTrace();};


}

//生成一對檔案myprikey.dat和mypubkey.dat---私鑰和公鑰,
//公鑰要使用者傳送(檔案,網路等方法)給其它使用者,私鑰儲存在本地
public boolean generatekey()
{
try {
java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance("DSA");
// SecureRandom secrand=new SecureRandom();
// secrand.setSeed("tttt".getBytes()); //初始化隨機產生器
// keygen.initialize(576,secrand); //初始化金鑰生成器
keygen.initialize(512);
KeyPair keys=keygen.genKeyPair();
// KeyPair keys=keygen.generateKeyPair(); //生成金鑰組
PublicKey pubkey=keys.getPublic();
PrivateKey prikey=keys.getPrivate();

java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
out.writeObject(prikey);
out.close();
System.out.println("寫入物件 prikeys ok");
out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
out.writeObject(pubkey);
out.close();
System.out.println("寫入物件 pubkeys ok");
System.out.println("生成金鑰對成功");
return true;
}
catch (java.lang.Exception e) {
e.printStackTrace();
System.out.println("生成金鑰對失敗");

return false;
};

}

public String byte2hex(byte[] b)
{
String hs="";
String stmp="";
for (int n=0;n
{
stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length()==1) hs=hs+"0"+stmp;
else hs=hs+stmp;
if (n }
return hs.toUpperCase();
}

}



2.4. DESede/DES對稱演算法
首先生成金鑰,並儲存(這裡並沒的儲存的程式碼,可參考DSA中的方法)

KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);

SecretKey deskey = keygen.generateKey();

用金鑰加密明文(myinfo),生成密文(cipherByte)

Cipher c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.ENCRYPT_MODE,deskey);

byte[] cipherByte=c1.doFinal(myinfo.getBytes());

傳送密文和金鑰,本文沒有相應程式碼可參考DSA

.............

用金鑰解密密文

c1 = Cipher.getInstance(Algorithm);

c1.init(Cipher.DECRYPT_MODE,deskey);

byte[] clearByte=c1.doFinal(cipherByte);

相對來說對稱金鑰的使用是很簡單的,對於JCE來講支技DES,DESede,Blowfish三種加密術

對於金鑰的儲存各傳送可使用物件流或者用二進位制編碼,相關參考程式碼如下
SecretKey deskey = keygen.generateKey();
byte[] desEncode=deskey.getEncoded();
javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);
SecretKey mydeskey=destmp;



相關API

KeyGenerator 在DSA中已經說明,在新增JCE後在instance進可以如下引數

DES,DESede,Blowfish,HmacMD5,HmacSHA1

javax.crypto.Cipher 加/解密器 public static final Cipher getInstance(java.lang.String transformation)
throws java.security.NoSuchAlgorithmException,
NoSuchPaddingException
返回一個指定方法的Cipher物件

引數:transformation 方法名(可用 DES,DESede,Blowfish)

public final void init(int opmode, java.security.Key key)
throws java.security.InvalidKeyException

用指定的金鑰和模式初始化Cipher物件

引數pmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)

key 金鑰


public final byte[] doFinal(byte[] input)
throws java.lang.IllegalStateException,
IllegalBlockSizeException,
BadPaddingException




對input內的串,進行編碼處理,返回處理後二進位制串,是返回解密文還是加解文由init時的opmode決定

注意:本方法的執行前如果有update,是對updat和本次input全部處理,否則是本inout的內容

/*
安全程式 DESede/DES測試
*/
import java.security.*;
import javax.crypto.*;
public class testdes {
public static void main(String[] args){
testdes my=new testdes();
my.run();
}
public void run() {
//新增新安全演算法,如果用JCE就要把它新增進去
Security.addProvider(new com.sun.crypto.provider.SunJCE());
String Algorithm="DES"; //定義 加密演算法,可用 DES,DESede,Blowfish
String myinfo="要加密的資訊";
try {
//生成金鑰
KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);
SecretKey deskey = keygen.generateKey();

//加密
System.out.println("加密前的二進串:"+byte2hex(myinfo.getBytes()));
System.out.println("加密前的資訊:"+myinfo);
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE,deskey);
byte[] cipherByte=c1.doFinal(myinfo.getBytes());
System.out.println("加密後的二進串:"+byte2hex(cipherByte));
//解密
c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE,deskey);
byte[] clearByte=c1.doFinal(cipherByte);
System.out.println("解密後的二進串:"+byte2hex(clearByte));
System.out.println("解密後的資訊:"+(new String(clearByte)));

}
catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}
catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}
catch (java.lang.Exception e3) {e3.printStackTrace();}
}
public String byte2hex(byte[] b) //二行制轉字串
{
String hs="";
String stmp="";
for (int n=0;n
{
stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length()==1) hs=hs+"0"+stmp;
else hs=hs+stmp;
if (n }
return hs.toUpperCase();
}

}



2.5. Diffie-Hellman金鑰一致協議
公開金鑰密碼體制的奠基人Diffie和Hellman所提出的 "指數金鑰一致協議"(Exponential Key Agreement Protocol),該協議不要求別的安全性 先決條件,允許兩名使用者在公開媒體上交換資訊以生成"一致"的,可以共享的金鑰。在JCE的中實現使用者alice生成DH型別的金鑰對,如果長度用1024生成的時間請,推薦第一次生成後儲存DHParameterSpec,以便下次使用直接初始化.使其速度加快

System.out.println("ALICE: 產生 DH 對 ...");
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
aliceKpairGen.initialize(512);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();



alice生成公鑰傳送組bob byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();



bob從alice傳送來的公鑰中讀出DH金鑰對的初始引數生成bob的DH金鑰對

注意這一步一定要做,要保證每個使用者用相同的初始引數生成的
DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamSpec);
KeyPair bobKpair = bobKpairGen.generateKeyPair();



bob根據alice的公鑰生成本地的DES金鑰
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKpair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");



bob已經生成了他的DES金鑰,他現把他的公鑰發給alice,
byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();



alice根據bob的公鑰生成本地的DES金鑰
,,,,,,解碼
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKpair.getPrivate());
aliceKeyAgree.doPhase(bobPubKey, true);
SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");



bob和alice能過這個過程就生成了相同的DES金鑰,在這種基礎就可進行安全能信

常用API

java.security.KeyPairGenerator 金鑰生成器類
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的演算法返回一個KeyPairGenerator 物件
引數: algorithm 演算法名.如:原來是DSA,現在新增了 DiffieHellman(DH)

public void initialize(int keysize)
以指定的長度初始化KeyPairGenerator物件,如果沒有初始化系統以1024長度預設設定
引數:keysize 演算法位長.其範圍必須在 512 到 1024 之間,且必須為 64 的倍數
注意:如果用1024生長的時間很長,最好生成一次後就儲存,下次就不用生成了

public void initialize(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException
以指定引數初始化

javax.crypto.interfaces.DHPublicKey
public DHParameterSpec getParams()
返回
java.security.KeyFactory

public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的演算法返回一個KeyFactory
引數: algorithm 演算法名SH,DH

public final PublicKey generatePublic(KeySpec keySpec)
throws InvalidKeySpecException
根據指定的key說明,返回一個PublicKey物件

java.security.spec.X509EncodedKeySpec
public X509EncodedKeySpec(byte[] encodedKey)
根據指定的二進位制編碼的字串生成一個key的說明
引數:encodedKey 二進位制編碼的字串(一般能過PublicKey.getEncoded()生成)
javax.crypto.KeyAgreement 密碼一至類

public static final KeyAgreement getInstance(java.lang.String algorithm)
throws java.security.NoSuchAlgorithmException
返回一個指定演算法的KeyAgreement物件
引數:algorithm 演算法名,現在只能是DiffieHellman(DH)

public final void init(java.security.Key key)
throws java.security.InvalidKeyException
用指定的私鑰初始化
引數:key 一個私鑰

public final java.security.Key doPhase(java.security.Key key,
boolean lastPhase)
throws java.security.InvalidKeyException,
java.lang.IllegalStateException
用指定的公鑰進行定位,lastPhase確定這是否是最後一個公鑰,對於兩個使用者的
情況下就可以多次定次,最後確定
引數:key 公鑰
lastPhase 是否最後公鑰

public final SecretKey generateSecret(java.lang.String algorithm)
throws java.lang.IllegalStateException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
根據指定的演算法生成金鑰
引數:algorithm 加密演算法(可用 DES,DESede,Blowfish)


*/
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.security.interfaces.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import com.sun.crypto.provider.SunJCE;

public class testDHKey {


public static void main(String argv[]) {
try {
testDHKey my= new testDHKey();
my.run();
} catch (Exception e) {
System.err.println(e);

}
}

private void run() throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE());

System.out.println("ALICE: 產生 DH 對 ...");
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
aliceKpairGen.initialize(512);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成時間長

// 張三(Alice)生成公共金鑰 alicePubKeyEnc 併傳送給李四(Bob) ,
//比如用檔案方式,socket.....
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

//bob接收到alice的編碼後的公鑰,將其解碼
KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec (alicePubKeyEnc);
PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);
System.out.println("alice公鑰bob解碼成功");
// bob必須用相同的引數初始化的他的DH KEY對,所以要從Alice發給他的公開金鑰,
//中讀出引數,再用這個引數初始化他的 DH key對

//從alicePubKye中取alice初始化時用的引數
DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamSpec);
KeyPair bobKpair = bobKpairGen.generateKeyPair();
System.out.println("BOB: 生成 DH key 對成功");
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKpair.getPrivate());
System.out.println("BOB: 初始化本地key成功");
//李四(bob) 生成本地的金鑰 bobDesKey
bobKeyAgree.doPhase(alicePubKey, true);
SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");
System.out.println("BOB: 用alice的公鑰定位本地key,生成本地DES金鑰成功");
// Bob生成公共金鑰 bobPubKeyEnc 併傳送給Alice,
//比如用檔案方式,socket.....,使其生成本地金鑰
byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();
System.out.println("BOB向ALICE傳送公鑰");

// alice接收到 bobPubKeyEnc後生成bobPubKey
// 再進行定位,使aliceKeyAgree定位在bobPubKey
KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
System.out.println("ALICE接收BOB公鑰並解碼成功");
;
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKpair.getPrivate());
System.out.println("ALICE: 初始化本地key成功");

aliceKeyAgree.doPhase(bobPubKey, true);
// 張三(alice) 生成本地的金鑰 aliceDesKey
SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");
System.out.println("ALICE: 用bob的公鑰定位本地key,並生成本地DES金鑰");

if (aliceDesKey.equals(bobDesKey)) System.out.println("張三和李四的金鑰相同");
//現在張三和李四的本地的deskey是相同的所以,完全可以進行傳送加密,接收後解密,達到
//安全通道的的目的

/*
* bob用bobDesKey金鑰加密資訊
*/
Cipher bobCipher = Cipher.getInstance("DES");
bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);
String bobinfo= "這是李四的機密資訊";
System.out.println("李四加密前原文:"+bobinfo);
byte[] cleartext =bobinfo.getBytes();
byte[] ciphertext = bobCipher.doFinal(cleartext);

/*
* alice用aliceDesKey金鑰解密
*/
Cipher aliceCipher = Cipher.getInstance("DES");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);
byte[] recovered = aliceCipher.doFinal(ciphertext);
System.out.println("alice解密bob的資訊:"+(new String(recovered)));
if (!java.util.Arrays.equals(cleartext, recovered))
throw new Exception("解密後與原文資訊不同");
System.out.println("解密後相同");

}

}

第3章 小結
在加密術中生成金鑰對時,金鑰對的當然是越長越好,但費時也越多,請從中從實際出發選取合適的長度,大部分例碼中的金鑰是每次執行就從新生成,在實際的情況中是生成後在一段時間儲存在檔案中,再次執行直接從檔案中讀入,從而加快速度。當然定時更新和加強金鑰保管的安全性也是必須的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-947610/,如需轉載,請註明出處,否則將追究法律責任。

相關文章