я вроде застрял с этим исключением:RuntimeException при закрытии CipherInputStream
java.lang.RuntimeException: error:0407806d:RSA routines:decrypt:DATA_LEN_NOT_EQUAL_TO_MOD_LEN
at com.android.org.conscrypt.NativeCrypto.RSA_private_decrypt(Native Method)
at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:274)
at javax.crypto.Cipher.doFinal(Cipher.java:1440)
at javax.crypto.CipherInputStream.close(CipherInputStream.java:190)
...
Это брошено, когда я закрываю CipherInputStream на Android Зефир. Кажется, что все работает с более ранними версиями Android.
Что означает DATA_LEN_NOT_EQUAL_TO_MOD_LEN
означает? Почему он, кажется, расшифровывает (вызывает RSA_private_decrypt
), когда он должен освобождать дескрипторы ресурсов (закрыть)?
UPDATE:
мне удалось воспроизвести ошибку с некоторым тестовым кодом. Он шифрует и расшифровывает "foobar"
. Один раз использовать шифр напрямую и один раз через CipherInputStream (как это сделано в оригинальном приложении).
Все работает на андроид < 6 и коды непотоковых даже работая на андроид 6. я был в состоянии получить код потокового работать на Android 6, когда я изменил явный шифр RSA/ECB/PKCS1Padding
к родовым RSA
. Но я бы поспорить, что это там по причине;)
static final String RSA_ALGO = "RSA/ECB/PKCS1Padding";
// static final String RSA_ALGO = "RSA";
private void _testCrypto2() throws Exception {
KeyPairGenerator keyGen;
KeyPair keys;
byte[] encrypted;
byte[] decrypted;
String input;
String output;
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
keys = keyGen.generateKeyPair();
input = "foobar";
// Plain crypto.
encrypted = this.RSAEncrypt(input, keys.getPublic());
output = this.RSADecrypt(encrypted, keys.getPrivate());
// Streaming crypto.
encrypted = this.RSAEncryptStream(input, keys.getPublic());
output = this.RSADecryptStream(encrypted, keys.getPrivate());
}
public byte[] RSAEncrypt(final String plain, PublicKey _publicKey) throws Exception {
byte[] encryptedBytes;
Cipher cipher;
cipher = Cipher.getInstance(RSA_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, _publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
return encryptedBytes;
}
public String RSADecrypt(final byte[] encryptedBytes, PrivateKey _privateKey) throws Exception {
Cipher cipher;
byte[] decryptedBytes;
String decrypted;
cipher = Cipher.getInstance(RSA_ALGO);
cipher.init(Cipher.DECRYPT_MODE, _privateKey);
decryptedBytes = cipher.doFinal(encryptedBytes);
decrypted = new String(decryptedBytes);
return decrypted;
}
public byte[] RSAEncryptStream(final String _plain, PublicKey _publicKey) throws Exception {
Cipher cipher;
InputStream in;
ByteArrayOutputStream out;
int numBytes;
byte buffer[] = new byte[0xffff];
in = new ByteArrayInputStream(_plain.getBytes());
out = new ByteArrayOutputStream();
cipher = Cipher.getInstance(RSA_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, _publicKey);
try {
in = new CipherInputStream(in, cipher);
while ((numBytes = in.read(buffer)) != -1) {
out.write(buffer, 0, numBytes);
}
}
finally {
in.close();
}
return out.toByteArray();
}
public String RSADecryptStream(final byte[] _encryptedBytes, PrivateKey _privateKey) throws Exception {
Cipher cipher;
InputStream in;
ByteArrayOutputStream out;
int numBytes;
byte buffer[] = new byte[0xffff];
in = new ByteArrayInputStream(_encryptedBytes);
out = new ByteArrayOutputStream();
cipher = Cipher.getInstance(RSA_ALGO);
cipher.init(Cipher.DECRYPT_MODE, _privateKey);
try {
in = new CipherInputStream(in, cipher);
while ((numBytes = in.read(buffer)) != -1) {
out.write(buffer, 0, numBytes);
}
}
finally {
in.close();
}
return new String(out.toByteArray());
}
Однако, похоже, есть два направления для исправления:
- Избавление от потокового для RSA
- Удаление явное кодирование RSA-кода.
Как вы думаете?
Что еще более важно, почему вы хотите использовать шифр RSA вместе с 'InputStream'? RSA не подходит для шифрования/дешифрования потоков данных. Эта проблема может быть своего рода изменением для поддержки аутентифицированных шифров. Аутентифицированные шифры не играют хорошо с входными потоками, а IMHO использует испорченный API. После прочтения данных из потока может потребоваться проверка тега аутентификации и/или заполнения. Это было бы хорошей причиной, чтобы называть 'doFinal' –
@MaartenBodewes спасибо. Это просто байт-поток с зашифрованным секретным ключом для AES, поэтому это не проблема количества данных. Но импликация может быть правильной. Нет смысла использовать поток, если для дешифрования существует только одно слово. Я обновляю ответ с помощью некоторого тестового кода. – lupz