1. 程式人生 > >Java加密解密全解

Java加密解密全解

1 sha加密

安全雜湊演算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裡面定義的數字簽名演算法(Digital Signature Algorithm DSA)。對於長度小於2^64位的訊息,SHA1會產生一個160位的訊息摘要。該演算法經過加密專家多年來的發展和改進已日益完善,並被廣泛使用。該演算法的思想是接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預對映或資訊),並把它們轉化為長度較短、位數固定的輸出序列即雜湊值(也稱為資訊摘要或資訊認證程式碼)的過程。雜湊函式值可以說是對明文的一種“

指紋”或是“摘要”所以對雜湊值的數字簽名就可以視為對此明文的數字簽名。

(Secure Hash Algorithm,SHA) 是美國國家標準技術研究所釋出的國家標準FIPS PUB 180,最新的標準已經於2008年更新到FIPS PUB 180-3。其中規定了SHA-1,SHA-224,SHA-256,SHA-384,和SHA-512這幾種單向雜湊演算法。SHA-1,SHA-224和SHA-256適用於長度不超過2^64二進位制位的訊息。SHA-384和SHA-512適用於長度不超過2^128二進位制位的訊息。

雜湊演算法

雜湊是資訊的提煉,通常其長度要比資訊小得多,且為一個固定長度。加密性強的雜湊一定是不可逆的,這就意味著通過雜湊結果,無法推出任何部分的原始資訊。任何輸入資訊的變化,哪怕僅一位,都將導致雜湊結果的明顯變化,這稱之為雪崩效應。雜湊還應該是防衝突的,即找不出具有相同雜湊結果的兩條資訊。具有這些特性的雜湊結果就可以用於驗證資訊是否被修改。
單向雜湊函式
一般用於產生訊息摘要,金鑰加密等,常見的有: l MD5(Message Digest Algorithm 5):是RSA資料安全公司開發的一種單向雜湊演算法。 l SHA(Secure Hash Algorithm):可以對任意長度的資料運算生成一個160位的數值; SHA-1 在1993年,安全雜湊演算法(SHA)由美國國家標準和技術協會(NIST)提出,並作為聯邦資訊處理標準(FIPS PUB 180)公佈;1995年又釋出了一個修訂版FIPS PUB 180-1,通常稱之為SHA-1。SHA-1是基於MD4演算法的,並且它的設計在很大程度上是模仿MD4的。現在已成為公認的最安全的雜湊演算法之一,並被廣泛使用。

原理

SHA-1是一種資料加密演算法,該演算法的思想是接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預對映或資訊),並把它們轉化為長度較短、位數固定的輸出序列即雜湊值(也稱為資訊摘要或資訊認證程式碼)的過程。 單向雜湊函式的安全性在於其產生雜湊值的操作過程具有較強的單向性。如果在輸入序列中嵌入密碼,那麼任何人在不知道密碼的情況下都不能產生正確的雜湊值,從而保證了其安全性。SHA將輸入流按照每塊512位(64個位元組)進行分塊,併產生20個位元組的被稱為資訊認證程式碼或資訊摘要的輸出。 該演算法輸入報文的長度不限,產生的輸出是一個160位的報文摘要。輸入是按512 位的分組進行處理的。SHA-1是不可逆的、防衝突,並具有良好的雪崩效應。 通過雜湊演算法可實現數字簽名實現,數字簽名的原理是將要傳送的明文通過一種函式運算(Hash)轉換成報文摘要(不同的明文對應不同的報文摘要),報文摘要加密後與明文一起傳送給接受方,接受方將接受的明文產生新的報文摘要與傳送方的發來報文摘要解密比較,比較結果一致表示明文未被改動,如果不一致表示明文已被篡改。 MAC (資訊認證程式碼)就是一個雜湊結果,其中部分輸入資訊是密碼,只有知道這個密碼的參與者才能再次計算和驗證MAC碼的合法性。

SHA-1與MD5的比較

