AES-GCM模式代码演示
2022-12-19 本文已影响0人
滨岩
对称加密与加密模式相关API
image.png代码演示
GCMAes
package com.deepway.cryptology;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
/**
* AES-GCM
* @author huyanbing
* @create 2022/12/18 1:40 下午
*/
public class GCMAes extends BaseCipher {
// AES-GCM parameters
/**
* The length in bytes of IV
* ivLen>=1
* But 96-bit IV values is recommended
*/
// 12 bytes
private static final int GCM_NONCE_LENGTH = 96 >> 3;
/**
* The length
* in bits
* of Tag <code>T</code>;
* tLen must
* be one of {
* 128, 120, 112, 104, 96
* }
* <p>
* GCM tag 长度 我们一般取最长 128 也是比较推荐的
*/
private static final int GCM_TAG_LENGTH = 128;// 16 bytes
private Cipher cipher;
//GCM的 IV 比较特殊
private GCMParameterSpec iv;
/**
* 构造函数
*
* @param keyLen
* @param plainKey
*/
public GCMAes(int keyLen, byte[] plainKey) {
super(keyLen, plainKey);
}
/**
* 初始化
*
* @param mode
* @param nonce
* @throws GeneralSecurityException
*/
public void initialize(int mode, byte[] nonce) throws GeneralSecurityException {
//如果用户没有给 nonce随机数的值,就自己生产初始化的随机值
if (nonce == null) {
nonce = new byte[GCM_NONCE_LENGTH];
new SecureRandom().nextBytes(nonce);
}
//构造GCMParameterSpec
//一个是 tag 长度 ,一个是初始化向量随机数 nonce 然后构成出IV
//GCM模式只能使用GCMParameterSpec
iv = new GCMParameterSpec(GCM_TAG_LENGTH, nonce);
try {
this.cipher = Cipher.getInstance("AES/GCM/PKCS5PADDING");
//3-args 初始化 三个参数 mode(加密模式、解密模式) key:密钥 iv:初始向量
this.cipher.init(mode, key, iv);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加密
* <p>
* <p>
* 两个参数 Input 消息本体
* aad 附加认证数据 aad 数据
*
* @param input
* @param aad
* @return
* @throws Exception
*/
public byte[] encrypt(byte[] input, byte[] aad) throws Exception {
//first aad
if (aad != null) {
//如果有aad 认证数据,需要先添加aad认证数据,然后才能加密消息本体数据
cipher.updateAAD(aad);
}
// cipher.update(input);
//如果 input 很小,直接调用cipher.doFinal(input) 就可以了
//jdk9 返回的 gcm->cipher-text,Tag 两个值 一个是加密密文,一个是认证的 128位的tag
return cipher.doFinal(input);
}
public static void main(String[] args) throws Exception {
byte[] plainText = "Hello AES-GCM ..11111111111111111111".getBytes();
System.out.println("GCM plaintText:");
System.out.println(new String(plainText));
byte[] aad = "My name is Bob".getBytes();
GCMAes gcmAlice = new GCMAes(128, null);
gcmAlice.initialize(Cipher.ENCRYPT_MODE, null);
byte[] cipherText = gcmAlice.encrypt(plainText, aad);
System.out.println("GCM cipher:");
System.out.println(HexCustomUtil.bytes2hex(cipherText));
//
// {
// //for RPC ,serializing,persistence
// new Object() {
// byte[] aad;
// byte[] iv;
// byte[] cipherText;
// };
// }
GCMAes gcmBob = new GCMAes(128, gcmAlice.exportKey());
gcmBob.initialize(Cipher.DECRYPT_MODE, gcmAlice.iv.getIV());
byte[] plainText2 = gcmBob.encrypt(cipherText, aad);
System.out.println("GCM plaintText:");
System.out.println(new String(plainText2));
}
}
BaseCipher
package com.deepway.cryptology;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
/**
* @author huyanbing
* @create 2022/12/17 6:44 下午
*/
public class BaseCipher {
private final int keyLen;
protected final SecretKey key;
private String algorithm = "AES";
/**
* 构造函数
*
* @param keyLen
* @param plainKey
*/
public BaseCipher(int keyLen, byte[] plainKey) {
//验证密钥长度是不是合规的
assert keyLen == 128 || keyLen == 192 || keyLen == 256;
this.keyLen = keyLen;
if (plainKey == null) {
key = generateKey();
} else {
key = importKey(plainKey);
}
}
/**
* 生成默认密钥
*
* @return
*/
public SecretKey generateKey() {
try {
//静态工厂模式
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
keyGen.init(keyLen);
SecretKey key = keyGen.generateKey();
return key;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**
* 导出密钥
*
* @return
*/
public byte[] exportKey() {
return key.getEncoded();
}
/**
* 导入密钥
*
* @param plainKey
* @return
*/
public SecretKey importKey(byte[] plainKey) {
//=> keyLen/8
assert plainKey.length == keyLen >> 3;
return new SecretKeySpec(plainKey, algorithm);
}
}
package com.deepway.cryptology;
/**
* @author huyanbing
* @create 2022/12/16 3:29 下午
*/
public class HexCustomUtil {
/*输入16进制byte[]输出16进制字符串*/
public static String byteArrayToHexStr(byte[] byteArray) {
if (byteArray == null) {
return null;
}
char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[byteArray.length * 2];
for (int j = 0; j < byteArray.length; j++) {
int v = byteArray[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static String bytes2hex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
String tmp;
sb.append("[");
for (byte b : bytes) {
// 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制
tmp = Integer.toHexString(0xFF & b);
if (tmp.length() == 1) {
tmp = "0" + tmp;//只有一位的前面补个0
}
sb.append(tmp).append(" ");//每个字节用空格断开
}
sb.delete(sb.length() - 1, sb.length());//删除最后一个字节后面对于的空格
sb.append("]");
return sb.toString();
}
}