Springboot后端开发 · RSA加密传输

Aouos 发布于 2025-10-09 47 次阅读


关于RSA加密,虽然密码学的课还没有教到RSA加密,但是网上这么多资料还是很方便能学习一下RSA的原理的。

AI给我的代码是2048bit的长度的密钥,太安全了哥们,量子计算机来了都破解不了这一块。

算法描述变量表示
1.选择两个大质数p,qp,q
2.计算n=p*qn
3.欧拉公式φ(n)=(p-1)(q-1)φ(n)
4.选择一个整数e,使得1<e<φ(n),且e和φ(n)互质e
5.计算e关于φ(n)的模逆元d,即e*d≡1(mod φ(n))d
此时得到公钥pk=(e, n),私钥sk=(d, n)

给定明文M,加密过程如下:

  • 计算C≡Me (mod n),得到的C就是密文。

给定密文C,解密过程如下:

  • 计算M≡Cd (mod n),得到的M就是解密后的明文。

来证明一下这个算法:

C≡Me (mod n)≡(Cd (mod n))e(mod n)≡Cde(mod n)

而由于e*d≡1(mod φ(n)) 带入我们们可以得到C1+kφ(n)(mod n)

所以我们要证明C≡C1+kφ(n)(mod n)

1.假设C与n互质

原式=C1+kφ(n)(mod n)=C*Ckφ(n)(mod n)

欧拉定理指出,如果 C 和 n 互素,则 C 的 ϕ(n) 次幂模 n 等于 1。

所以我们得到Cϕ(n)≡1(mod n) 所以(Cϕ(n))k(mod n)≡1 (mod n)

得证C*Ckφ(n)(mod n)=C*1 (mod n)=C

2.假设C与n不互质

设 C 是 p 的倍数,则 C = xp,其中 x 为正整数, 那么 gcd(m, q) = 1,由欧拉定理得:

Cφ(q) ≡ 1(mod q)

那么

Cφ(q) ≡ 1(mod q)

(Cφ(q))φ(p) ≡ 1φ(p) (mod q)

(Cφ(q))φ(p) ≡ 1 (mod q)

Ckφ(n) ≡ 1(mod q)

所以,会存在一个整数 r,使得 Ckφ(n) ≡ 1 + rq ,两边同时乘以 C = xp 得:

Ckφ(n)+1 = C + rxpq = C + rxn

即 Ckφ(n)+1 ≡C (mod n) 得证

搞定了证明,我们就可以把他搬进我们的代码里了。

/RsaUtil.java
package com.example.newtest.util;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtil {

    // 生成RSA密钥对
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    // 获取公钥字符串
    public static String getPublicKeyString(PublicKey publicKey) {
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }

    // 从字符串加载公钥
    public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(spec);
    }

    // 从字符串加载私钥
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(spec);
    }

    // RSA加密
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    // RSA解密
    public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.getDecoder().decode(encryptedData);
        byte[] decryptedBytes = cipher.doFinal(dataBytes);
        return new String(decryptedBytes, "UTF-8");
    }
}

this is RSA密码生成器,这里封装了6个函数,都是用于RSA加密解密功能的实现。


@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;
    @Autowired
    private HttpServletRequest request;

    // 移除静态密钥对

    // 获取RSA公钥接口
    @GetMapping("/public-key")
    public Map<String, String> getPublicKey(HttpSession session) throws Exception {
        // 为每个session生成独立的密钥对
        KeyPair keyPair = RsaUtil.generateKeyPair();
        session.setAttribute("rsaKeyPair", keyPair);

        Map<String, String> result = new HashMap<>();
        result.put("publicKey", RsaUtil.getPublicKeyString(keyPair.getPublic()));
        return result;
    }

    @PostMapping("/login")
    public result login(@RequestBody user user, HttpSession session) {
        String username = user.getUsername();
        String password = user.getPassword();
        String captcha = user.getCaptcha();

        // 验证验证码
        String sessionCaptcha = (String) session.getAttribute("captcha");
        if (sessionCaptcha == null || !sessionCaptcha.equalsIgnoreCase(captcha)) {
            return result.captchaError();
        }

        // 验证成功后清除session中的验证码
        session.removeAttribute("captcha");

        try {
            // 从session获取对应的密钥对
            KeyPair keyPair = (KeyPair) session.getAttribute("rsaKeyPair");
            if (keyPair == null) {
                return result.error("密钥已过期,请刷新页面重试");
            }

            // 使用私钥解密密码
            String decryptedPassword = RsaUtil.decrypt(password, keyPair.getPrivate());
            // 解密后立即清除密钥对
            session.removeAttribute("rsaKeyPair");

            return loginService.login(username, decryptedPassword);
        } catch (Exception e) {
            e.printStackTrace();
            return result.error("密码解密失败,请重试");
        }
    }
}

LoginController这边代码很清楚调用函数解密验证,但是做到这里我感觉到全部把密钥对放入session中是不安全的,所以考虑使用静态的密钥对,让私钥不离开服务器会更加安全。AI给出的方案是使用密钥池,但是我感觉严重拖累性能了,需要尝试所有的私钥解密,这是人类能想出来的方法吗?好吧AI确实不是人类,改用单密钥定期更换会好一点。

做到这里其实也是可以的,但是我在实装之后发现:哇!怎么卡成这个样子了,整个登录会变慢了特别多,RSA虽然是非常安全,但是运算量太大会导致严重影响的服务器性能。所以要使用AES加密数据,然后将密钥经过RSA公钥加密后传输到服务器,这样既保证了安全,也减少了对性能的影响。

全都不会写!
最后更新于 2025-12-25