因為二者均由MD4匯出,SHA-1和MD5彼此很相似。相應的,他們的強度和其他特性也是相似,但還有以下幾點不同: l 對強行攻擊的安全性:最顯著和最重要的區別是SHA-1摘要比MD5摘要長32 位。使用強行技術,產生任何一個報文使其摘要等於給定報摘要的難度對MD5是2^128數量級的操作,而對SHA-1則是2^160數量級的操作。這樣,SHA-1對強行攻擊有更大的強度。 l 對密碼分析的安全性:由於MD5的設計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。 l 速度:在相同的硬體上,SHA-1的執行速度比MD5慢。

JAVA 已經實現了 SHA-256 和 SHA-512 兩種 Hash 演算法

利用 java.security.MessageDigest 呼叫已經整合的 Hash 演算法

建立 Encrypt 物件,並呼叫 SHA256 或者 SHA512 並傳入要加密的文字資訊,分別得到 SHA-256 或 SHA-512 兩種被加密的 hash 串。

若要改為 MD5 演算法,修改傳入引數 strType 為 "MD5" 即可得到 MD5 加密功能。


/**
 * @file Encrypt.java
 * @date 2016年8月5日
 * @version 3.4.1
 *
 * Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
 */
package encrypt;
 
/**
 * 
 *
 * @author chengjian.he
 * @version  3.4, 2016年8月5日 上午10:05:37 
 * @since   
 */

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Encrypt
{

  /**
   * 傳入文字內容,返回 SHA-256 串
   * 
   * @param strText
   * @return
   */
  public String SHA256(final String strText)
  {
    return SHA(strText, "SHA-256");
  }

  /**
   * 傳入文字內容,返回 SHA-512 串
   * 
   * @param strText
   * @return
   */
  public String SHA512(final String strText)
  {
    return SHA(strText, "SHA-512");
  }

  /**
   * 字串 SHA 加密
   * 
   * @param strSourceText
   * @return
   */
  private String SHA(final String strText, final String strType)
  {
    // 返回值
    String strResult = null;

    // 是否是有效字串
    if (strText != null && strText.length() > 0)
    {
      try
      {
        // SHA 加密開始
        // 建立加密物件 並傳入加密型別
        MessageDigest messageDigest = MessageDigest.getInstance(strType);
        // 傳入要加密的字串
        messageDigest.update(strText.getBytes());
        // 得到 byte 型別結果
        byte byteBuffer[] = messageDigest.digest();

        // 將 byte 轉換為 string
        StringBuffer strHexString = new StringBuffer();
        // 遍歷 byte buffer
        for (int i = 0; i < byteBuffer.length; i++)
        {
          String hex = Integer.toHexString(0xff & byteBuffer[i]);
          if (hex.length() == 1)
          {
            strHexString.append('0');
          }
          strHexString.append(hex);
        }
        // 得到返回結果
        strResult = strHexString.toString();
      }
      catch (NoSuchAlgorithmException e)
      {
        e.printStackTrace();
      }
    }

    return strResult;
  }
  
  public static void main(String args[]){
	  Encrypt ey = new Encrypt();
	  System.out.println(ey.SHA("ILoveYou", "MD5"));//62accaf23ac9a73c0b28765b7dfaf75a
  }
}


2 Base64

Base64是網路上最常見的用於傳輸8Bit位元組程式碼的編碼方式之一,大家可以檢視RFC2045~RFC2049,上面有MIME的詳細規範。Base64編碼可用於在HTTP環境下傳遞較長的標識資訊。例如,在Java Persistence系統Hibernate中,就採用了Base64來將一個較長的唯一識別符號(一般為128-bit的UUID)編碼為一個字串,用作HTTP表單和HTTP GET URL中的引數。在其他應用程式中,也常常需要把二進位制資料編碼為適合放在URL(包括隱藏表單域)中的形式。此時,採用Base64編碼具有不可讀性,即所編碼的資料不會被人用肉眼所直接看到。

