Android与OpenSSL公钥加密结果不一致的原因排查
在Android中通过以下代码使用PEM格式公钥加密字符串:
public static String encryptDataWithPEM(String data, String publicKeyPEM) { try { PublicKey publicKey = getPublicKeyFromPEM(publicKeyPEM); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encryptedData = cipher.doFinal(data.getBytes()); return Base64.getEncoder().encodeToString(encryptedData); } catch (Exception e) { e.printStackTrace(); } return null; } private static PublicKey getPublicKeyFromPEM(String publicKeyPEM) { try { PEMParser pemParser = new PEMParser(new StringReader(publicKeyPEM)); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); Object pemObject = pemParser.readObject(); if (pemObject != null) { return converter.getPublicKey((org.bouncycastle.asn1.x509.SubjectPublicKeyInfo) pemObject); } else { throw new IllegalArgumentException("Invalid PEM format: RSA public key expected"); } } catch (Exception e) { e.printStackTrace(); } return null; }
同时在Debian控制台执行以下OpenSSL命令重复加密操作:
echo -n "Your data to encrypt" | openssl rsautl -encrypt -pubin -inkey public_key.pem | base64
为何Android与OpenSSL的加密结果不同?
加密结果不一致通常由以下因素导致:
字符编码不统一
Android代码中data.getBytes()默认采用UTF-8编码,但如果OpenSSL所在系统的默认字符编码不是UTF-8,会导致原始数据的字节数组存在差异。解决办法是在Android代码中明确指定UTF-8编码:byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);Base64编码格式差异
Android的Base64.getEncoder()输出无换行的标准Base64字符串,而OpenSSL的base64命令默认每64个字符添加换行符,导致最终输出字符串不同。修改OpenSSL命令,添加-A参数输出单行Base64:echo -n "Your data to encrypt" | openssl rsautl -encrypt -pubin -inkey public_key.pem | base64 -A公钥格式或解析不匹配
如果OpenSSL使用的是PKCS#1格式公钥(头为-----BEGIN RSA PUBLIC KEY-----),而Android代码仅支持解析X.509格式公钥(头为-----BEGIN PUBLIC KEY-----),会导致解析出的公钥不一致。可以将OpenSSL的公钥转换为X.509格式:openssl rsa -pubin -in public_key.pem -outform PEM -out public_key_x509.pemPKCS#1 v1.5填充的随机性(正常现象)
RSA使用PKCS1Padding时,加密过程会引入随机生成的填充字节,这是安全规范要求的,目的是避免相同明文加密后得到相同密文。因此即使是同一明文、同一公钥,Android和OpenSSL的加密结果不同是正常现象,只要两边都能正确解密即可验证正确性。
内容的提问来源于stack exchange,提问作者Aina T

