加密算法RSA是怎么进行加解密的

package com.shgbit.sfkc.thirdapi.judicial.utils;

import javax.crypto.Cipher;
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;


public class KeyUtil {

    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";

    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * <p>
     * ⽤私钥对信息⽣成数字签名
     * </p>
     *
     * @param data       已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return 签名
     * @throws Exception 加密异常
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(data);
        return new String(Base64.getEncoder().encode(signature.sign()));
    }

    /**
     * <p>
     * 校验数字签名
     * </p>
     *
     * @param data      已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @param sign      数字签名
     * @return 验证签名结果
     * @throws Exception 验签异常
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(data);
        return signature.verify(Base64.getDecoder().decode(sign));
    }

    /**
     * <p>
     * 用公钥加密
     * </p>
     *
     * @param data      源数据
     * @param publicKey 公钥(BASE64编码)
     * @return 加密后的数据(BASE64编码)
     * @throws Exception 加密异常
     */
    public static String encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(x509KeySpec);
        
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        
        int inputLen = data.length;
        byte[] out = new byte[0];
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            byte[] temp = new byte[out.length + cache.length];
            System.arraycopy(out, 0, temp, 0, out.length);
            System.arraycopy(cache, 0, temp, out.length, cache.length);
            out = temp;
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        return Base64.getEncoder().encodeToString(out);
    }

    /**
     * <p>
     * 用私钥解密
     * </p>
     *
     * @param encryptedData 已加密数据(BASE64编码)
     * @param privateKey    私钥(BASE64编码)
     * @return 解密后的数据
     * @throws Exception 解密异常
     */
    public static byte[] decryptByPrivateKey(String encryptedData, String privateKey) throws Exception {
        byte[] data = Base64.getDecoder().decode(encryptedData);
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        
        int inputLen = data.length;
        byte[] out = new byte[0];
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            byte[] temp = new byte[out.length + cache.length];
            System.arraycopy(out, 0, temp, 0, out.length);
            System.arraycopy(cache, 0, temp, out.length, cache.length);
            out = temp;
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        return out;
    }

    /**
     * <p>
     * 用私钥加密
     * </p>
     *
     * @param data       源数据
     * @param privateKey 私钥(BASE64编码)
     * @return 加密后的数据(BASE64编码)
     * @throws Exception 加密异常
     */
    public static String encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        
        int inputLen = data.length;
        byte[] out = new byte[0];
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            byte[] temp = new byte[out.length + cache.length];
            System.arraycopy(out, 0, temp, 0, out.length);
            System.arraycopy(cache, 0, temp, out.length, cache.length);
            out = temp;
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        return Base64.getEncoder().encodeToString(out);
    }

    /**
     * <p>
     * 用公钥解密
     * </p>
     *
     * @param encryptedData 已加密数据(BASE64编码)
     * @param publicKey     公钥(BASE64编码)
     * @return 解密后的数据
     * @throws Exception 解密异常
     */
    public static byte[] decryptByPublicKey(String encryptedData, String publicKey) throws Exception {
        byte[] data = Base64.getDecoder().decode(encryptedData);
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(x509KeySpec);
        
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        
        int inputLen = data.length;
        byte[] out = new byte[0];
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            byte[] temp = new byte[out.length + cache.length];
            System.arraycopy(out, 0, temp, 0, out.length);
            System.arraycopy(cache, 0, temp, out.length, cache.length);
            out = temp;
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        return out;
    }
}

  1. 代码概述
  • 语言与依赖:Java,使用了 javax.crypto 和 java.security 包中的加密相关类(如 Cipher、KeyFactory、Signature),以及 java.util.Base64 用于处理 Base64 编码。
  • 加密算法:使用 RSA(非对称加密算法),签名算法为 MD5withRSA
  • 主要功能
    • 加密/解密:支持公钥加密、私钥解密,以及私钥加密、公钥解密。
    • 数字签名:使用私钥生成签名,使用公钥验证签名。
    • 分段处理:由于 RSA 加密对数据长度有限制(明文最大 117 字节,密文最大 128 字节),代码实现了分段加密和解密。
  • 应用场景:可能用于司法相关的第三方 API(如 sfkc.thirdapi.judicial 包名暗示),需要数据安全传输或身份验证的场景,例如与法院、司法机构的数据交互。

  1. 常量说明