然而,標準的Base64並不適合直接放在URL裡傳輸,因為URL編碼器會把標準Base64中的“/”和“+”字元變為形如“%XX”的形式,而這些“%”號在存入資料庫時還需要再進行轉換,因為ANSI SQL中已將“%”號用作萬用字元。 為解決此問題,可採用一種用於URL的改進Base64編碼,它不僅在末尾填充'='號,並將標準Base64中的“+”和“/”分別改成了“-”和“_”,這樣就免去了在URL編解碼和資料庫儲存時所要作的轉換,避免了編碼資訊長度在此過程中的增加,並統一了資料庫、表單等處物件識別符號的格式。 另有一種用於正則表示式的改進Base64變種,它將“+”和“/”改成了“!”和“-”,因為“+”,“*”以及前面在IRCu中用到的“[”和“]”在正則表示式中都可能具有特殊含義。 此外還有一些變種,它們將“+/”改為“_-”或“._”(用作程式語言中的識別符號名稱)或“.-”(用於XML中的Nmtoken)甚至“_:”(用於XML中的Name)。 其他應用 Mozilla Thunderbird和Evolution用Base64來保密電子郵件密碼 Base64 也會經常用作一個簡單的“加密”來保護某些資料,而真正的加密通常都比較繁瑣。 垃圾訊息傳播者用Base64來避過反垃圾郵件工具,因為那些工具通常都不會翻譯Base64的訊息。 在LDIF檔案,Base64用作編碼字串。
/**
 * @file Base64.java
 * @date 2016年8月5日
 * @version 3.4.1
 *
 * Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
 */
package encrypt;

import java.io.UnsupportedEncodingException;

import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;

/**
 * 
 *
 * @author chengjian.he
 * @version  3.4, 2016年8月5日 上午10:32:23 
 * @since   Yeexun 3.4
 */

