PHP RSA加密后安卓端解密异常问题求助
解决安卓RSA公钥与PHP跨平台加解密不匹配问题
我之前也踩过不少RSA跨平台加解密的坑,结合你给出的代码和报错情况,核心问题大概率是填充方式不一致或者公钥解析错误导致的,咱们一步步来排查和解决:
先明确你的现有代码(方便对照)
安卓端密钥生成代码
// 安卓端密钥生成代码(你提供的原代码) KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); // 你提供的公钥格式示例 String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxQ...(完整公钥内容)";
PHP端加密逻辑(提取模数指数)
// PHP端加密代码(你原逻辑) require_once('Crypt/RSA.php'); $rsa = new Crypt_RSA(); $rsa->loadKey(array( 'n' => new Math_BigInteger(base64_decode($modulus), 256), 'e' => new Math_BigInteger(base64_decode($exponent), 256) )); $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); $ciphertext = $rsa->encrypt($plaintext); $encryptedStr = base64_encode($ciphertext);
安卓端解密代码
// 安卓端解密代码(你提供的原代码) Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(Base64.decode(encryptedStr, Base64.DEFAULT)); String decryptedStr = new String(decryptedBytes);
核心问题分析与解决方案
1. 填充方式不匹配(最常见原因)
安卓中Cipher.getInstance("RSA")是不明确指定填充方式的写法,不同Android厂商的默认实现可能不一样:有的是RSA/ECB/PKCS1Padding,有的可能是RSA/ECB/NoPadding。而你PHP里用的是CRYPT_RSA_ENCRYPTION_PKCS1(对应PKCS1填充),两者不一致就会导致解密失败或乱码。
修正方案:安卓解密时明确指定填充方式,和PHP对齐:
try { // 强制指定PKCS1填充,和PHP的加密模式匹配 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); // 用NO_WRAP避免Base64换行导致的解析问题 byte[] encryptedBytes = Base64.decode(encryptedStr, Base64.NO_WRAP); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); // 明确指定UTF-8编码,避免字符集不一致导致乱码 String decryptedStr = new String(decryptedBytes, StandardCharsets.UTF_8); Log.d("DecryptSuccess", "解密结果:" + decryptedStr); } catch (Exception e) { e.printStackTrace(); }
2. 公钥手动提取模数/指数出错
你手动从安卓公钥提取n和e的过程很容易出错——安卓的公钥是X.509格式的ASN.1结构,手动截取字符串或解析二进制很容易搞混编码、长度。一旦公钥解析错误,PHP加密出来的密文就是无效的,安卓解密时自然会出现数组越界(密文长度不符合2048位RSA的256字节要求)。
更稳妥的方案:直接把安卓的完整公钥传给PHP,用phpseclib直接加载,不用手动提取n和e:
require_once('Crypt/RSA.php'); $rsa = new Crypt_RSA(); // 给安卓的Base64公钥补上头尾(如果你的公钥没有的话) $androidPublicKey = "-----BEGIN PUBLIC KEY-----\n" . $yourAndroidPublicKeyStr . "\n-----END PUBLIC KEY-----"; $rsa->loadKey($androidPublicKey); $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); // 可选:指定哈希算法,确保跨平台一致性 $rsa->setHash('sha256'); $plaintext = "测试加密内容"; $ciphertext = $rsa->encrypt($plaintext); // 直接输出Base64编码的密文,供安卓解密 echo base64_encode($ciphertext);
3. 其他注意事项
- RSA加密长度限制:2048位RSA用PKCS1填充时,最多只能加密245字节左右的明文(因为填充占了11字节),如果要加密大内容,建议用AES对称加密,再用RSA加密AES密钥。
- 字符编码一致性:安卓和PHP都要统一用UTF-8编码处理字符串转字节数组,避免乱码。
- Base64编码差异:安卓的
Base64.DEFAULT会自动换行,PHP的base64_encode不会,所以安卓解密时用Base64.NO_WRAP更稳妥。
内容的提问来源于stack exchange,提问作者Introser
相关产品推荐
相关产品推荐

