目录
1.背景知识:
- 在密码学中,加密算法分为
单向加密
和双向加密
。单向加密
包括MD5、SHA等摘要算法,它们是不可逆的。双向加密
包括对称加密和非对称加密。双向加密是可逆的,存在密文的密钥。对称加密
是指加密和解密使用相同的密钥,包括AES加密、DES加密等。非对称加密
是指加密和解密使用不同的密钥,包括RSA加密等。
2.RSA简介
RSA: 是一种应用比较广泛的
非对称加密算法
,是由MIT工作的三人姓名首字母命名的。
- RSA算法主要依靠分解大素数的复杂性来实现其安全性,由于大素数之积难被分解,因此该密码就难被破解。(素数:质数的别称,一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则成为合数,规定1既不是质数也不是合数。)
- 从1977年提出到现在已经四十余年,经历了各种攻击的考验,普遍认为是目前最优秀的公钥方案之一。
3.RSA原理
3.1 基本原理
RSA基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成为私钥。公钥是可以发布的,供任何人使用,私钥则为自己所有,供解密之用。
3.2 RSA公私钥生成流程
随机找两个质数P和Q,P与Q越大,越安全。(例如:61和53)
计算P和Q的乘积n。(n=61*53=3233,n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。)
计算n的欧拉函数φ(n)。(根据公式 φ(n) = (p-1)(q-1) 算出 φ(3233) = 60 * 52,即3120。)
随机选择一个整数e,条件是1<e<φ(n),且e与φ(n) 互质。(条件是1<e<φ(n),且e与φ(n) 互质。1到3120之间,随机选择了17。)
有一个整数d,可以使得ed 除以φ(n) 的余数为 1。(ed ≡ 1 (mod φ(n)),即17*2753 mode 3120=1)
将n和e封装成公钥,n和d封装成私钥。(n=3233,e=17,d=2753,所以公钥就是:3233,17,私钥就是:3233, 2753。)
3.3 RSA加密
首先对明文进行比特串分组,使得每个分组对应的十进制数小于n,然后依次对每个分组m做一次加密,所有分组的密文构成的序列就是原始消息的加密结果,即m满足0<=m<n,则加密算法为:c=m^e mod n; c为密文,且0<=c<n。
3.4 RSA解密
对于密文0<=c<n,解密算法为:m=c^d mod n。
4.RSA 加密算法的优缺点
优点: RSA算法是国际标准算法,属于主流算法之一,应用广泛,兼容性比较广,能够适用于各种不同的系统之中,不容易出现限制问题。
缺点: RSA算法加密长度为2048位,对于服务器的消耗是比较大的,计算速度也比较慢,效率偏低,一般只适用于处理小量数据。
总结: 尽管RSA加密算法运行消耗大,效率低,但是由于其优秀兼容性和安全性,它依旧是使用最广泛的非对称加密算法。
5.Java实现
注意:RSA加密包含Base64加密、byte[]和十六进制的转换
加解密核心步骤:
1.初始化密钥对。
2.获取公钥字符串: 初始化后的密钥对 -> 取公钥 -> Base64编码 -> 公钥字符串。
3.生成私钥字符串: 初始化后的密钥对 -> 取私钥 -> Base64编码 -> 私钥字符串。
4.公钥加密: 明文 -> 获取公钥(公钥字符串 -> Base64解码 -> 公钥) -> 公钥加密 -> Hex编码 -> 密文。
5.私钥解密: 密文 -> 获取私钥(私钥字符串 -> Base64解码 -> 私钥) -> 私钥解密 -> Hex解码 -> 明文。测试地址:
1.RSA密钥对:http://web.chacuo.net/netrsakeypair
2.RSA公钥加密:http://tool.chacuo.net/cryptrsapubkey
2.RSA私钥解密:http://tool.chacuo.net/cryptrsaprikey
5.1 Maven 依赖
- RSA加密 底层依赖的是 Bouncy Castle 的
bcprov-jdbc15on
库。 - 这个库 包含了基本的密码学算法实现,如:对称加密算法(AES、DES等)、非对称加密算法(RSA、ECC等)、哈希函数(SHA-256、SHA-512等)、消息摘要算法等。
<!-- Bouncy Castle库 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
5.2 RSAUtil.java 源码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* RSA加密工具类
*
* @author ACGkaka
* @since 2021-09-19 19:11:03
*/
public class RSAUtil {
/**
* 密钥类实例化入参
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* Cipher类实例化入参
*/
private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
/**
* 密钥对中公钥映射key
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 密钥对中私钥映射key
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 签名类实例化入参
*/
private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* 初始化密钥对生成器时,指定密钥大小的整数值(安全漏洞,长度至少为2048)
*/
private static final int KEY_PAIR_INIT_SIZE = 2048;
/**
* RSA最大解密密文大小,
* RSA 位数 如果采用1024 上面最大加密和最大解密则须填写: 117 128
* RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
*/
private static final int MAX_DECRYPT_BLOCK = 256;
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 获取公钥字符串
*
* @param keyMap 密钥对
* @return 公钥字符串
* @throws Exception 异常
*/
public static String getPublicKeyStr(Map<String, Object> keyMap) throws Exception {
//获得map中的公钥对象 转为key对象
Key key = (Key) keyMap.get(PUBLIC_KEY);
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
/**
* 获取私钥字符串
*
* @param keyMap 密钥对
* @return 私钥字符串
* @throws Exception 异常
*/
public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception {
//获得map中的私钥对象 转为key对象
Key key = (Key) keyMap.get(PRIVATE_KEY);
//编码返回字符串
return encryptBASE64(key.getEncoded());
}
/**
* 获取公钥
*
* @param key 公钥字符串
* @return 公钥
* @throws Exception 异常
*/
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = decryptBASE64(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
/**
* 获取私钥
*
* @param key 私钥字符串
* @return 私钥
* @throws Exception 异常
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = decryptBASE64(key);
// 修复异常:java.security.InvalidKeyException: IOException : algid parse error, not a sequence
Security.addProvider(new BouncyCastleProvider());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
/**
* Base64解码,返回byte[]
*
* @param key 待解码字符串
* @return 解码后的byte[]
*/
public static byte[] decryptBASE64(String key) {
return Base64.getMimeDecoder().decode(key);
}
/**
* 将byte[]进行Base64编码
*
* @param key 待编码的byte[]
* @return 编码后的字符串
*/
public static String encryptBASE64(byte[] key) {
return Base64.getMimeEncoder().encodeToString(key);
}
/**
* 生成签名
*
* @param data 待生成签名内容
* @param privateKeyStr 私钥
* @return 签名信息
* @throws Exception 异常
*/
public static String sign(byte[] data, String privateKeyStr) throws Exception {
PrivateKey priK = getPrivateKey(new String(hexToBytes(privateKeyStr)));
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initSign(priK);
sig.update(data);
return bytesToHex(sig.sign());
}
/**
* 验证签名
*
* @param data 待验证原文
* @param sign 待验证签名
* @param publicKeyStr 公钥
* @return 是否验证成功
* @throws Exception 异常
*/
public static boolean verify(byte[] data, String sign, String publicKeyStr) throws Exception {
PublicKey pubK = getPublicKey(new String(hexToBytes(publicKeyStr)));
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(pubK);
sig.update(data);
return sig.verify(hexToBytes(sign));
}
/**
* RSA加密
* @param plainText 待加密内容
* @param publicKeyStr 公钥字符串
* @return 加密后内容
* @throws Exception 异常
*/
public static String encrypt(byte[] plainText, String publicKeyStr) throws Exception {
PublicKey publicKey = getPublicKey(publicKeyStr);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = plainText.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
int i = 0;
byte[] cache;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(plainText, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(plainText, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptText = out.toByteArray();
out.close();
return bytesToHex(encryptText);
}
/**
* RSA解密
* @param encryptTextHex 已加密内容
* @param privateKeyStr 私钥字符串
* @return 解密后内容
* @throws Exception 异常
*/
public static String decrypt(String encryptTextHex, String privateKeyStr) throws Exception {
byte[] encryptText = hexToBytes(encryptTextHex);
PrivateKey privateKey = getPrivateKey(privateKeyStr);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int inputLen = encryptText.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptText, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] plainText = out.toByteArray();
out.close();
return new String(plainText);
}
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(KEY_PAIR_INIT_SIZE);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 将byte[]转换为16进制字符串
*
* @param bytes 待转换byte[]
* @return 转换后的字符串
*/
public static String bytesToHex(byte[] bytes) {
//一个byte为8位,可用两个十六进制位标识
char[] buf = new char[bytes.length * 2];
int a = 0;
int index = 0;
for (byte b : bytes) { // 使用除与取余进行转换
if (b < 0) {
a = 256 + b;
} else {
a = b;
}
buf[index++] = HEX_CHAR[a / 16];
buf[index++] = HEX_CHAR[a % 16];
}
return new String(buf);
}
/**
* 将16进制字符串转换为byte[]
*
* @param str 待转换字符串
* @return 转换后的byte[]
*/
public static byte[] hexToBytes(String str) {
if (str == null || "".equals(str.trim())) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
public static void main(String[] args) throws Exception {
// 初始化密钥对
Map<String, Object> keyMap = initKey();
// 获取公钥字符串
String publicKey = getPublicKeyStr(keyMap);
// 获取私钥字符串
String privateKey = getPrivateKeyStr(keyMap);
// 打印公钥、私钥
System.out.println("公钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)");
System.out.println("-----BEGIN PUBLIC KEY-----");
System.out.println(publicKey);
System.out.println("-----END PUBLIC KEY-----");
System.out.println("\n");
System.out.println("私钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)");
System.out.println("-----BEGIN RSA PRIVATE KEY-----");
System.out.println(privateKey);
System.out.println("-----END RSA PRIVATE KEY-----");
System.out.println("\n");
// 待加密内容,例:123
String s = "123";
// 进行RSA加密
String encrypt = encrypt(s.getBytes(), publicKey);
// 打印加密后内容
System.out.println("密文:(填充方式:PKCS1_PADDING,输出类型:hex,字符集:utf8编码)");
System.out.println(encrypt);
System.out.println("\n");
// 进行RSA解密
String decrypt = decrypt(encrypt, privateKey);
// 打印解密后内容
System.out.println("解密后明文: ");
System.out.println(decrypt);
}
}
5.3 执行结果
公钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwdN4/2H4J6RzLu7gHnGQgMN4odQuwmzo
r1/upmaHlXQqew2Ac5ZAaDs7hKLg7sO2+wgMLK2iZSB3hBMe2L8CmXmRPKPJU+WG6SzWZmm6gGMu
18O1rlMKKxl9/WTBk8nb00Ft30bdA7vl0aEUOdoEz7ls45bu/ZFZeCpmdk3Q4NEYEmO4OB4uvIec
1A6F5ZpEaKAWs/+i9c73hrnakgHgUhZUyA7A0ZYUqKCSXzgexDBOyd9dAoUrAv7PxQQlSCCOkKuK
9dHh5+rH66mgKUiVNUzUiMlV3L4KlTFPlnMCbi1n5I3CvgeTWIanC4wFMzhBYJbtFIwFlgp9Sa+j
y0x01QIDAQAB
-----END PUBLIC KEY-----
私钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB03j/YfgnpHMu7uAecZCAw3ih
1C7CbOivX+6mZoeVdCp7DYBzlkBoOzuEouDuw7b7CAwsraJlIHeEEx7YvwKZeZE8o8lT5YbpLNZm
abqAYy7Xw7WuUworGX39ZMGTydvTQW3fRt0Du+XRoRQ52gTPuWzjlu79kVl4KmZ2TdDg0RgSY7g4
Hi68h5zUDoXlmkRooBaz/6L1zveGudqSAeBSFlTIDsDRlhSooJJfOB7EME7J310ChSsC/s/FBCVI
II6Qq4r10eHn6sfrqaApSJU1TNSIyVXcvgqVMU+WcwJuLWfkjcK+B5NYhqcLjAUzOEFglu0UjAWW
Cn1Jr6PLTHTVAgMBAAECggEAbeairOu38YJlbS81FQ3/iYNMWzYcbVGjfg0/HEr2hd+gVrWJKAEB
9Bfh34sbT0bZ8ezWOl8ZvY1zNwhAbVWg+7TajS+xcEis/nnV96vre/DBFsZ5taaQFXAW6B7BRDMo
2dg3nGpp1zwrS3myJjtgjwfsnf9u84f+2wvvnRTyYwYvfnYOGOe6MdIoKSDAZPVtHJHXhx7jtt0a
H832U3liulI4H7yjZpqMO/jNwnVWB/wQlpnsBSQiR3pa+BpnQJzaY53te4g1vX4CLBfd8mCbcaS0
rkqoKaMytyA7Uc5ztThTjJqNKsC0dPlr6T1jLtJcbEIoWoRu6SOgEWLOZ0ijAQKBgQDnPNVnaCY9
pE35leINmV9xjwg4Yh4l8/KnaRLWd53uwiDolteVuN8uqR5ErZ/gkc9vHp5NuwHoJSmVAkeZqMT1
ZlbLQybum1QAznzd4h9CiVjwn+WEvMBm6FRPBeo3ej78Q6qcw121MBJby2zQqw5128/VgbePQNEo
DexTM+g6jQKBgQDWlQkTeo7uG5y3nK+u/FS2ZdzzsnMh68Ewxx/WLx1pTCK1bIoXglM8CiqNN3Fi
iOQ0P8Q164ctn4X1zB+yu2HOLkwIjBrEFUjnyBD3xvp96386QE3BOQtHA6VgVkSGXfDlLIBtwUAc
gcCCWFzYoSV5TK6zSm79RC0zI5qIWfJ1aQKBgQCMszpSmk+ycDg15ppOlgU6LsLcs+8OPtpmPQwG
TXBep+aoP6hb5MqANM1DErZWScKDJYlDWMe3Pm2HyoRQnh2CCExFj82vn/nEJ+BYjk9hB/uDJnfc
hZE4zKMIFlxGd269xlqY2lM5fU+eZTAH5B1/X0md6zkKxHC/w4EJu2rRvQKBgHzw1BMKZlSPsUVb
rxN1CqIVV3xxqAXVLmyHVKsyTa60zPTT2OftUyd/R91nrdZQnIcrpcQ7ej7/RlGi48X+wuj5Hf5Q
DXmkZwnF/NM4gTt2NmMlc+CQpVdY8R7RvB58bjoSGklNn1W52uxKJO1hjt883e+45D0FSEghb+X6
cWkZAoGBANW3nScSIqbDxP3oY7Pmp+MmsJYM05RuGxKS11chNEJyLICUeXHli192T9rnPhjg8Xzz
tDN1e55wkRI3wCiVpXw5VSaJdiFBSZUr09GVN21P1uoqkXUvoEM5QiStMy9dwk44B2Pki3n8xF/M
MahlFyj6URw7QMSk7RiDc3TSdapP
-----END RSA PRIVATE KEY-----
密文:(填充方式:PKCS1_PADDING,输出类型:hex,字符集:utf8编码)
70f00ee3a479ed6a0ebea17da9459bcbb9d1cbbdd456e1a13339f9632d842a23e423a8046cea40bbc7c6f0e6543a901e35457b65ffa5e774928eb58e374c7b3f51c3ee14339f9220ead039442520772b0d302d3aad7e84466eea57381131c0bfc866f5eb453b0890956e15992c36f2c860366575c29b3517e9ec562468169aedd45529849f9b3176f81d8fe5cdec5128b757a1e3c2291a635af1150a4db2b874237e668ba3990ff9a2919576baa8b7f4c7443a5fa91483bfb3a79fdfa55e1bec1bc08dc1eb2547153e47b9e3ea1a0de02845c2d167942d6621f2d06534e309356b7092eaf84e289d1a76905f6ab84b08522c2ad91718251378f0dd0f2acb965a
解密后明文:
123
5.4 线上验证
验证地址:
- 生成密钥对: http://web.chacuo.net/netrsakeypair
- 公钥加密: http://tool.chacuo.net/cryptrsapubkey
- 私钥解密: http://tool.chacuo.net/cryptrsaprikey
验证完毕,完结撒花 ~
参考:
1.RSA加密算法:https://blog.youkuaiyun.com/lbwnbnbnbnbnbnbn/article/details/124173910
2.简述RSA加密算法:https://baijiahao.baidu.com/s?id=1734867691033700155&wfr=spider&for=pc