以下是类中定义的常量及其含义:

  • KEY_ALGORITHM = “RSA”:指定加密算法为 RSA,一种非对称加密算法,基于大整数分解的数学难题。
  • SIGNATURE_ALGORITHM = “MD5withRSA”:签名算法,使用 MD5 哈希算法结合 RSA 加密生成数字签名。:MD5 现已被认为不安全,实际生产环境中更推荐使用 SHA-256(如 SHA256withRSA)。
  • MAX_ENCRYPT_BLOCK = 117:RSA 加密明文的最大块大小(单位:字节)。对于 1024 位 RSA 密钥,最大明文长度为密钥长度(128 字节)减去 11 字节的填充(PKCS#1 填充),即 128 - 11 = 117 字节。
  • MAX_DECRYPT_BLOCK = 128:RSA 解密密文的最大块大小,等于密钥长度(1024 位密钥对应 128 字节)。

  1. 方法详细解析

以下是对每个方法的详细解释,包括功能、实现原理和代码逻辑。

3.1 sign(byte[] data, String privateKey)

  • 功能:使用私钥对数据生成数字签名。
  • 参数
    • data:待签名的数据(已加密或原始数据,字节数组)。
    • privateKey:Base64 编码的私钥字符串。
  • 返回值:Base64 编码的签名字符串。
  • 实现步骤
    1. 将 Base64 编码的私钥字符串解码为字节数组。
    2. 使用 PKCS8EncodedKeySpec 创建私钥规范(RSA 私钥的标准格式)。
    3. 通过 KeyFactory 生成私钥对象。
    4. 初始化 Signature 对象,使用 MD5withRSA 算法,设置私钥进行签名。
    5. 更新签名对象的数据(signature.update(data))。
    6. 调用 signature.sign() 生成签名,并将结果编码为 Base64 字符串返回。
  • 用途:数字签名用于验证数据的完整性和发送者身份。例如,发送方用私钥签名数据,接收方用公钥验证签名,确保数据未被篡改且来自可信发送方。

3.2 verify(byte[] data, String publicKey, String sign)

  • 功能:使用公钥验证数字签名的有效性。
  • 参数
    • data:待验证的原始数据(字节数组)。
    • publicKey:Base64 编码的公钥字符串。
    • sign:Base64 编码的签名字符串。
  • 返回值:布尔值,true 表示签名有效,false 表示无效。
  • 实现步骤
    1. 将 Base64 编码的公钥字符串解码为字节数组。
    2. 使用 X509EncodedKeySpec 创建公钥规范(RSA 公钥的标准格式)。
    3. 通过 KeyFactory 生成公钥对象。
    4. 初始化 Signature 对象,使用 MD5withRSA 算法,设置公钥进行验证。
    5. 更新签名对象的数据(signature.update(data))。
    6. 将 Base64 编码的签名解码为字节数组,调用 signature.verify() 验证签名是否匹配。
  • 用途:验证签名以确认数据的完整性和来源。例如,接收方用公钥验证签名,确保数据未被篡改且来自持有对应私钥的发送方。

3.3 encryptByPublicKey(byte[] data, String publicKey)

  • 功能:使用公钥加密数据。
  • 参数
    • data:待加密的明文数据(字节数组)。
    • publicKey:Base64 编码的公钥字符串。
  • 返回值:Base64 编码的密文字符串。
  • 实现步骤
    1. 将 Base64 编码的公钥字符串解码为字节数组。
    2. 使用 X509EncodedKeySpec 创建公钥规范,通过 KeyFactory 生成公钥对象。
    3. 初始化 Cipher 对象,使用 RSA 算法,设置为加密模式(Cipher.ENCRYPT_MODE)。
    4. 由于 RSA 加密明文长度受限(最大 117 字节),对数据进行分段处理:
      • 如果剩余数据大于 MAX_ENCRYPT_BLOCK(117 字节),加密 117 字节。
      • 否则,加密剩余的所有数据。
      • 使用 System.arraycopy 合并每段加密结果到输出数组。
    5. 将最终加密结果编码为 Base64 字符串返回。
  • 用途:公钥加密通常用于安全传输数据,只有持有私钥的接收方才能解密。例如,客户端用服务器的公钥加密敏感数据,服务器用私钥解密。

3.4 decryptByPrivateKey(String encryptedData, String privateKey)

  • 功能:使用私钥解密数据。
  • 参数
    • encryptedData:Base64 编码的密文字符串。
    • privateKey:Base64 编码的私钥字符串。
  • 返回值:解密后的明文数据(字节数组)。
  • 实现步骤
    1. 将 Base64 编码的密文和私钥字符串分别解码为字节数组。
    2. 使用 PKCS8EncodedKeySpec 创建私钥规范,通过 KeyFactory 生成私钥对象。
    3. 初始化 Cipher 对象,使用 RSA 算法,设置为解密模式(Cipher.DECRYPT_MODE)。
    4. 由于 RSA 解密密文长度受限(最大 128 字节),对密文进行分段处理:
      • 如果剩余密文大于 MAX_DECRYPT_BLOCK(128 字节),解密 128 字节。
      • 否则,解密剩余的所有密文。
      • 使用 System.arraycopy 合并每段解密结果到输出数组。
    5. 返回解密后的明文字节数组。
  • 用途:私钥解密用于接收方解密由公钥加密的数据。例如,服务器接收客户端发送的加密数据,用私钥解密。

3.5 encryptByPrivateKey(byte[] data, String privateKey)

  • 功能:使用私钥加密数据。
  • 参数
    • data:待加密的明文数据(字节数组)。
    • privateKey:Base64 编码的私钥字符串。
  • 返回值:Base64 编码的密文字符串。
  • 实现步骤:与 encryptByPublicKey 类似,但使用私钥进行加密:
    1. 解码 Base64 私钥,生成私钥对象。
    2. 初始化 Cipher 为加密模式,使用私钥。
    3. 分段加密(最大 117 字节),合并结果。
    4. 返回 Base64 编码的密文。
  • 用途:私钥加密通常用于生成数字签名或特殊场景(如身份验证),接收方用公钥解密以验证发送方身份。

3.6 decryptByPublicKey(String encryptedData, String publicKey)

  • 功能:使用公钥解密数据。
  • 参数
    • encryptedData:Base64 编码的密文字符串。
    • publicKey:Base64 编码的公钥字符串。
  • 返回值:解密后的明文数据(字节数组)。
  • 实现步骤:与 decryptByPrivateKey 类似,但使用公钥进行解密:
    1. 解码 Base64 密文和公钥,生成公钥对象。
    2. 初始化 Cipher 为解密模式,使用公钥。
    3. 分段解密(最大 128 字节),合并结果。
    4. 返回解密后的明文。
  • 用途:公钥解密用于解密由私钥加密的数据,通常与数字签名相关,验证数据的来源和完整性。

  1. 代码特点与实现细节

4.1 RSA 算法

  • RSA 是一种非对称加密算法,使用一对密钥(公钥和私钥):
    • 公钥加密,私钥解密的场景用于数据保密。
    • 私钥加密,公钥解密的场景用于身份验证或签名。
  • RSA 加密/解密需要考虑数据长度限制,因此代码实现了分段处理。
  • 密钥格式:
    • 公钥使用 X509EncodedKeySpec(X.509 标准)。
    • 私钥使用 PKCS8EncodedKeySpec(PKCS#8 标准)。

4.2 分段加密/解密

  • RSA 算法对数据块大小有限制(1024 位密钥为 117 字节明文,128 字节密文)。
  • 代码通过循环分段处理长数据:
    • 加密时按 MAX_ENCRYPT_BLOCK(117)分割。
    • 解密时按 MAX_DECRYPT_BLOCK(128)分割。
    • 使用 System.arraycopy 动态合并结果。
  • 注意:分段加密可能影响性能,对于大文件通常结合对称加密(如 AES)使用 RSA 加密密钥。
  • 潜在问题与改进建议
  1. 签名算法安全性
    • MD5withRSA 使用 MD5 哈希算法,MD5 已知存在碰撞风险,建议升级为 SHA256withRSA 或更高(如 SHA512withRSA)。
  2. 密钥长度
    • 代码假设 1024 位密钥(128 字节),但未显式校验密钥长度。建议增加对 2048 位或更高密钥的支持,并动态计算 MAX_ENCRYPT_BLOCK 和 MAX_DECRYPT_BLOCK。
  3. 异常处理
    • 代码抛出通用 Exception,建议细化异常类型(如 InvalidKeyException、BadPaddingException),便于调试。
  4. 性能优化
    • 分段加密/解密的内存分配(byte[] temp)可能导致频繁 GC,建议使用 ByteArrayOutputStream 优化。
  5. 输入验证
    • 未对输入(data、privateKey、publicKey)进行空值或格式校验,可能导致运行时异常。建议添加输入验证。
  6. 填充模式
    • 代码使用默认填充(可能是 RSA/ECB/PKCS1Padding),建议显式指定填充模式以确保跨平台兼容性。

  1. 实际应用场景
  • 司法 API:根据包名(sfkc.thirdapi.judicial),可能用于与司法系统交互,如提交加密的案件数据、验证签名以确认数据来源。
  • 数据安全传输:客户端用公钥加密敏感数据(如身份证号、案件详情),服务器用私钥解密。
  • 身份验证:私钥签名数据,接收方用公钥验证,确保数据来自可信方。
  • 典型流程
    1. 客户端用服务器公钥加密数据,发送给服务器。
    2. 客户端用私钥对数据签名,服务器用客户端公钥验证签名。
    3. 服务器用私钥解密数据,处理后返回结果。

  1. 代码示例

以下是一个使用 KeyUtil 类的简单示例:

java

public class KeyUtilExample {
    public static void main(String[] args) throws Exception {
        // 示例公钥和私钥(Base64 编码,需替换为实际密钥)
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...";
        String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJd...";

        // 待加密的数据
        String data = "Hello, this is a test message!";
        byte[] dataBytes = data.getBytes();

        // 1. 公钥加密
        String encrypted = KeyUtil.encryptByPublicKey(dataBytes, publicKey);
        System.out.println("Encrypted: " + encrypted);

        // 2. 私钥解密
        byte[] decrypted = KeyUtil.decryptByPrivateKey(encrypted, privateKey);
        System.out.println("Decrypted: " + new String(decrypted));

        // 3. 私钥签名
        String signature = KeyUtil.sign(dataBytes, privateKey);
        System.out.println("Signature: " + signature);

        // 4. 公钥验证签名
        boolean verified = KeyUtil.verify(dataBytes, publicKey, signature);
        System.out.println("Signature verified: " + verified);
    }
}

  1. 总结

KeyUtil 是一个功能完备的 RSA 加密和签名工具类,适用于需要非对称加密和数字签名的场景。其主要特点包括:

  • 支持公钥/私钥加密解密。
  • 支持私钥签名和公钥验证。
  • 实现分段加密/解密,处理长数据。
  • 使用 Base64 编码便于密钥和数据的传输。

改进方向

  • 升级签名算法到更安全的 SHA-256。
  • 增强输入验证和异常处理。
  • 优化分段加密的性能。
  • 显式指定填充模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MonkeyKing.sun

对你有帮助的话,可以打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值