Java 實現(xiàn)一個AES/ECB/PKCS5Padding
加解密算法工具類
- 加密算法: AES
- 模式: ECB
- 補碼方式: PKCS5Padding
1. 工具類
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
/**
* Created by @author yihui in 19:12 20/1/2.
*/
@Slf4j
public class EncryptUtil {
private static final String KEY_ALGORITHM = "AES";
/**
* 算法/模式/補碼方式
*/
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String CODE = "utf-8";
@Setter
@Getter
public static String encryptKey;
public static String encrypt(String content) {
return encrypt(content, encryptKey);
}
/**
* 加密
*
* @param content
* @param key
* @return
* @throws Exception
*/
public static String encrypt(String content, String key) {
try {
byte[] encrypted = encrypt2bytes(content, key);
return Base64Utils.encodeToString(encrypted);
} catch (Exception e) {
log.error("failed to encrypt: {} of {}", content, e);
return null;
}
}
public static byte[] encrypt2bytes(String content, String key) {
try {
byte[] raw = key.getBytes(CODE);
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(content.getBytes(CODE));
} catch (Exception e) {
log.error("failed to encrypt: {} of {}", content, e);
return null;
}
}
public static String decrypt(String content) {
try {
return decrypt(content, encryptKey);
} catch (Exception e) {
log.error("failed to decrypt: {}, e: {}", content, e);
return null;
}
}
/**
* 解密
*
* @param content
* @param key
* @return
* @throws Exception
*/
public static String decrypt(String content, String key) throws Exception {
return decrypt(Base64Utils.decodeFromString(content), key);
}
public static String decrypt(byte[] content, String key) throws Exception {
if (key == null) {
log.error("AES key should not be null");
return null;
}
byte[] raw = key.getBytes(CODE);
SecretKeySpec keySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
try {
byte[] original = cipher.doFinal(content);
return new String(original, CqODE);
} catch (Exception e) {
log.error("failed to decrypt content: {}/ key: {}, e: {}", content, key, e);
return null;
}
}
}
請注意上面的實現(xiàn),提供了兩種方式
- 一個是AES加密之后使用base64編碼輸出,對應的是解密base64編碼的數(shù)據(jù)
- 一個是AES加密之后筏勒,直接返回字節(jié)數(shù)組偿渡;也是直接解碼字節(jié)數(shù)組
2. 測試case
我們提供了兩個加密的文件牵寺,用于解密使用绰更;
base64加解密
@Test
public void testEncrypt() throws Exception {
String abc = "Hello, 一灰灰Blog!";
String key = "JC66fRd3wj85k8Hr";
String out = EncryptUtil.encrypt(abc, key);
System.out.println(out);
System.out.println(EncryptUtil.decrypt(out, key));
}
輸出結(jié)果如:
TKrN7VKrqsAQ4JqygeHOlG21Sd3IRJ3Y11k4kOdOG4s=
Hello, 一灰灰Blog!
字節(jié)數(shù)組加解密
@Test
public void testEncryptByte() throws Exception {
String abc = "Hello, 一灰灰Blog!";
String key = "JC66fRd3wj85k8Hr";
byte[] out = EncryptUtil.encrypt2bytes(abc, key);
System.out.println(new String(out));
System.out.println(EncryptUtil.decrypt(out, key));
}
輸出結(jié)果如:
// 加密的字節(jié)數(shù)組惰帽,就是亂碼... 你沒看錯
L???R?????????Δm?I??D???Y8??N??
Hello, 一灰灰Blog!
為什么有上面兩種區(qū)別?
如果我們將加密后的字節(jié)數(shù)組与殃,直接 new String()
獲得一個字符串,然后解密這個字符串碍现,會發(fā)現(xiàn)解密失敗哦
簡單修改一下上面的測試用例
@Test
public void testEncryptByte() throws Exception {
String abc = "Hello, 一灰灰Blog!";
String key = "JC66fRd3wj85k8Hr";
byte[] out = EncryptUtil.encrypt2bytes(abc, key);
String enc = new String(out, "utf-8");
System.out.println(enc);
System.out.println(EncryptUtil.decrypt(enc.getBytes("utf-8"), key));
}
執(zhí)行之后幅疼,發(fā)現(xiàn)解密失敗
image
為啥會出現(xiàn)這樣情況呢?
-
enc = new String(out, "utf-8")
與enc.getBytes("utf-8")
字節(jié)數(shù)組轉(zhuǎn)字符串昼接; 字符串轉(zhuǎn)字節(jié)數(shù)組這兩個過程會導致最終生成的字節(jié)數(shù)組爽篷,與原始的不一致!!!
解密遠程資源的case
最后給一個解密遠程加密的二進制文件的實例case
private void binKey(String uri, String key) throws Exception {
// 這個文件是沒有base64編碼,直接上傳的二進制
URL url = new URL(uri);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
int lenth = connection.getContentLength();
byte[] out = new byte[lenth];
stream.read(out);
stream.close();
String ans = decrypt(out, key);
System.out.println(ans);
}
public void testDe() throws Exception {
String key = "5JRHMJn8xHnMDRXa";
binKey("http://q8rnsprw0.bkt.clouddn.com/mwzz/b0001", key);
}
II. 其他
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的個人博客慢睡,記錄所有學習和工作中的博文逐工,歡迎大家前去逛逛
2. 聲明
盡信書則不如,以上內(nèi)容漂辐,純屬一家之言泪喊,因個人能力有限,難免有疏漏和錯誤之處髓涯,如發(fā)現(xiàn)bug或者有更好的建議袒啼,歡迎批評指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 掃描關注
一灰灰blog
QrCode