0x00 说明
备份RSAStaticUtil到优快云,其提供获取公钥和进行解密。该代码使用前端WxmpRsa组件进行RSA加密,RSAStaticUtil进行RSA解密时,会出现“too much data for RSA block”错误。其需要对文本进行机密性保护,但是由于缺乏CA的实现,导致不能抵挡中间人攻击,所以暂时搁置研究。
0x01 代码
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
/**
* RSA 静态工具类(单例密钥对模式)
* 特性:
* 1. 类加载时自动生成密钥对
* 2. 公钥对外暴露,私钥仅内部使用
* 3. 线程安全
* 4. 异常安全处理
*/
public final class RSAStaticUtil {
// 安全提供者(BouncyCastle)
private static final Provider BC_PROVIDER = new BouncyCastleProvider();
private static final String ALGORITHM = "RSA";
private static final int KEY_SIZE = 2048;
private static final String publicKeyStr;
private static final PrivateKey privateKey;
static {
try {
// 初始化密钥对
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM, BC_PROVIDER);
SecureRandom random = SecureRandom.getInstanceStrong(); // 更安全的随机数
generator.initialize(KEY_SIZE, random);
KeyPair keyPair = generator.generateKeyPair();
// 编码公钥
publicKeyStr = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
// 保存私钥
privateKey = keyPair.getPrivate();
} catch (NoSuchAlgorithmException e) {
System.err.println("RSA 初始化失败: " + e.getMessage());
throw new ExceptionInInitializerError(e);
}
}
/**
* 获取Base64编码的公钥
*/
public static String getPublicKey() {
return publicKeyStr;
}
/**
* 使用私钥解密数据
*
* @param encryptedBase64Text Base64编码的加密文本
* @return 解密后的明文
*/
public static String decrypt(String encryptedBase64Text) {
if (StringUtils.isBlank(encryptedBase64Text)) {
return null;
}
try {
byte[] encryptedBytes = Base64.decodeBase64(encryptedBase64Text);
Cipher cipher = Cipher.getInstance(ALGORITHM, BC_PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(encryptedBytes));
} catch (GeneralSecurityException e) {
throw new SecurityException("解密失败", e);
}
}
public static String decryptLong(String encryptedBase64Text) {
if (StringUtils.isBlank(encryptedBase64Text)) {
return null;
}
try {
byte[] encryptedBytes = Base64.decodeBase64(encryptedBase64Text);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", BC_PROVIDER); // 明确指定填充方式
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 核心修复逻辑:分段解密
int blockSize = 256; // 2048位密钥对应的密文块大小(单位:字节)
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
for (int offset = 0; offset < encryptedBytes.length; offset += blockSize) {
int inputLength = Math.min(blockSize, encryptedBytes.length - offset);
byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLength);
buffer.write(decryptedBlock, 0, decryptedBlock.length);
}
return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
} catch (GeneralSecurityException e) {
throw new SecurityException("分段解密失败", e);
}
}
}
0x02 测试
@Test
void testRSAStaticUtil() {
// 获取公钥
String pubKey = RSAStaticUtil.getPublicKey();
System.out.println("Public Key:\n" + pubKey);
// 解密示例(需要实际加密数据测试)
// String decrypted = RSAStaticUtil.decrypt("加密后的Base64字符串");
// 假设公钥:
// MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydd4bGyY9XxaSasoCyvri0U9ajejRAwOmMVbJf5VsUHgGeCQxD3/UhW8D1Yo/De0JYZaCaWoCdBQSIIXE89SwMsknrxGPdu0zgWLfPOL8upSC4ZagTwBl/nuzIlqd6RCp2qFJ2ix+3eHTJqci40BWwpmsnS5uFCS/H7Pskc25O5i7rnmXTvciUeOWCcX/LFhwHre4DxCyzDeVWewqmuJVi+3WFiCaVA/7mgMAkAPWO4mQQfqwXKTpdNddfB4NF6BA13tqaxmnWKbXma65tDwOmmd0xXffSaTWPUeZ5TbGxGG0PK9MGWL5sqdf6HNpKeaVwoVQGTJnqyvPZhtIcevUQIDAQAB
// 假设明文:password
System.out.println("Plain Text:\n" + "password");
Scanner input = new Scanner(System.in);
String cipherText = input.next();
String plainText = RSAStaticUtil.decryptLong(cipherText);
System.out.println("plainText:\n" + plainText);
if (plainText != null && plainText.equals("password")) {
System.out.println("通过");
} else {
System.out.println("不通过");
}
}
值得注意的是,这个测试一直没有通过。
0x03 后记
后续可以继续实现RSA加密解密,参照资料2。另一方面,可以接着研究RSA加密中,证书签名的相关问题,使得在一定程度能够抵挡中间人攻击。
0x04 资料
1. too much data for RSA block .关于RSA算法密钥长度/密文长度/明文长度-优快云博客