文中基于对称加密AES和非对称加密ECC为例,其他对称加密(DES等)和非对称加密(RSA等)同理。
1. 获取密钥
首先我们要学会获得算法密钥,这是最基础的,有了密钥才能去加密解密签名验签。
首先以AES为例:
1.1 获取AES密钥
-
系统生成
public static SecretKey generateKey(int keySize) throws NoSuchAlgorithmException { // 指定所用算法为AES,同理可以将"AES"换成"DES" KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // jdk中AES密钥长度只能是三种:128、192、256 bits。加密速度依次递减,安全性依次递增 keyGen.init(keySize); return keyGen.generateKey(); }
-
自定义
private static SecretKeySpec String2Key(String password, int keySize) throws NoSuchAlgorithmException { // 先对password取哈希值,因为后面我们要截取 MessageDigest digester = MessageDigest.getInstance("SHA-256"); digester.update(password.getBytes(StandardCharsets.UTF_8)); byte[] passwordHash = digester.digest(); // 密钥长度依然受限,因此需要截取。keySize可以是16、24、32,分别对应128、192、256 bits的长度 byte[] pwd = Arrays.copyOfRange(passwordHash, 0, keySize); return new SecretKeySpec(pwd, "AES"); }
1.2 获取EC密钥
public static KeyPair initKey(int keySize) throws Exception {
// 指定所用算法为EC,同理可以将"EC"换成"RSA"
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
// 设置密钥长度, 常见ECC密钥长度:256、384、521
keyPairGen.initialize(256);
// 生成密钥对
return keyPairGen.generateKeyPair();
}
// 从密钥对中获得公私钥
PublicKey publicKey = keypair.getPublic();
PrivateKey privateKey = keypair.getPrivate();
2. 加解密
javax.crypto.Cipher类是java自带的用来加解密的类,详情见官方文档:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/Cipher.html
使用主要分为三步:
- 获取算法实例(getInstance方法)
- 初始化(init方法)
- 加密或解密(doFinal方法)
2.1 AES加解密
获取算法实例用于加解密:
private static Cipher cipher; // 单例设计模式
static {
try {
cipher = Cipher.getInstance("AES");
} catch (Exception e) {
e.printStackTrace();
}
}
加密:
public static byte[] encrypt(String plainText, Key secretKey) throws Exception {
// 因为明文和密码都是中文,所以这里用了UTF_8字符集
byte[] data = plainText.getBytes(StandardCharsets.UTF_8);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
解密:
public static byte[] decrypt(byte[] ciphertext, Key secretKey) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(ciphertext);
}
2.2 ECIES加解密
因为Java本地库不支持ECIES,所以我们需要引入第三方库,这里以BC库为例:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.76</version>
</dependency>
然后我们在使用BC库前,需要将BC注册为提供者,我这里通过静态代码块进行注册:
// 这段代码块一定要放在最前面,因为注册是最基础的必须操作
static {
Security.addProvider(new BouncyCastleProvider());
}
获取算法实例用于加解密:
private static Cipher cipher; // 单例设计模式
static {
try {
// 指定用BC库来支持ECIES,因此在这之前确保BC已经注册
cipher = Cipher.getInstance("ECIES", "BC");
} catch (Exception e) {
e.printStackTrace();
}
}
加密:
public static byte[] encryptByPublicKey(String data, PublicKey publicKey) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, publicKey, iesParameterSpec);
return cipher.doFinal(data.getBytes());
}
解密:
public static byte[] decryptByPrivateKey(byte[] ciphertext, PrivateKey privateKey) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, privateKey, iesParameterSpec);
return cipher.doFinal(ciphertext);
}
其中iesParameterSpec
参数是IES参数,加解密的IES参数必须相同。
我因为是做实验,便将它设置为全局常量:
public static IESParameterSpec iesParameterSpec = new IESParameterSpec(
null,
null,
256,
256,
null,
false);
3. 签名和验签
java.security.Signature类是java自带的用来签名和验签的类,详情见官方文档:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/Signature.html#sign()
使用主要分为四步:
- 获得算法实例(getInstance方法)
- 初始化(init方法)
- 更新(update方法)
- 签名或验签(sign或verify方法)
3.1 ECDSA数字签名
首先获取算法实例:
private static Signature signer; // 单例设计模式
static {
try {
signer = Signature.getInstance("SHA256withECDSA");
} catch (Exception e) {
e.printStackTrace();
}
}
签名:
public static byte[] sign(String message, PrivateKey privateKey) throws Exception {
signer.initSign(privateKey);
signer.update(message.getBytes(StandardCharsets.UTF_8));
return signer.sign();
}
验签:
public static boolean verifySignature(String message, byte[] signature, PublicKey publicKey) throws Exception {
signer.initVerify(publicKey);
signer.update(message.getBytes(StandardCharsets.UTF_8));
return signer.verify(signature);
}
如果对你有用请一件三连😊