You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS与Python非对称加解密报错:密文长度需匹配密钥长度

解决RSA非对称加密流程中的ValueError: Ciphertext length must be equal to key size错误

问题根源

  • 密钥对不匹配:Flask路由每次处理请求都会生成新的密钥对,POST请求解密用的私钥和GET请求传给前端的公钥不是同一组,导致无法解密。
  • 填充算法不一致:前端使用OAEP-SHA256加密,后端却用PKCS1_v1_5解密,两者不兼容。
  • 密文双重编码错误:JSEncrypt的encrypt方法已返回Base64编码结果,前端额外调用btoa再次编码,后端未正确解码两次,导致解密时传入的不是正确的加密字节。

修正后的代码

1. Python加密解密函数(统一使用OAEP填充)

from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding
import base64

def decrypt(private_key, ciphertext_b64):
    # 解码前端的双重Base64编码
    ciphertext = base64.b64decode(base64.b64decode(ciphertext_b64))
    private_key_obj = serialization.load_pem_private_key(private_key, password=None)
    
    # 使用与前端匹配的OAEP-SHA256解密
    decrypted_data = private_key_obj.decrypt(
        ciphertext,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return decrypted_data.decode('utf-8')

def generate_keypair():
    private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    public_key = private_key.public_key()

    pem_private_key = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    
    pem_public_key = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )
    
    return pem_private_key, pem_public_key

2. Flask路由(保存密钥对到Session,确保前后请求密钥一致)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if session.get('username'):
        return redirect(url_for('index'))
    
    # GET请求生成密钥对并存入Session
    if request.method == 'GET':
        private_key, public_key = generate_keypair()
        session['private_key'] = private_key.decode('utf-8')
        return render_template('login.html', error_message='Not Authorised', public_key=public_key.decode('utf-8'))
    
    if form.validate_on_submit():
        username = form.username.data
        encrpassword = form.password.data
        # 从Session取出对应私钥
        private_key = session.get('private_key').encode('utf-8')
        
        try:
            password = decrypt(private_key=private_key, ciphertext_b64=encrpassword)
            print(f'username {username} password {password}')
            access, role = get_user_access_privilege(username)
            # 后续业务逻辑...
        except Exception as e:
            print(f"解密失败: {str(e)}")
            # 错误处理逻辑

3. JavaScript代码(移除多余的btoa编码)

<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.3.2/jsencrypt.js"></script>
<script>
    var encryptor = new JSEncrypt();
    function encryptAndSubmit(e) {
        e.preventDefault(); // 阻止默认提交
        
        var password = document.getElementById("password").value;
        var publicKey = "{{ public_key }}";

        encryptor.setPublicKey(publicKey);
        // JSEncrypt返回的已是Base64密文,无需二次编码
        var encryptedPassword = encryptor.encrypt(password, 'sha256');
        
        document.getElementById("password").value = encryptedPassword;
        document.getElementById("LoginForm").submit();
    }

    document.getElementById("LoginForm").addEventListener("submit", encryptAndSubmit);
</script>

额外注意事项

  • 确保Flask已配置Session密钥:app.secret_key = 'your-secure-secret-key',否则无法保存私钥。
  • 生产环境可优化密钥生成逻辑,避免每次GET请求生成新密钥,可采用缓存或固定密钥(固定密钥需做好安全防护)。

内容的提问来源于stack exchange,提问作者Prawal Lamshal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:50:25