JAVA 的加密演算法

1 篇文章 / 0 new
author
JAVA 的加密演算法

對稱式加密系統是一種傳統的加密演算法,是指訊息的傳送方和接收方共同使用同 一把金鑰進行加解密。通常,使用的加密演算法比較簡便效率高,金鑰簡短,加解密速度快,破解極其困難。但是加密的安全性依靠金鑰保管的安全性,在公開的電 腦網路上安全地傳送和保管金鑰是一個困難的問題,並且如果在多使用者的情況下金鑰的保管安全性也是一個問題。

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函式的特殊要求是︰

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

代表︰DSA

2章在JAVA中的實作

2.1. 相關

Diffie-Hellman金鑰協議協定和DES程式需要JCE工具庫的支援,可以到 http://java.sun.com/security/index.html 下載JCE,並進行安裝。簡易安裝把 jce1.2.1\lib 下的所有內容複製到 %java_home%\lib\ext下,如果沒有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())

相關API

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<b.length;n++)

      {

       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));

       if (stmp.length()==1) hs=hs+"0"+stmp;

       else hs=hs+stmp;

       if (n<b.length-1)  hs=hs+":";

      }

     return hs.toUpperCase();

    }

 

}

 

2.3. 數位簽章DSA

  1. 對於一個使用者來講首先要產生他的金鑰對,並且分別儲存

產生一個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();

 

  1. 用他私密金鑰(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();

    把他的公開金鑰的訊息及簽章發給其它使用者

 

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

 

 

  1. 常用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
用指定的私密金鑰初始化
參數:privateKey 所進行簽章時用的私密金鑰

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
用指定的公開金鑰初始化
參數:publicKey 驗證時用的公開金鑰

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<b.length;n++)

      {

       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));

       if (stmp.length()==1) hs=hs+"0"+stmp;

       else hs=hs+stmp;

       if (n<b.length-1)  hs=hs+":";

      }

     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物件

參數:opmode 方式(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(TripleDES),AES,Blowfish,RC2,RC4(ARCFOUR)

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<b.length;n++)

      {

       stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));

       if (stmp.length()==1) hs=hs+"0"+stmp;

       else hs=hs+stmp;

       if (n<b.length-1)  hs=hs+":";

      }

     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 演算法名稱:DSH,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("解密後相同");

 

    }

}

 from http://www.ibm.com/developerworks/tw/library/l-security.html

Free Web Hosting