RSA 敏感数据加解密方案
RSA密码RSA加解密算法举个例子加密解密超长文本加密方案REFERENCES
手机用户请
横屏
获取最佳阅读体验,REFERENCES
中是本文参考的链接,如需要链接和更多资源,可以扫码加入『知识星球』(文末)获取长期知识分享服务。
RSA密码
RSA密码是1978年美国麻省理工学院三位密码学者R.L.Rivest、A.Shamir和L.Adleman提出的一种基于大合数因子分解困难性的公开密钥密码。由于RSA密码既可用于加密,又可用于数字签名,通俗易懂,因此RSA密码已成为目前应用最广泛的公开密钥密码。RSA算法是现今使用最广泛的公钥密码算法,也是号称地球上最安全的加密算法。在了解RSA算法之前,先熟悉下几个术语,根据密钥的使用方法,可以将密码分为对称密码和公钥密码。
- 对称密码:加密和解密使用同一种密钥的方式
- 公钥密码:加密和解密使用不同的密码的方式,因此公钥密码通常也称为非对称密码。
RSA加解密算法
1.随机地选择两个大素数p和q,而且保密;
2.计算n=pq,将n公开;
3.计算φ(n)=(p-1)(q-1),对φ(n)保密;
4.随机地选取一个正整数e,1<e<φ(n)且(e,φ(n))=1,将e公开;
5.根据ed=1(mod φ(n)),求出d,并对d保密;
6.加密运算:c=m^e(mod n); 也就是说对密文进行D次方后除以N的余数就是明文,这就是RSA解密过程。知道D和N就能进行解密密文了,所以D和N的组合就是私钥
7.解密运算:m=c^d(mod n)。
注意:在加密运算和解密运算中,m和c的值都必须小于n,也就是说,如果明文(或密文)太大,必须进行分组加密(或解密)。
RSA的加密方式和解密方式是相同的,加密是求`e次方的mod n
;解密是求d次方的mod n
,此处d
是解密(Decryption)的首字母;n是数字(Number)的首字母;e是加密(Encrypt)的首字母。
举个例子
小写转为大写,便于阅读,
表达式约定
在这里插入图片描述
求解方式
在这里插入图片描述
N
我们准备两个很小对质数, p = 17 q = 19 N = p * q = 323
L
L 是 p-1 和 q-1的最小公倍数,可用如下表达式表示L = lcm(p-1, q-1)
L`= lcm(16,18) = 144,即144为16和18对最小公倍数
E
求E必须要满足2个条件:1 < E < L ,gcd(E,L)=1,即1 < E < 144,gcd(E,144) = 1 E和144互为质数,5显然满足上述2个条件 故E = 5,此时公钥=(E,N)= (5,323)
D
求D也必须满足2个条件:1 < D < L,E*D mod L = 1 即1 < D < 144,5 * D mod 144 = 1 显然当D= 29 时满足上述两个条件 1 < 29 < 144 5*29 mod 144 = 145 mod 144 = 1 此时私钥=(D,N)=(29,323)
加密
准备的明文必须时小于N的数,因为加密或者解密都要mod N其结果必须小于N 假设明文 = 123 则 密文=明文^E mod N=123^5 mod 323=225
解密
明文=密文^D mod N=225^29 mod 323=123 解密后的明文为123。
好了至此RSA的算法原理已经讲解完毕,是不是很简单?
超长文本加密方案
问题原因: 前端敏感数据加密,文本过长导致加密失败。
日志
Data must not be longer than 245 bytes javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
原因: 待加密数据超长的原因
处理方式: 分片加解密
代码语言:javascript复制/*
* @ProjectName: 编程学习
* @Copyright: 2020 HangZhou Yiyuery Dev, Ltd. All Right Reserved.
* @address: 微信搜索公众号「架构探险之道」获取更多资源。
* @date: 2020/8/15 4:50 下午
* @description: 本内容仅限于编程技术学习使用,转发请注明出处.
*/
package com.dsb.framework.boot.security.ed;
import org.apache.commons.io.IOUtils;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* <p>
* RSA 加解密工具类
* </p>
*
* @author Yiyuery
* @date 2020/8/15 4:50 下午
*/
public class RsaAssistant {
/**
* 算法名称
*/
private static final String KEY_ALGORITHM = "RSA";
/**
* 密钥长度
*/
private static final int KEY_SIZE = 2048;
/**
* RSA最大加密明文大小 KEY_SIZE/8-11
*/
private static final int MAX_ENCRYPT_BLOCK = 245;
/**
* RSA最大解密密文大小 KEY_SIZE/8
*/
private static final int MAX_DECRYPT_BLOCK = 256;
/**
* 随机生成密钥对(包含公钥和私钥)
*/
public static KeyPair generateKeyPair() throws Exception {
// 获取指定算法的密钥对生成器
KeyPairGenerator gen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对生成器(指定密钥长度, 使用默认的安全随机数源)
gen.initialize(KEY_SIZE);
// 随机生成一对密钥(包含公钥和私钥)
return gen.generateKeyPair();
}
/**
* 将 公钥/私钥 编码后以 Base64 的格式保存到指定文件
*/
public static void saveKeyForEncodedBase64(Key key, File keyFile) throws IOException {
// 获取密钥编码后的格式
byte[] encBytes = key.getEncoded();
// 转换为 Base64 文本
String encBase64 = Base64Utils.encodeToString(encBytes);
// 保存到文件
IOUtils.write(encBase64, new FileWriter(keyFile));
}
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String str, String publicKey) throws Exception {
//base64编码的公钥
byte[] decoded = Base64Utils.decodeFromString(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
String outStr = Base64Utils.encodeToString(cipherEncrypt(str, pubKey));
return outStr;
}
private static byte[] cipherEncrypt(String str, RSAPublicKey pubKey) throws Exception {
byte[] srcBytes = str.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipherDoFinal(cipher,srcBytes,MAX_ENCRYPT_BLOCK);
}
/**
* RSA私钥解密
*
* @param str 加密字符串
* @param privateKey 私钥
* @return 铭文
* @throws Exception 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64Utils.decode(str.getBytes("UTF-8"));
//base64编码的私钥
byte[] decoded = Base64Utils.decodeFromString(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA分段解密
String outStr = new String(cipherDecrypt(inputByte, priKey));
return outStr;
}
/**
* 分段解密
*
* @param inputByte
* @param priKey
* @return
*/
private static byte[] cipherDecrypt(byte[] inputByte, RSAPrivateKey priKey) throws Exception {
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, priKey);
return cipherDoFinal(cipher,inputByte,MAX_DECRYPT_BLOCK);
}
/**
* 分段大小
*
* @param cipher
* @param srcBytes
* @return
* @throws Exception
*/
private static byte[] cipherDoFinal(Cipher cipher, byte[] srcBytes,int segment)
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int inputLen = srcBytes.length;
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > segment) {
cache = cipher.doFinal(srcBytes, offSet, segment);
} else {
cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i ;
offSet = i * segment;
}
byte[] data = out.toByteArray();
out.close();
return data;
}
}
代码语言:javascript复制超长加密测试
/**
* 测试加解密
*/
@Test
public void encryptAndDecrypt() throws Exception {
String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqtu2aVZv7xG0Ti2JxvJojR2l9yF w9ptIYGLAoTVISzQ80V43Otxx769idFyLRDDaxcIMFMmBX2IKhQjBecUMxPuo1nKJq6HEVfTKXaWezJgedyY HfmkoVBZ6f0FEWUXwQlLKMazQ7T1Uu5nYD5RqYm UDW70kEPcMz S5RJ2URBTXHtJx4dgnfY jWD77o1Rag4Rai6/N8qXVnDhx5LAmxT efmnJ3Uw74yJqJMmRaHlwOoERm7kIiS6w084Fi4ttFk HlRDqo5/tRL00BmNOh4pbjoln 8hwgqzwQkpAwZp2Y2S0OelCIsl07LqnY XIMZVpEfq4K8223skWw1wIDAQAB";
String priKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCq27ZpVm/vEbROLYnG8miNHaX3IX7D2m0hgYsChNUhLNDzRXjc63HHvr2J0XItEMNrFwgwUyYFfYgqFCMF5xQzE 6jWcomrocRV9MpdpZ7MmB53Jj4d aShUFnp/QURZRfBCUsoxrNDtPVS7mdgPlGpib5QNbvSQQ9wzP5LlEnZREFNce0nHh2Cd9j6NYPvujVFqDhFqLr83ypdWcOHHksCbFP55 acndTDvjImokyZFoeXA6gRGbuQiJLrDTzgWLi20WT4eVEOqjn 1EvTQGY06HiluOiWf7yHCCrPBCSkDBmnZjZLQ56UIiyXTsuqdj5cgxlWkR rgrzbbeyRbDXAgMBAAECggEBAJ3WBnbdEN5rHoOx8btFqGvkXbMk0DQhjfsL7tzO1QymmAiDvxlmr190xyePwsf62mwNhNUPmuorgyRMIeaMB87/tM5WXjbJt6C/3yRIls8MRK OqAmwyeHFFByrvQEVHfGApM7Nhi60aeB66eekg6NOTmaoDWZTr4VW1KZ2sSWP3/XG1m ursijpVBJcDJLClAoY32HmQ0IkwBqt2yI03esI Y/fgFKUwM9XWQo0NPfCncaxu7cpnIi7wccs0So00VmvsFua/HcryhjkCgEaZi9nZgGMZbRIDPn6Rh/xJ3uS5tHFMNqkF8Xd67zIuneBWFG7Au4x/fcW3/Y40LnBAECgYEA/p MAiXK2o4WMH7apggHXLTQsJCRvblkj/Qo5fV2EXdlAS hvrV8 C7pEufI0r5kLB/0LDwB4cZFk xK4XPB7xUgJKoO80NxXCvYIFgXAivu69EBJoV3o2CRqsoK4CGaykWTwKnNCkrD6hGFYVJLHBGe/K3alAL9lfuFdzj0tdcCgYEAq8g3iv3LY/qY/b00AUgEsGJlISDZ6ROZWhYHzypbmFKNjTbUh3EoHlyhXdmwad9lW2gr5ixlIydjg7MhYbNvr9JSsXM iYIH8/DjiHkmg8WGhDMb72Bo65GkUUsPl/JuMycF50L9UyQkPaabej4PiI03LXCxnKyeqGK9 rjRfQECgYAsrvkWA2XW9upj9k8OpggMt9qLscMxxVAlhxaKTIo8xHQgQiijXBwjPbA/VhfDDBBuQelKvfkikvXw4J2/dN4Kw 1RIdrfy 0f3L3f5zWadvVFwvbtuKxnKnJFw5EnBh6w8obcX7AQ50/8SrjafGOb GerNiNOqDuyT2J7qq4fHwKBgQCJMNnLE24AZv8Qhq6l51J3W93QW2AtGQ38OGP8O6PzPtr10Lhjgye7N9dYEKcnptZX7hZBOWt8a9S6NbGPSbMFBEAuoA9t2n9nfxb4w2jTDEmmAvtobeLdX PTRcjDuaby6qXS5TtdYvMAOdi9XWKZN2QWNRAgEwlxtZbCoekAQKBgQDy ZlS4iM36LzN5BWPncXRmnGUNnDsSoWpP7yXjOuDpKFMF4cZi 0jDwyHxhSTTg24JOcZDZdRmkvQtF2cgC2QCN9twqLM79 VlGV/KABX0xebZdz6DrlWDwk7jem6b3zCh1XwuzHAvTPthIgGUGBPZjHQgcwoozXZ/TXY0fAlSg==";
String text = "asdasdasda";
for (; text.length() < 200; text = text) {
}
String encodeStr = RsaAssistant.encrypt(text, pubKey);
LogAssistant.info("解密后:{}", RsaAssistant.decrypt(encodeStr, priKey));
}