public class Base64 {
	// 加密
	public String getBase64(String str) {
		byte[] b = null;
		String s = null;
		try {
			b = str.getBytes("utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		if (b != null) {
			s = new BASE64Encoder().encode(b);
		}
		return s;
	}

	// 解密
	public String getFromBase64(String s) {
		byte[] b = null;
		String result = null;
		if (s != null) {
			BASE64Decoder decoder = new BASE64Decoder();
			try {
				b = decoder.decodeBuffer(s);
				result = new String(b, "utf-8");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return result;
	}
	
	public static void main(String args[]){
		Base64 b6 = new Base64();
		System.out.println(b6.getBase64("ILoveYou"));
		System.out.println(b6.getFromBase64(b6.getBase64("ILoveYou")));
	}
}

SUxvdmVZb3U=
ILoveYou

3 Base64Encoder

一直以來Base64的加密解密都是使用sun.misc包下的BASE64EncoderBASE64Decodersun.misc.BASE64Encoder/BASE64Decoder類。這人個類是sun公司的內部方法,並沒有在java api中公開過,不屬於JDK標準庫範疇,但在JDK中包含了該類,可以直接使用。

/**
 * @file Base64Encoder.java
 * @date 2016年8月5日
 * @version 3.4.1
 *
 * Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
 */
package encrypt;
 
/**
 * 
 *
 * @author chengjian.he
 * @version  3.4, 2016年8月5日 上午10:44:22 
 * @since   Yeexun 3.4
 */
public class Base64Encoder {
    public static String getBASE64(String s) {  
        if (s == null)  
            return null;  
        return (new sun.misc.BASE64Encoder()).encode(s.getBytes());  
    }  
    // 將 BASE64 編碼的字串 s 進行解碼   解密
    public static String getFromBASE64(String s) {  
        if (s == null)  
            return null;  
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();  
        try {  
            byte[] b = decoder.decodeBuffer(s);  
            return new String(b);  
        } catch (Exception e) {  
            return null;  
        }  
    }  
    public static String mTOa(Object ming){
        return Base64Encoder.getBASE64(Base64Encoder.getBASE64(Base64Encoder.getBASE64((String)ming)));
    }
    public static String aTOm(String an){
        return Base64Encoder.getFromBASE64(Base64Encoder.getFromBASE64(Base64Encoder.getFromBASE64(an)));
    }
    public static void main(String[] args) {
        String a = mTOa("100000.89".toString());
          System.out.println(a);//加密
          System.out.println(aTOm(a));//解密
    }
}


4 RSA

RSA公鑰加密演算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。1987年首次公佈,當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。 RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密標準。 今天只有短的RSA鑰匙才可能被強力方式解破。到2008年為止,世界上還沒有任何可靠的攻擊RSA演算法的方式。只要其鑰匙的長度足夠長,用RSA加密的資訊實際上是不能被解破的。但在分散式計算量子計算機理論日趨成熟的今天,RSA加密安全性受到了挑戰。 RSA演算法基於一個十分簡單的數論事實:將兩個大質數相乘十分容易,但是想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。

缺點

編輯 1)產生金鑰很麻煩,受到素數產生技術的限制,因而難以做到一次一密。 2)安全性,RSA的安全性依賴於大數的因子分解,但並沒有從理論上證明破譯RSA的難度與大數分解難度等價,而且密碼學界多數人士傾向於因子分解不是NP問題。現今,人們已能分解140多個十進位制位的大素數,這就要求使用更長的金鑰,速度更慢;另外,人們正在積極尋找攻擊RSA的方法,如選擇密文攻擊,一般攻擊者是將某一資訊作一下偽裝(Blind),讓擁有私鑰的實體簽署。然後,經過計算就可得到它所想要的資訊。實際上,攻擊利用的都是同一個弱點,即存在這樣一個事實:乘冪保留了輸入的乘法結構: (XM)d = Xd *Md mod n 前面已經提到,這個固有的問題來自於公鑰密碼系統的最有用的特徵--每個人都能使用公鑰。但從演算法上無法解決這一問題,主要措施有兩條:一條是採用好的公鑰協議,保證工作過程中實體不對其他實體任意產生的資訊解密,不對自己一無所知的資訊簽名;另一條是決不對陌生人送來的隨機文件簽名,簽名時首先使用One-Way Hash Function對文件作HASH處理,或同時使用不同的簽名演算法。除了利用公共模數,人們還嘗試一些利用解密指數或φ(n)等等攻擊. 3)速度太慢,由於RSA 的分組長度太大,為保證安全性,n 至少也要 600 bits以上,使運算代價很高,尤其是速度較慢,較對稱密碼演算法慢幾個數量級;且隨著大數分解技術的發展,這個長度還在增加,不利於資料格式的標準化。SET(Secure Electronic Transaction)協議中要求CA採用2048位元長的金鑰,其他實體使用1024位元的金鑰。為了速度問題,人們廣泛使用單,公鑰密碼結合使用的方法,優缺點互補:單鑰密碼加密速度快,人們用它來加密較長的檔案,然後用RSA來給檔案金鑰加密,極好的解決了單鑰密碼的金鑰分發問題。
/**
 * @file RSATool.java
 * @date 2016年8月5日
 * @version 3.4.1
 *
 * Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
 */
package encrypt;
 
/**
 * 
 *
 * @author chengjian.he
 * @version  3.4, 2016年8月5日 上午10:51:35 
 * @since   Yeexun 3.4
 */
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;

public class RSATool {

