RSAStaticUtil For Java

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算法密钥长度/密文长度/明文长度-优快云博客

2.  微信小程序与java端进行RSA非对称加解密_微信小程序使用java rsa加解密-优快云博客

3. 用户注册登录系统加密方案分析与实践-腾讯云开发者社区-腾讯云 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值