JAVA RSA加密Demo,应用
一,背景
项目上需要对接资方,并且使用RSA加密,此文章做一个当时踩坑记录。
二,代码
RSA加密核心流程:(使用分段加解密)
// 流程: 加解密方式分段加解密,不然可能会提示 太长解密不了。
// 1.先生成 密钥对 :见【生成密钥对.docx】
// 2.交互: 公钥加密,私钥加签。
// A 与 B 交互
// A 使用 B 公钥加密报文 得到byte[],使用Base64编码数组,得到密文data
// A 使用 A 私钥,加签密文data得到byte[],使用Base64编码数组,得到签名sign
// 传输...
// B 收到密文data,使用Base64编码data得到byte[]
// B 使用A公钥验签
// B 使用B私钥,解密byte[],得到明文报文
生成 密钥对 :见【生成密钥对.docx】
代码
package com.madorangecat.artmartxt.utils.rsa;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
@Slf4j
public class RSAUtil {
private static final String SIGNATURE_INSTANCE = "SHA512withRSA";
private static final String KEY_ALGORITHM = "RSA";
// 最大解密长度
private static final int MAX_DECRYPT_BLOCK = 256;
// 最大加密长度
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* @Title: getPublicKey
* @Description:实例化公钥
*/
public static PublicKey getPublicKey(String publicKey) throws Exception {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}
/**
* @Title: getPrivateKey
* @Description:实例化私钥
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
/**
* @Title: sign
* @Description:私钥签名
*/
public static byte[] sign(byte[] content, String privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
signature.initSign(getPrivateKey(privateKey));
signature.update(content);
return signature.sign();
}
/**
* @Title: verify
* @Description:公钥验签
*/
public static boolean verify(byte[] content, byte[] sign, String publicKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
signature.initVerify(getPublicKey(publicKey));
signature.update(content);
return signature.verify(sign);
}
/**
* 使用私钥分段解密
*
* @param dataBytes 密文byte
* @param privateKeyStr 密钥Str
*/
public static String decryptByPrivateKeyPartition(byte[] dataBytes, String privateKeyStr) {
String str = "";
try {
PrivateKey privateKey = getPrivateKey(privateKeyStr);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int inputLen = dataBytes.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(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
// 解密后的内容
str = new String(decryptedData, StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("===== 私钥分段解密:decryptByPrivateKeyPartition,失败异常:", e);
}
return str;
}
/**
* 使用公钥加密
*
* @param dataBytes 密文byte
* @param publicKeyStr 公钥Str
*/
public static String encryptByPublicKeyPartition(byte[] dataBytes, String publicKeyStr) {
// 加密
String str = "";
try {
PublicKey publicKey = getPublicKey(publicKeyStr);
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
//MAX_ENCRYPT_BLOCK 117
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
Base64.Encoder encoder = Base64.getEncoder();
str = encoder.encodeToString(encryptedData);
} catch (Exception e) {
log.error("===== 加密失败 encryptByPublicKeyPartition :", e);
}
return str;
}
public static void main(String[] args) throws Exception {
String content = "一段待加密的报文!!";
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJqiwH7oPZTbxUf5/7zWJ3qdCtuXanicuPlAoqE8mIgmUAWGEcJZMqPJge3PbsU2iALDCeKk4W6lWTwinwAo/lupvmvqw0a+1SFjTamZxQkl+OpDQTuUitgBOtKNBZy/019aOYVn1STMKiuzKMTe9K6gz0AhX3MHu4u9NoQxMkhNckyh2YSH7AvPh11mpE2fH3mA32ql23qqTcIdzdIpSx65kGF8ddHvX047IhngHZ8xc3bbtNCGoP4PwholK3r4HG0zd0T2fsEeAlM3gPjpCcF1F3Mtah5Teq/ZQt5VrGAjXQVxkx96PdHNWX5kWdNefhTOMcFA2U+N4ChTkzxSiXAgMBAAECggEAfLci99zH6ZGrkM5E0GKaLMr2ZRm3ibEsFKVHC/Kkxilt8SK3G8MZGhvTL/MTOd/zVqD9iDM9i+pr9i8z5FyRRU98rtzofprob8WP9kjdZO7A7eQa2L/TdIkqogIFqCMBe2doPwPuCQGztRP0FNT10eyhCh5oGzD+sqZl/arNodz0bUZ7MQAwO4oUOVAIowxArzMoFrKzqTQrdGdAryCtRhUzR2oolFAV3nfskKr33UfdPs/usgOTX1Ud0F4iy5heT5ix9verFD82J1Cd6yHa29q8CQArC+HjvCUc1gYZ8/E84hLB2Le/a1W0tS8afNhHB7y9oI56PwKMFCdxALuG8QKBgQDlAjshWrfY2V/T3Zb5EUsm7eU0/DGwr3FBR0AHw6sdYsFeb0SUitSvsXcTySxMhG0UIIAeBH3KccsNF/sTQOEOGNzLOhMfDedrwLZsIOjy1+WGzvkBBAoH/5z67r/ICzwFkc7SjZbT6yNDpeHr51yY8YRxvosMyS6WWlMb+DoDgwKBgQDhbueDAi7Z5r4s8t9iU3MULyfEVm6ODrslpyG/IQMq6j1vObNtckppivOUMqAEIoCGtFxuSlVn+kz6vSAPFTZOIrgyf6D3YBcpgMfZZASdFgf6devqiTIqCPrMfXHmS0inJZXOVRX2kaTheYc9Mugtf34675SIGDmhKtUrMMb2XQKBgQCKmJoOqqefbpcnUvFe1Y+FBO717TdpdhR17FgdGteBllXvACoiOA9WMXIeoF7WBEM7rhu7jv5OOiYoS34hfd92t6dQu1n9Ll4DgYMl15kD95OqzbI3YilsA9AOsdWRjPMLb94a4aQJp0IVW9xp+AC7c1ezuxzOZSdzyUCJKeZ6XwKBgF4M9Q2VRZKnT8RU95mAE0zid4bUQH+H6dBFsDCbR0UKQOM8gX0x55Ws4P59OIFBG7oXWVCyNnuQ07jwWoWwZd2nYo2+7ZsHPeKU1cv+jGY1INL/i5Nn9cPPx0K1O2XHFuKVLbDNksIx9UYUe7WunWsN+83+0ksNQE3bHuBME1YJAoGAGtQ7qOlCRwr8mDPDV4FYJN36SbFPSi5jv4vE/SNyqfiwbQIffea8uhAHujNZ+mPreV8HK1FrQR2EZ4nFBFruw/MMafwQRXAnek9RceLmNYB2na87yRqOH0WpB63JKs++XtYerWsMSKIXds1FQNiwPCLeUrx5EOQtbScxKwIT91Y=";
String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyaosB+6D2U28VH+f+81id6nQrbl2p4nLj5QKKhPJiIJlAFhhHCWTKjyYHtz27FNogCwwnipOFupVk8Ip8AKP5bqb5r6sNGvtUhY02pmcUJJfjqQ0E7lIrYATrSjQWcv9NfWjmFZ9UkzCorsyjE3vSuoM9AIV9zB7uLvTaEMTJITXJModmEh+wLz4ddZqRNnx95gN9qpdt6qk3CHc3SKUseuZBhfHXR719OOyIZ4B2fMXN227TQhqD+D8IaJSt6+BxtM3dE9n7BHgJTN4D46QnBdRdzLWoeU3qv2ULeVaxgI10FcZMfej3RzVl+ZFnTXn4UzjHBQNlPjeAoU5M8UolwIDAQAB";
Base64.Encoder encoder = Base64.getEncoder();
Base64.Decoder decoder = Base64.getDecoder();
// 加密
String publicMessage = encryptByPublicKeyPartition(content.getBytes(StandardCharsets.UTF_8), publicKey);
System.out.println("公钥加密密文:" + publicMessage);
// 加签
byte[] sign = sign(publicMessage.getBytes(StandardCharsets.UTF_8), privateKey);
byte[] signEncode = encoder.encode(sign);
String privateSign = new String(signEncode);
System.out.println("私钥加签密文:" + privateSign);
// 传输
// ...
// 解密
byte[] decode = decoder.decode(publicMessage.getBytes());
String decryMessage = decryptByPrivateKeyPartition(decode, privateKey);
System.out.println("明文:" + decryMessage);
// 验签
byte[] signDecode = decoder.decode(privateSign.getBytes());
boolean verify = verify(publicMessage.getBytes(), signDecode, publicKey);
System.out.println("验签结果:" + verify);
// 流程: 加解密方式分段加解密,不然可能会提示 太长解密不了。
// 1.先生成 密钥对 :见【生成密钥对.docx】
// 2.交互: 公钥加密,私钥加签。
// A 与 B 交互
// A 使用 B 公钥加密报文 得到byte[],使用Base64编码数组,得到密文data
// A 使用 A 私钥,加签密文data得到byte[],使用Base64编码数组,得到签名sign
// 传输...
// B 收到密文data,使用Base64编码data得到byte[]
// B 使用A公钥验签
// B 使用B私钥,解密byte[],得到明文报文
}
}
生成密钥对文档:
找不到上传文档的地方…
1.登录http://web.chacuo.net/netrsakeypair (证书在线生成)
2.点击 rsa秘钥对
3.生成秘钥位数选择2048 秘钥格式选择pksc#8
4.点击生成秘钥 按页面提示分别保存为 ***pub.key ***pri.key即可
一些坑的地方:
1.生成密钥这只是其中一种方法,用其他方式(命令)等,注意参数就可以了
证书可以不选择,生成的密钥,使用时,不要换行。否则会报错。
2.工具类公钥加密后得到的byte[],不能直接new String,控制台打印会是乱码,并且不能用于后续str.getByte(),去解密这些。所以需要使用Base64编码。