一、AES和RSA
1、对称加密和非对称加密简介
目前常见的加密方式是有两种,一种是对称加密(AES为代表),一种是非对称加密(RSA为代表)。
对称加密只有一个秘钥,加密和解密都是用同一个秘钥,所以叫做对称加密。
非对称加密有两个秘钥,一个是公钥,一个是私钥。非对称的特点在于,公钥加密的私钥可以解密,但私钥加密的,公钥解不出来,只能验证是否由私钥进行加密
?这样可以保证就算有人拿到公钥,也解密不出私钥加密后的信息,公钥可以在网上安全的传输。而且公钥可以验证这个密文是不是由私钥加密出来的,也起到了验证的作用,一举两得。
一般公钥加密私钥解密的情况是用来传输数据,私钥加密公钥验证的情况是用来验证签名。
2、组合使用
对称加密(AES)的优势在于加密较快,但劣势在于秘钥一旦给出去就不安全了。非对称加密(RSA)的优势在于安全,就算提供公钥出去,别人也解密不了数据,但加密速度较慢。
实际使用的过程中常常将两者组合使用(AES+RSA):
1、先生成一个随机AES秘钥字符串。
2、使用RSA公钥加密AES秘钥,然后再用AES秘钥加密真正的内容。
3、把skey=加密的AES秘钥,body=AES秘钥加密的内容传过去。
4、对面使用RSA私钥解密AES秘钥,然后用AES秘钥解密出内容。
这样可以安全的传输AES秘钥,避免了RSA加密的慢速度。
3、转换模式
在实际使用的过程中,往往提供过来的加密方法是这样一个格式 (AES/ECB/PKCS5Padding),这个格式为?算法/工作模式/填充模式,下面简单介绍一下:
算法:是指具体使用到的算法名称,比如“AES”,“RSA”,“SHA-256”。
工作模式:是指分组密码,工作模式需要加密的原始信息分成固定长度的数据块,然后用分组密码对这些数据块进行加密。目前有“ECB”,“CBC”,“CFB”,“OFB”,“CTR”五种工作模式。
填充模式:分组密码只能加密长度等于分组长度的单块数据,所以如果数据长度不足,就需要填充数据到匹配的长度。填充算法有 “NoPadding”, “PKCS#5”, “PKCS#7”, “ISO 10126”, “ANSI X9.23”和“ZerosPadding”。
常见的是“NoPadding”, “PKCS#5”, “PKCS#7”,“ZerosPadding”。
NoPadding:不填充。
PKCS#7 &? PKCS#5:缺几个字符就填几个缺的字节数,比如 |DD DD DD DD DD 03 03 03| (差3个字节,填3个03)。PKCS#5 和?PKCS#7 是完全一样的,不同的地方在于?PKCS#5 限制了块大小为 8 bytes 而??PKCS#7 没有限定。
ZerosPadding:全部填充0,无论缺多少都填0。
二、在Java 中的使用
1、AES工具类
package com.jing.app.common.encryption;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import java.security.Key;
/**
* @author jing
* @version 1.0
* @desc AES 加解密工具类
* @date 2021/2/7 0007 11:28
**/
public class AESUtils {
public static String MODE = "AES/ECB/PKCS5Padding";
public static String KEY_ALGORITHM = "AES";
public static String CHARSET = "utf-8";
private static final int KEY_SIZE = 128;
/**
* 获取密钥
*
* @return
* @throws Exception
*/
private static Key getKey() throws Exception {
// 实例
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
// AES
kg.init(KEY_SIZE);
// 生成密钥
SecretKey secretKey = kg.generateKey();
return secretKey;
}
/**
* 加密
*
* @param content 内容
* @param key 秘钥
* @return 加密后的数据
*/
public static String encrypt(String content, String key) throws Exception {
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(MODE);
// 初始化秘钥
SecretKeySpec sks = new SecretKeySpec(key.getBytes(CHARSET), KEY_ALGORITHM);
// 初始化加密类
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] encrypt = cipher.doFinal(content.getBytes());
// 这一步非必须,是因为二进制数组不方便传输,所以加密的时候才进行base64编码
encrypt = Base64.getEncoder().encode(encrypt);
// 转成字符串返回
return new String(encrypt, CHARSET);
}
/**
* 解密数据
*
* @param content 内容
* @param key 秘钥
* @return 数据
*/
public static String decrypt(String content, String key) throws Exception{
// 替换base64里的换行,这一步也非必须,只是有些情况base64里会携带换行符导致解码失败
content = content.replaceAll("[\\n\\r]", "");
// base64 解码,跟上面的编码对称
byte[] data = Base64.getDecoder().decode(content.getBytes(CHARSET));
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(MODE);
// 初始化秘钥
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), KEY_ALGORITHM);
// 初始化类
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 解密
byte[] result = cipher.doFinal(data);
// 返回解密后的内容
return new String(result);
}
}
2、RSA工具类
这里引入了一个jar 包用来生成公钥和私钥
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.59</version>
</dependency>
package com.jing.app.common.encryption;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @author jing
* @version 1.0
* @desc RSA
* @date 2021/9/10 0010 10:20
**/
public class RSAUtils {
private final static String ASYMMETRIC_ALGORITHM = "RSA/None/PKCS1Padding";
/**
* 初始化key pair
*
* @return KeyPair
*/
private static KeyPair genrateKey() {
try {
// 添加provider
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
// 随机数用于安全加密
SecureRandom random = new SecureRandom();
// 初始化秘钥
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", provider);
generator.initialize(2048, random);
return generator.generateKeyPair();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 解密数据
*
* @param arg 需要解密的字符串
* @param privateKeyStr base64加密的秘钥
* @return 解密后的字符串
*/
public static String decryptBase64(String arg, String privateKeyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
// 初始化私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 进行解密
return decryptBase64(arg, privateKey);
}
/**
* 解密数据
*
* @param arg 需要解密的字符串
* @return 解密后的字符串
*/
public static String decryptBase64(String arg, PrivateKey privateKey) throws Exception {
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
// Cipher 提供加密和解密功能
Cipher cipher = Cipher.getInstance(ASYMMETRIC_ALGORITHM, provider);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 解密数据
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(arg));
return new String(plainText);
}
/**
* 加密数据
*
* @param arg 需要解密的字符串
* @param pubKeyStr 公钥base64编码后
* @return 解密后的字符串
*/
public static String encryptBase64(String arg, String pubKeyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(pubKeyStr);
// 初始化公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
return encryptBase64(arg, publicKey);
}
/**
* @param arg 待加密数据字节数
* @param publicKey 公钥
* @return 加密后数据
*/
public static String encryptBase64(String arg, PublicKey publicKey) throws Exception {
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
Cipher cipher = Cipher.getInstance(ASYMMETRIC_ALGORITHM, provider);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypt = cipher.doFinal(arg.getBytes());
return Base64.getEncoder().encodeToString(encrypt);
}
public static void main(String[] args) throws Exception {
KeyPair keyPair = genrateKey();
String privateKeyStr = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
String publicKeyStr = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
System.out.println(String.format("privateKey: %s", privateKeyStr));
System.out.println(String.format("publicKey: %s", publicKeyStr));
String content = "abcde";
String cipherText = encryptBase64(content, publicKeyStr);
String decryptText = decryptBase64(cipherText, privateKeyStr);
System.out.println(String.format("解密数据:%s", decryptText));
}
}
到此结束。
参考文章:(有兴趣可以读一下扩展)
为什么使用 Java Cipher 要指定转换模式? | Bigzuo's Blog
??????AES加密(3):AES加密模式与填充 - 知乎
|