	public static void makekeyfile(String pubkeyfile, String privatekeyfile)
			throws NoSuchAlgorithmException, FileNotFoundException, IOException {
		// KeyPairGenerator類用於生成公鑰和私鑰對,基於RSA演算法生成物件
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		// 初始化金鑰對生成器,金鑰大小為1024位
		keyPairGen.initialize(1024);
		// 生成一個金鑰對,儲存在keyPair中
		KeyPair keyPair = keyPairGen.generateKeyPair();

		// 得到私鑰
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		// 得到公鑰
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

		// 生成私鑰
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
				privatekeyfile));
		oos.writeObject(privateKey);
		oos.flush();
		oos.close();

		oos = new ObjectOutputStream(new FileOutputStream(pubkeyfile));
		oos.writeObject(publicKey);
		oos.flush();
		oos.close();

		System.out.println("make file ok!");
	}

	/**
	 * 
	 * @param k
	 * @param data
	 * @param encrypt
	 *            1 加密 0解密
	 * @return
	 * @throws NoSuchPaddingException
	 * @throws Exception
	 */
	public static byte[] handleData(Key k, byte[] data, int encrypt)
			throws Exception {

		if (k != null) {

			Cipher cipher = Cipher.getInstance("RSA");

			if (encrypt == 1) {
				cipher.init(Cipher.ENCRYPT_MODE, k);
				byte[] resultBytes = cipher.doFinal(data);
				return resultBytes;
			} else if (encrypt == 0) {
				cipher.init(Cipher.DECRYPT_MODE, k);
				byte[] resultBytes = cipher.doFinal(data);
				return resultBytes;
			} else {
				System.out.println("引數必須為: 1 加密 0解密");
			}
		}
		return null;
	}

	public static void main(String[] args) throws Exception {

		String pubfile = "d:/temp/pub.key";
		String prifile = "d:/temp/pri.key";

		 makekeyfile(pubfile, prifile);

		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pubfile));
		RSAPublicKey pubkey = (RSAPublicKey) ois.readObject();
		ois.close();

		ois = new ObjectInputStream(new FileInputStream(prifile));
		RSAPrivateKey prikey = (RSAPrivateKey) ois.readObject();
		ois.close();

		// 使用公鑰加密
		String msg = "~O(∩_∩)O哈哈~";
		String enc = "UTF-8";

		// 使用公鑰加密私鑰解密
		System.out.println("原文: " + msg);
		byte[] result = handleData(pubkey, msg.getBytes(enc), 1);
		System.out.println("加密: " + new String(result, enc));
		byte[] deresult = handleData(prikey, result, 0);
		System.out.println("解密: " + new String(deresult, enc));

		msg = "嚯嚯";
		// 使用私鑰加密公鑰解密
		System.out.println("原文: " + msg);
		byte[] result2 = handleData(prikey, msg.getBytes(enc), 1);
		System.out.println("加密: " + new String(result2, enc));
		byte[] deresult2 = handleData(pubkey, result2, 0);
		System.out.println("解密: " + new String(deresult2, enc));

	}
}
make file ok!
原文: ~O(∩_∩)O哈哈~
加密: �A N�ډB�����ym��r�C��ʇ�������U

5 AES對稱加密

加密技術可以分為對稱與非對稱兩種.

對稱加密,解密,即加密與解密用的是同一把祕鑰,常用的對稱加密技術有DES,AES等

而非對稱技術,加密與解密用的是不同的祕鑰,常用的非對稱加密技術有RSA等

為什麼要有非對稱加密,解密技術呢

假設這樣一種場景A要傳送一段訊息給B,但是又不想以明文傳送,所以就需要對訊息進行加密.如果採用對稱加密技術,那麼加密與解密用的是同一把祕鑰.除非B事先就知道A的祕鑰,並且儲存好.這樣才可以解密A發來的訊息.

由於對稱技術只有一把祕鑰,所以祕鑰的管理是一個很麻煩的問題.而非對稱技術的誕生就解決了這個問題.非對稱加密與解密使用的是不同的祕鑰,並且祕鑰對是一一對應的,即用A的私鑰加密的密文只有用A的公鑰才能解密.

這樣的話,每個人都有兩把祕鑰,私鑰和公鑰,私鑰是隻有自己才知道的,不能告訴別人,而公鑰是公開的,大家都可以知道.這樣,當A想要傳送訊息給B的時候,只需要用B的公鑰對訊息進行加密就可以了,由於B的私鑰只有B才擁有,所以A用B的公鑰加密的訊息只有B才能解開.而B想更換自己的祕要時也很方便,只須把公鑰告訴大家就可以了.

