1. 程式人生 > >【工具類】Java實現AES演算法 加密和解密

【工具類】Java實現AES演算法 加密和解密

最近在學AES加密,通過網上學習查詢資料,封裝了一個AES加密解密工具類,說幾點:

  1. 演算法/模式/填充,這個最好使用"AES/CBC/PKCS5Padding","ECB"在IOS中不安全(網上查詢資料得知),不要使用預設填充
  2. 引數金鑰要使用16位,不使用"AES/CBC/PKCS5Padding"這種模式是不需要16位金鑰的,不過這種安全性比較高,關於安全性可以查詢下資料
  3. 這裡主要是程式碼方面,理論方面要查詢其他資料(修改版:第一次發的程式碼,跨域解密不出來,這次的可以了)
  4. 重點)Linux下解密一定要加上SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
      secureRandom.setSeed(secretKey.getBytes());否則會報錯,javax.crypto.BadPaddingException: Given final block not properly padded
  5. 重點)使用Base64再包一層是為了防止加密後在變數之間傳遞丟字元

如果有不對的地方,請大家多指點。微笑 多反饋。

模式/填充參考:

演算法/模式/填充 16位元組加密後資料長度 不滿16位元組加密後長度
AES/CBC/NoPadding 16 不支援
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始資料長度
AES/CFB/PKCS5Padding 32 16
AES/ECB/NoPadding 16 不支援
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding
32 16
AES/OFB/NoPadding 16 原始資料長度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支援
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16
多了一行。。。 微笑 奮鬥

程式碼如下:

package com.test;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * AES加密和解密工具類
 * 
 * @author zhancy
 */
public class AESUtilFinal {

	// AES密碼器
	private static Cipher cipher;

	// 字串編碼
	private static final String KEY_CHARSET = "UTF-8";

	// 演算法方式
	private static final String KEY_ALGORITHM = "AES";

	// 演算法/模式/填充
	private static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";

	// 私鑰大小128/192/256(bits)位 即:16/24/32bytes,暫時使用128,如果擴大需要更換java/jre裡面的jar包
	private static final Integer PRIVATE_KEY_SIZE_BIT = 128;
	private static final Integer PRIVATE_KEY_SIZE_BYTE = 16;


	public static void main(String[] args) {
		String secretKey = "1234567812345678";
		String text = "test123測試";
		String miwen = encrypt(secretKey, text);
		System.out.println("密文為:" + miwen);
		String mingwen = decrypt(secretKey, miwen);
		System.out.println("明文為:" + mingwen);
	}
	
	/**
	 * 加密
	 * 
	 * @param secretKey
	 *            金鑰:加密的規則 16位
	 * @param plainText
	 *            明文:要加密的內容
	 * @return cipherText
	 * 			     密文:加密後的內容,如有異常返回空串:""
	 */
	public static String encrypt(String secretKey, String plainText) {
		if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
			throw new RuntimeException("AESUtil:Invalid AES secretKey length (must be 16 bytes)");
		}
		
		// 密文字串
		String cipherText = "";
		try {
			// 加密模式初始化引數
			initParam(secretKey, Cipher.ENCRYPT_MODE);
			// 獲取加密內容的位元組陣列
			byte[] bytePlainText = plainText.getBytes(KEY_CHARSET);
			// 執行加密
			byte[] byteCipherText = cipher.doFinal(bytePlainText);
			cipherText = Base64.encodeBase64String(byteCipherText);
		} catch (Exception e) {
			throw new RuntimeException("AESUtil:encrypt fail!", e);
		}
		return cipherText;
	}

	/**
	 * 解密
	 * 
	 * @param secretKey
	 *            金鑰:加密的規則 16位
	 * @param cipherText
	 *            密文:加密後的內容,即需要解密的內容
	 * @return plainText 
	 * 		  	      明文:解密後的內容即加密前的內容,如有異常返回空串:""
	 */
	public static String decrypt(String secretKey, String cipherText) {
		if (secretKey.length() != PRIVATE_KEY_SIZE_BYTE) {
			throw new RuntimeException("AESUtil:Invalid AES secretKey length (must be 16 bytes)");
		}
		
		// 明文字串
		String plainText = "";
		try {
			initParam(secretKey, Cipher.DECRYPT_MODE);
			// 將加密並編碼後的內容解碼成位元組陣列
			byte[] byteCipherText = Base64.decodeBase64(cipherText);
			// 解密
			byte[] bytePlainText = cipher.doFinal(byteCipherText);
			plainText = new String(bytePlainText, KEY_CHARSET);
		} catch (Exception e) {
			throw new RuntimeException("AESUtil:decrypt fail!", e);
		}
		return plainText;
	}
	
	/**
	 * 初始化引數
	 * @param secretKey
	 * 			 	金鑰:加密的規則 16位
	 * @param mode
	 * 				加密模式:加密or解密
	 */
	public static void initParam(String secretKey, int mode) {
		try {
			// 防止Linux下生成隨機key
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
	        secureRandom.setSeed(secretKey.getBytes());
			// 獲取key生成器
			KeyGenerator keygen = KeyGenerator.getInstance(KEY_ALGORITHM);
			keygen.init(PRIVATE_KEY_SIZE_BIT, secureRandom);
			
			// 獲得原始對稱金鑰的位元組陣列
			byte[] raw = secretKey.getBytes();
			
			// 根據位元組陣列生成AES內部金鑰
			SecretKeySpec key = new SecretKeySpec(raw, KEY_ALGORITHM);
			// 根據指定演算法"AES/CBC/PKCS5Padding"例項化密碼器
			cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
			IvParameterSpec iv = new IvParameterSpec(secretKey.getBytes());
			System.out.println("iv:" + new String(iv.getIV()));
			cipher.init(mode, key, iv);
		} catch (Exception e) {
			throw new RuntimeException("AESUtil:initParam fail!", e);
		}
	}

}