移動(dòng)端網(wǎng)站建設(shè)的方案用asp做的網(wǎng)站
加解密概念
- 加密
- AES加密
- 填充模式
- 加密模式
- 示例
加密
通過(guò)一系列計(jì)算將明文轉(zhuǎn)換成一個(gè)密文。
加密和解密的對(duì)象通常是字節(jié)數(shù)組(有的語(yǔ)言動(dòng)態(tài)數(shù)組類比切片)
加密后的數(shù)據(jù),可能有很多是不可讀字符。通常會(huì)將其轉(zhuǎn)換為可見(jiàn)的字符串。
- 直接將字節(jié)數(shù)組轉(zhuǎn)為16進(jìn)制的字符串(一個(gè)字節(jié)8位,4位表示一個(gè)16進(jìn)制數(shù)據(jù),因此轉(zhuǎn)換后的數(shù)據(jù)是轉(zhuǎn)換前字節(jié)數(shù)組的2倍長(zhǎng)度,之所以采用16進(jìn)制是因?yàn)槠淇雌饋?lái)更加緊湊)
- 將字節(jié)數(shù)組進(jìn)行Base64編碼
AES加密
算法相關(guān)常量在類Cipher的注釋中有說(shuō)明。
AES是一種對(duì)稱加密算法。具體加密方式是以16字節(jié)為一塊進(jìn)行分塊加密。
如果明文長(zhǎng)度不是16的倍數(shù),那么則需要進(jìn)行填充,這就引申出了填充模式。
其密鑰長(zhǎng)度要求為 128 位(16字節(jié))、192(24字節(jié)) 位和 256(32字節(jié)) 位,三種,越長(zhǎng)越安全,速度越慢
填充模式
常用填充模式:PKCS#5、PKCS#7。在Java中已經(jīng)將其行為統(tǒng)一了。
在模式的定義上:
- PKCS#5用于8字節(jié)塊為單位的加密場(chǎng)景
- PKCS#7用于非8字節(jié)塊為單位的加密場(chǎng)景,在現(xiàn)代應(yīng)用中更通用
但是它們的實(shí)現(xiàn)方式都是類似的,剩余的字節(jié)數(shù)組長(zhǎng)度距離加密塊差幾個(gè)字節(jié),就填充幾個(gè)字節(jié),而且每一位值也是這個(gè)長(zhǎng)度
比如剩下5字節(jié),在AES中,是以16字節(jié)為單位,差11個(gè)字節(jié)。那么就會(huì)在這5個(gè)字節(jié)后面加11個(gè)項(xiàng),而且每一項(xiàng)的值都是11
加密模式
AES中現(xiàn)在用得最多的就是CBC模式.
這種方式在加密一個(gè)塊時(shí),需要使用上一個(gè)塊加密后的數(shù)據(jù)與當(dāng)前明文塊進(jìn)行異或運(yùn)算。也就是說(shuō)每一個(gè)塊的加密都不一樣。
這就有一個(gè)點(diǎn),第一個(gè)加密塊前面沒(méi)有數(shù)據(jù)塊,所以我們需要指定一個(gè)初始向量(有的也稱之為偏移量)(其長(zhǎng)度就是一個(gè)數(shù)據(jù)塊的長(zhǎng)度)
示例
- 加密(如果使用的是CBC模式,則需要指定初始向量(說(shuō)白了就是手動(dòng)創(chuàng)建加密塊,因此也是長(zhǎng)度為16的字節(jié)數(shù)組),)
- 生成iv(如果是CBC模式,則需要。長(zhǎng)度與加密塊一致(16字節(jié)))
// 如果是CBC模式 首先生成iv向量(本質(zhì)就是一個(gè)長(zhǎng)度為16的字節(jié)數(shù)組,隨便怎么構(gòu)建)
// 可以自行創(chuàng)建16字節(jié)的數(shù)組,但推薦生成隨機(jī)iv
// 自行構(gòu)建
String ivStr = "1111111111111111"
byte[] iv = ivStr.getBytes(StandardCharsets.UTF_8);// 生成隨機(jī)iv
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
- 生成密鑰
AES密鑰支持16字節(jié)、24字節(jié)、32字節(jié)(越長(zhǎng)越安全,運(yùn)算速度越慢)
與iv類似,可以自定義密鑰串,然后構(gòu)建密鑰對(duì)象,也可以直接生成指定長(zhǎng)度的隨機(jī)密鑰
// 指定密鑰串,構(gòu)建密鑰對(duì)象
String key = "0111111111111111";
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");// 生成指定長(zhǎng)度的密鑰
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
- 創(chuàng)建加密器,在java中通過(guò)聚合名稱指定多項(xiàng)配置(AES/CBC/PKCS7Padding)它制定了加密算法、加密模式、填充模式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
- 初始化加密器。指定加密還是解密,密鑰,初始向量
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
- 執(zhí)行加解密。參數(shù)和返回都是字節(jié)數(shù)組
byte[] resByte = cipher.doFinal(s.getBytes(StandardCharsets.UTF_8));
- 以上加密結(jié)果可能會(huì)有一些不可讀的字符,因此為了方便查看,存儲(chǔ),傳輸?shù)?#xff0c;我們常常將字節(jié)數(shù)組轉(zhuǎn)為16進(jìn)制字符串或者Base64進(jìn)行存儲(chǔ),傳輸。
不僅是加密結(jié)果。隨的的生成iv字節(jié)數(shù)組,隨機(jī)生成密鑰字節(jié)數(shù)組 等包含不可讀字符的字節(jié)數(shù)組都可以采用這種方式進(jìn)行分發(fā),存儲(chǔ)
- Base64編解碼
// 字節(jié)數(shù)組編碼為Base64字符串
String encodedKey = Base64.getEncoder().encodeToString(encoded);// Base64字符串解碼為字節(jié)數(shù)組
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
- 16進(jìn)制編解碼
// 字節(jié)數(shù)組編碼為16進(jìn)制字符串
public String byte2Hex(byte[] bytes) {int len = bytes.length;StringBuilder builder = new StringBuilder();for (int i = 0, j = 0; i < len; i++) {builder.append(Integer.toHexString((0xF0 & bytes[i]) >>> 4));builder.append(Integer.toHexString(0x0F & bytes[i]));}return builder.toString();
}// 16進(jìn)制字符串轉(zhuǎn)字節(jié)數(shù)組
public byte[] hexToBytes(String s) {byte[] bytes = new byte[(s.length() + 1) >> 1];for (int i = 0, j = 0; i < bytes.length; i++) {int left = Character.digit(s.charAt(j++), 16) << 4;left = left | Character.digit(s.charAt(j++), 16);bytes[i] = (byte) (left & 0xff);}return bytes;
}