昨天遇到一個加密的東西项钮,解決問題的過程異常艱辛
要求是
AES256-CBC-PKCS7Padding加密 base64輸出
代碼老大之前有給過我一份棒厘,讓我參考一下檩互。
毫無疑問的粹胯,我拿來就跑demo蓖柔,當(dāng)時是在android studio上跑,毫無疑問失敗了风纠,jdk的版本用的是開源的况鸣,不是oracle的。demo也沒有好好看
當(dāng)時電腦有點卡竹观,鍵盤好像也有點問題镐捧,拷貝東西的時候不多按幾下c鍵,東西可能拷貝不下來臭增。
遇到的第一個問題是 java.security.InvalidKeyException:illegal Key Size
解決方案:我換了個jdk懂酱,然后下載了local_policy.jar和US_export_policy.jar覆蓋本地的包
遇到的第二個問題是生成的密文不對,解不出來速址。然后懷疑給的代碼有問題玩焰,網(wǎng)上找各種版本。Google芍锚,stackoverflow昔园。他們建議是把PKCS7Padding換成PKCS5Padding蔓榄。這個和需求有悖。那就只能找別人實現(xiàn)了的默刚,這個相當(dāng)耗時甥郑,時間久了,感覺不對就去老大那里看他那加解密是怎么做的荤西。他那邊的做法和我一樣只是不同的語言實現(xiàn)的澜搅。這時候我回到原來的代碼上,一陣狂改邪锌,感覺沒用的全都注釋勉躺。老大看過看我的代碼,發(fā)現(xiàn)上面的key觅丰,和iv是一樣的饵溅,我當(dāng)時就懵逼了
最后一個問題:不多說,自己犯傻
深深鄙視貼代碼貼一半的人
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//這個類可以自己寫妇萄,base64網(wǎng)上一大堆 本文用的是apache的 commons-codec
import org.apache.commons.codec.binary.Base64;
/**
* 說明:異常java.security.InvalidKeyException:illegal Key Size的解決方案
* <ol>
* <li>在官方網(wǎng)站下載JCE無限制權(quán)限策略文件(JDK7的下載地址:
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124
* .html
* jdk 8
* http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
* </li>
* <li>下載后解壓蜕企,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
* <li>如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件</li>
* <li>如果安裝了JDK冠句,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件</li>
* </ol>
*/
public class ASE {
static Charset CHARSET = Charset.forName("utf-8");
Base64 base64 = new Base64();
byte[] aesKey = "1q2w3e4rfdsaffdsa343234d".getBytes();
byte[] iv = "0316030405060709".getBytes();
static int BLOCK_SIZE = 32;
// 生成4個字節(jié)的網(wǎng)絡(luò)字節(jié)序
byte[] getNetworkBytesOrder(int sourceNumber) {
byte[] orderBytes = new byte[4];
orderBytes[3] = (byte) (sourceNumber & 0xFF);
orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
return orderBytes;
}
// 還原4個字節(jié)的網(wǎng)絡(luò)字節(jié)序
int recoverNetworkBytesOrder(byte[] orderBytes) {
int sourceNumber = 0;
for (int i = 0; i < 4; i++) {
sourceNumber <<= 8;
sourceNumber |= orderBytes[i] & 0xff;
}
return sourceNumber;
}
// 隨機(jī)生成16位字符串
String getRandomStr() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 16; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 對明文進(jìn)行加密.
*
* @param text
* 需要加密的明文
* @return 加密后base64編碼的字符串
* @throws AesException
* aes加密失敗
*/
String encrypt(String text) throws AesException {
ByteGroup byteCollector = new ByteGroup();
byte[] randomStrBytes = getRandomStr().getBytes(CHARSET);
byte[] textBytes = text.getBytes(CHARSET);
byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
// randomStr + networkBytesOrder + text
byteCollector.addBytes(randomStrBytes);
byteCollector.addBytes(networkBytesOrder);
byteCollector.addBytes(textBytes);
// ... + pad: 使用自定義的填充方式對明文進(jìn)行補(bǔ)位填充
byte[] padBytes = encode(byteCollector.size());
byteCollector.addBytes(padBytes);
// 獲得最終的字節(jié)流, 未加密
byte[] unencrypted = byteCollector.toBytes();
try {
// 設(shè)置加密模式為AES的CBC模式
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec ivs = new IvParameterSpec(iv, 0, 16);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivs);
// 加密
byte[] encrypted = cipher.doFinal(unencrypted);
// 使用BASE64對加密后的字符串進(jìn)行編碼
String base64Encrypted = base64.encodeToString(encrypted);
return base64Encrypted;
} catch (Exception e) {
e.printStackTrace();
throw new AesException(AesException.EncryptAESError);
}
}
/**
* 對密文進(jìn)行解密.
*
* @param text
* 需要解密的密文
* @return 解密得到的明文
* @throws AesException
* aes解密失敗
*/
String decrypt(String text) throws AesException {
byte[] original;
try {
// 設(shè)置解密模式為AES的CBC模式
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec ivs = new IvParameterSpec(Arrays.copyOfRange(iv, 0, 16));
cipher.init(Cipher.DECRYPT_MODE, key_spec, ivs);
// 使用BASE64對密文進(jìn)行解碼
byte[] encrypted = Base64.decodeBase64(text);
// 解密
original = cipher.doFinal(encrypted);
} catch (Exception e) {
e.printStackTrace();
throw new AesException(AesException.DecryptAESError);
}
String xmlContent;
try {
// 去除補(bǔ)位字符
byte[] bytes = decode(original);
// 分離16位隨機(jī)字符串,網(wǎng)絡(luò)字節(jié)序和AppId
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
int xmlLength = recoverNetworkBytesOrder(networkOrder);
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
} catch (Exception e) {
e.printStackTrace();
throw new AesException(AesException.IllegalBuffer);
}
return xmlContent;
}
/**
* 獲得對明文進(jìn)行補(bǔ)位填充的字節(jié).
*
* @param count
* 需要進(jìn)行填充補(bǔ)位操作的明文字節(jié)個數(shù)
* @return 補(bǔ)齊用的字節(jié)數(shù)組
*/
static byte[] encode(int count) {
// 計算需要填充的位數(shù)
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0) {
amountToPad = BLOCK_SIZE;
}
// 獲得補(bǔ)位所用的字符
char padChr = chr(amountToPad);
String tmp = new String();
for (int index = 0; index < amountToPad; index++) {
tmp += padChr;
}
return tmp.getBytes(CHARSET);
}
/**
* 刪除解密后明文的補(bǔ)位字符
*
* @param decrypted
* 解密后的明文
* @return 刪除補(bǔ)位字符后的明文
*/
static byte[] decode(byte[] decrypted) {
int pad = (int) decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
/**
* 將數(shù)字轉(zhuǎn)化成ASCII碼對應(yīng)的字符轻掩,用于對明文進(jìn)行補(bǔ)碼
*
* @param a
* 需要轉(zhuǎn)化的數(shù)字
* @return 轉(zhuǎn)化得到的字符
*/
static char chr(int a) {
byte target = (byte) (a & 0xFF);
return (char) target;
}
class ByteGroup {
ArrayList<Byte> byteContainer = new ArrayList<Byte>();
public byte[] toBytes() {
byte[] bytes = new byte[byteContainer.size()];
for (int i = 0; i < byteContainer.size(); i++) {
bytes[i] = byteContainer.get(i);
}
return bytes;
}
public ByteGroup addBytes(byte[] bytes) {
for (byte b : bytes) {
byteContainer.add(b);
}
return this;
}
public int size() {
return byteContainer.size();
}
}
/***
* 抄代碼注意頂部的提示
* key和iv別拷貝的時候仔細(xì)點,別搞錯了
*
* @param args
*/
public static void main(String[] args) {
String str = "11111";
ASE ase = new ASE();
String aseword = null;
try {
aseword = ase.encrypt(str);
System.out.println(aseword);
String decryptString = ase.decrypt(aseword);
System.out.println(decryptString);
} catch (AesException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
exception類
public class AesException extends Exception {
private static final long serialVersionUID = 1L;
public final static int OK = 0;
public final static int EncryptAESError = -40006;
public final static int DecryptAESError = -40007;
public final static int IllegalBuffer = -40008;
private int code;
private static String getMessage(int code) {
switch (code) {
case EncryptAESError:
return "aes加密失敗";
case DecryptAESError:
return "aes解密失敗";
case IllegalBuffer:
return "解密后得到的buffer非法";
default:
return null; // cannot be
}
}
public int getCode() {
return code;
}
AesException(int code) {
super(getMessage(code));
this.code = code;
}
}