那麼,既然非對稱加密如此之好,對稱加密就沒有存在的必要了啊,其實不然,由於非對稱加密演算法的開銷很大,所以如果直接以非對稱技術來加密傳送的訊息效率會很差.那麼怎麼辦呢?解決的辦法也很簡單,就是把對稱加密技術與非對稱加密技術結合起來使用.

還是這個例子:A要傳送一個訊息給B.

一,A先生成一個對稱祕鑰,這個祕鑰可以是隨機生成的,

二,A用B的公鑰加密第一步生成的這個對稱祕鑰

三,A把加密過的對稱祕鑰發給B

四,A用第一步生成的這個對稱祕鑰加密實際要發的訊息

五,A把用對稱祕鑰加密的訊息發給B

對於B

他先收到A發來的對稱祕鑰,這個祕鑰是用B的公鑰加密過的,所以B需要用自己的私鑰來解密這個祕鑰

然後B又收到A發來的密文,這時候用剛才解密出來的祕鑰來解密密文

這樣子的整個過程既保證了安全,又保證了效率.

接下來是Java實現:

我這個Java實現使用的是AES的對稱加密和RSA的非對稱加密(DES的對稱加密實現方法和AES的是一樣的,但是由於DES演算法本身有缺陷,容易被破解,所以現在多用其升級版AES對稱加密)

/**
 * @file AES.java
 * @date 2016年8月5日
 * @version 3.4.1
 *
 * Copyright (c) 2013 Sihua Tech, Inc. All Rights Reserved.
 */
package encrypt;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
 * 
 *
 * @author chengjian.he
 * @version  3.4, 2016年8月5日 上午11:35:13 
 * @since   Yeexun 3.4
 */
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

public class AES {
	
	private Key key;
	
	/**
	 * 生成AES對稱祕鑰
	 * @throws NoSuchAlgorithmException
	 */
	public void generateKey() throws NoSuchAlgorithmException {
		KeyGenerator keygen = KeyGenerator.getInstance("AES");
		SecureRandom random = new SecureRandom();
		keygen.init(random);
		this.key = keygen.generateKey();
	}
	
	
	/**
	 * 加密
	 * @param in
	 * @param out
	 * @throws InvalidKeyException
	 * @throws ShortBufferException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws IOException
	 */
	public void encrypt(InputStream in, OutputStream out) throws InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
		this.crypt(in, out, Cipher.ENCRYPT_MODE);
	}
	
	/**
	 * 解密
	 * @param in
	 * @param out
	 * @throws InvalidKeyException
	 * @throws ShortBufferException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws IOException
	 */
	public void decrypt(InputStream in, OutputStream out) throws InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
		this.crypt(in, out, Cipher.DECRYPT_MODE);
	}

	/**
	 * 實際的加密解密過程
	 * @param in
	 * @param out
	 * @param mode
	 * @throws IOException
	 * @throws ShortBufferException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 */
	public void crypt(InputStream in, OutputStream out, int mode) throws IOException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(mode, this.key);
		
		int blockSize = cipher.getBlockSize();
		int outputSize = cipher.getOutputSize(blockSize);
		byte[] inBytes = new byte[blockSize];
		byte[] outBytes = new byte[outputSize];
		
		int inLength = 0;
		boolean more = true;
		while (more) {
			inLength = in.read(inBytes);
			if (inLength == blockSize) {
				int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
				out.write(outBytes, 0, outLength);
			} else {
				more = false;
			}
		}
		if (inLength > 0)
			outBytes = cipher.doFinal(inBytes, 0, inLength);
		else
			outBytes = cipher.doFinal();
		out.write(outBytes);
		out.flush();
	}

	public void setKey(Key key) {
		this.key = key;
	}

	public Key getKey() {
		return key;
	}
	
	public static void main(String[] args) throws Exception {
		AES aes = new AES();
        aes.generateKey();
        File file = new File("D:/aa.jpg");  
        FileInputStream in = new FileInputStream(file); 
        File file1 = new File("D:/temp/pub.key");  
        FileOutputStream out = new FileOutputStream(file1);  
		aes.encrypt(in, out);
		aes.decrypt(in, out);
	}

}