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

Python与Kotlin的RSA加密无法互解问题求助

RSA跨语言加密解密不互通问题排查与修正

你遇到的问题核心是Python和Kotlin两端的RSA加密参数不匹配,导致互相解密时填充校验失败。以下是具体的问题点和解决方案:

1. 填充方式不匹配(最常见原因)

RSA加密的填充方案必须两端完全一致,否则必然出现填充错误:

  • 如果Python用PKCS1v15填充,Kotlin必须对应RSA/ECB/PKCS1Padding;
  • 如果用更安全的OAEP填充,两端必须同步哈希算法(如SHA-256)和MGF1参数。

示例修正(PKCS1v15方案)

Python端(cryptography库)

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

# 加密(明确指定PKCS1v15填充)
def rsa_encrypt(plaintext: str, pub_key_pem: str) -> str:
    pub_key = serialization.load_pem_public_key(pub_key_pem.encode('utf-8'))
    encrypted_bytes = pub_key.encrypt(
        plaintext.encode('utf-8'),
        padding.PKCS1v15()  # 与Kotlin端填充一致
    )
    return base64.b64encode(encrypted_bytes).decode('utf-8')

# 解密
def rsa_decrypt(ciphertext: str, priv_key) -> str:
    encrypted_data = base64.b64decode(ciphertext)
    decrypted_bytes = priv_key.decrypt(
        encrypted_data,
        padding.PKCS1v15()
    )
    return decrypted_bytes.decode('utf-8')

Kotlin端(原生加密模块)

import android.util.Base64
import java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher

object RSAHelper {
    // 与Python端PKCS1v15完全对应的转换字符串
    private const val RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"

    // 解析Python传来的PEM格式公钥
    fun loadPemPublicKey(pemStr: String): java.security.PublicKey {
        val cleanPem = pemStr.replace("-----BEGIN PUBLIC KEY-----", "")
                            .replace("-----END PUBLIC KEY-----", "")
                            .replace("\\s+".toRegex(), "")
        val keyBytes = Base64.decode(cleanPem, Base64.DEFAULT)
        val spec = X509EncodedKeySpec(keyBytes)
        return KeyFactory.getInstance("RSA").generatePublic(spec)
    }

    // 加密
    fun encrypt(plaintext: String, publicKey: java.security.PublicKey): String {
        val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
        cipher.init(Cipher.ENCRYPT_MODE, publicKey)
        val encryptedBytes = cipher.doFinal(plaintext.toByteArray(Charsets.UTF_8))
        return Base64.encodeToString(encryptedBytes, Base64.DEFAULT)
    }

    // 解密(需传入对应私钥)
    fun decrypt(ciphertext: String, privateKey: java.security.PrivateKey): String {
        val cipher = Cipher.getInstance(RSA_TRANSFORMATION)
        cipher.init(Cipher.DECRYPT_MODE, privateKey)
        val encryptedBytes = Base64.decode(ciphertext, Base64.DEFAULT)
        val decryptedBytes = cipher.doFinal(encryptedBytes)
        return String(decryptedBytes, Charsets.UTF_8)
    }
}

若使用OAEP更安全方案

Python端调整

# 加密/解密时替换填充为OAEP
padding.OAEP(
    mgf=padding.MGF1(algorithm=hashes.SHA256()),
    algorithm=hashes.SHA256(),
    label=None
)

Kotlin端调整

// 转换字符串同步为OAEP+SHA256
private const val RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
// 部分Android版本需添加BC提供者:"RSA/ECB/OAEPWithSHA-256AndMGF1Padding,BC"

2. 密钥格式不兼容

  • Python默认使用PEM格式密钥(带-----BEGIN PUBLIC KEY-----头),Kotlin需先清理PEM头再解析为X509格式;
  • 确保两端密钥长度一致(如均为2048位或4096位),长度不匹配会直接导致解密失败。

3. 加密内容长度超限

RSA加密有明文长度限制:

  • 2048位RSA+PKCS1v15:最大明文长度为245字节(2048/8 - 11);
  • 2048位RSA+OAEP:最大明文长度为214字节(2048/8 - 2*SHA256长度 - 2);
    若明文超出长度,会导致加密截断,解密时触发填充错误。

4. 编码方式不一致

加密前的字符串转字节时,两端必须统一使用UTF-8编码,避免因编码差异导致字节流不匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 05:39:55