Android生成的RSA公钥经WebCrypto加密后解密抛IllegalBlockSizeException求助
问题:跨端RSA OAEP加密后Android解密抛出
IllegalBlockSizeException 我生成RSA密钥对,将公钥Base64编码后存入数据库,Chrome扩展取出公钥加密数据,但加密后的数据在Android端解密时,cipher.doFinal(encryptedData)抛出javax.crypto.IllegalBlockSizeException异常。Android自身加密的数据可正常解密,双方使用的是RSA/NONE/OAEPwithSHA-256andMGF1Padding算法。
Android端生成密钥与导出公钥代码
class EnCryptor { private static final String TRANSFORMATION = "RSA/NONE/OAEPwithSHA-256andMGF1Padding"; private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private KeyStore keyStore; private byte[] encryption; EnCryptor() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { initKeyStore(); } private void initKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); } byte[] encryptText(final String alias, final String textToEncrypt) throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidAlgorithmParameterException, SignatureException, BadPaddingException, IllegalBlockSizeException { final Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(alias)); return (encryption = cipher.doFinal(textToEncrypt.getBytes("UTF-8"))); } @NonNull private PublicKey getSecretKey(final String alias) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, UnrecoverableEntryException, KeyStoreException { if (keyStore.getCertificate(alias) != null){ PublicKey pubKey = keyStore.getCertificate(alias).getPublicKey(); String publicKeyString = Base64.encodeToString(pubKey.getEncoded(), 2); return keyStore.getCertificate(alias).getPublicKey(); } else { final KeyPairGenerator keyPairGenerator = KeyPairGenerator .getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE); keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) .setKeySize(2048) .setDigests(KeyProperties.DIGEST_SHA256) .build()); PublicKey pubKey = keyPairGenerator.genKeyPair().getPublic(); String publicKeyString = Base64.encodeToString(pubKey.getEncoded(), 2); return keyPairGenerator.genKeyPair().getPublic(); } } }
Chrome扩展加密代码
function encryptUsingImportedKey(pubKey,data){ const fromBase64 = buffer => Uint8Array.from(atob(buffer), c => c.charCodeAt(0)); var pubKeyArray = fromBase64(pubKey); keyMaterial = await window.crypto.subtle.importKey( 'spki', pubKeyArray, {name: 'RSA-OAEP', hash: 'SHA-256',}, false, ['encrypt'] ); const encoder = new TextEncoder(); const encoded_data = encoder.encode(data); const toBase64 = buffer => btoa(String.fromCharCode(...new Uint8Array(buffer))); var encrypted_array = await window.crypto.subtle.encrypt( { name: "RSA-OAEP", }, keyMaterial, encoded_data ) var encrypted = toBase64(encrypted_array); }
Android端解密代码
class DeCryptor { private static final String TRANSFORMATION = "RSA/NONE/OAEPwithSHA-256andMGF1Padding"; private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private KeyStore keyStore; DeCryptor() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { initKeyStore(); } private void initKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); } String decryptData(final String alias, final byte[] encryptedData) throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { final Cipher cipher = Cipher.getInstance(TRANSFORMATION); KeyStore.Entry secretKey = keyStore.getEntry(alias, null); cipher.init(Cipher.DECRYPT_MODE, getSecretKey(alias)); byte[] decryptData = cipher.doFinal(encryptedData); return new String(decryptData, "UTF-8"); } private PrivateKey getSecretKey(final String alias) throws NoSuchAlgorithmException, UnrecoverableEntryException, KeyStoreException { return ((KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null)).getPrivateKey(); } }
问题根源分析
- 密钥生成重复:Android端
getSecretKey方法中,生成新密钥对时调用了两次keyPairGenerator.genKeyPair(),导致数据库存储的公钥与AndroidKeyStore中实际保存的密钥对不匹配,Chrome用错误公钥加密的数据自然无法被正确私钥解密。 - 异步函数错误:Chrome扩展的
encryptUsingImportedKey使用了await但未声明为async,导致语法错误,加密逻辑无法正常执行,返回的密文数据无效。 - 变量未声明/返回:Chrome代码中
keyMaterial、encrypted_array等变量未用const/let声明,且未返回加密结果,调用方无法获取有效密文。
修复方案
1. 修复Android端密钥生成问题
修改getSecretKey方法,只生成一次密钥对:
@NonNull private PublicKey getSecretKey(final String alias) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, UnrecoverableEntryException, KeyStoreException { if (keyStore.getCertificate(alias) != null){ PublicKey pubKey = keyStore.getCertificate(alias).getPublicKey(); String publicKeyString = Base64.encodeToString(pubKey.getEncoded(), Base64.NO_WRAP); return pubKey; } else { final KeyPairGenerator keyPairGenerator = KeyPairGenerator .getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE); keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) .setKeySize(2048) .setDigests(KeyProperties.DIGEST_SHA256) .build()); KeyPair keyPair = keyPairGenerator.genKeyPair(); // 仅生成一次密钥对 PublicKey pubKey = keyPair.getPublic(); String publicKeyString = Base64.encodeToString(pubKey.getEncoded(), Base64.NO_WRAP); return pubKey; } }
2. 修复Chrome扩展异步与变量问题
async function encryptUsingImportedKey(pubKey, data){ const fromBase64 = buffer => Uint8Array.from(atob(buffer), c => c.charCodeAt(0)); const pubKeyArray = fromBase64(pubKey); const keyMaterial = await window.crypto.subtle.importKey( 'spki', pubKeyArray, {name: 'RSA-OAEP', hash: 'SHA-256'}, false, ['encrypt'] ); const encoder = new TextEncoder(); const encoded_data = encoder.encode(data); const toBase64 = buffer => btoa(String.fromCharCode(...new Uint8Array(buffer))); const encrypted_array = await window.crypto.subtle.encrypt( {name: "RSA-OAEP"}, keyMaterial, encoded_data ); const encrypted = toBase64(encrypted_array); return encrypted; // 返回加密结果 }
3. 确保解密时Base64解码正确
Android端接收Chrome返回的Base64密文后,需用对应模式解码:
// 假设传入的encrypted是Chrome返回的Base64字符串 byte[] encryptedData = Base64.decode(encrypted, Base64.NO_WRAP); // 再调用decryptData方法解密
验证要点
- 对比数据库中存储的公钥与AndroidKeyStore中密钥对的公钥Base64值,确保一致。
- 检查Chrome加密后的密文长度:2048位RSA OAEP加密后密文应为256字节,Base64编码后约344字符,长度不符则说明加密逻辑异常。
内容的提问来源于stack exchange,提问作者AmInO
相关产品推荐
相关产品推荐

