IllegalBlockSizeException "null" в расшифровке RSA на Android
В настоящее время я работаю на Android-клиенте моего программного обеспечения для шифрования, но я продолжал получать IllegalBlockSizeException
, а также e.getMessage()
всегда возвращается null
Вот код, который я использовал, чтобы найти проблему
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore");
generator.initialize(new KeyGenParameterSpec.Builder(
"1",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setDigests(KeyProperties.DIGEST_SHA256)
.setKeySize(2048)
.build());
KeyPair kp = generator.generateKeyPair();
KeyStore store = KeyStore.getInstance("AndroidKeyStore");
store.load(null);
PublicKey pubKey = store.getCertificate("1").getPublicKey();
PrivateKey privkey = ((KeyStore.PrivateKeyEntry) store.getEntry("1",null)).getPrivateKey();
byte[] content = "123456789".getBytes();
Cipher encrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
encrypt.init(Cipher.ENCRYPT_MODE,pubKey);
Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
decrypt.init(Cipher.DECRYPT_MODE,privkey);
byte[] A = encrypt.doFinal(content);
byte[] B = decrypt.doFinal(A);
String resultA = new String(A);
String resultB = new String(B);
Toast.makeText(getApplicationContext(), resultA + "\n" + resultB, Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.toString()+"\n"+e.getMessage(), Toast.LENGTH_LONG).show();
}
Как я уже сказал, был IllegalBlockSizeException
который я нашел был брошен decrypt.doFinal()
, а также e.getMessage()
возвращается null
Вот что я получаю из консоли отладки
W/System.err: javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
at javax.crypto.Cipher.doFinal(Cipher.java:1741)
W/System.err: at storm.cyanine.decryptor.MainActivity$override.onJob(MainActivity.java:187)
at storm.cyanine.decryptor.MainActivity$override.onOpen(MainActivity.java:115)
at storm.cyanine.decryptor.MainActivity$override.access$dispatch(Unknown Source:50)
at storm.cyanine.decryptor.MainActivity.onOpen(Unknown Source:15)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
W/System.err: at android.view.View.performClick(View.java:6329)
at android.view.View$PerformClick.run(View.java:24996)
at android.os.Handler.handleCallback(Handler.java:809)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7377)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)
W/System.err: Caused by: android.security.KeyStoreException: Unknown error
at android.security.KeyStore.getKeyStoreException(KeyStore.java:709)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
... 16 more
Сначала я думал, что это было какое-то ограничение AndroidKeyStore
это мешает мне использовать закрытый ключ, поэтому я попытался использовать тот, который был сгенерирован KeyPairGenerator
но ничего не изменилось
Что может вызвать эту проблему?
Я искал в Интернете в течение нескольких дней, и я не нашел ничего плохого в приведенном выше коде (кроме того, что я не являюсь конечным продуктом)
Мое устройство Android 8.1.0
Изменить: Большое спасибо Стив Miskovetz за решение, и я только что нашел официальное руководство Android для этой темы: Криптография
(не знаю, почему я не смог найти это раньше)
3 ответа
Похоже, что ваша проблема была представлена с Android Oreo, но есть обходной путь.
Этот пост в стеке потока обсуждает это:
Android 8.0: IllegalBlocksizeException при использовании RSA/ECB/OAEPWithSHA-512AndMGF1Padding
У этого трекера Google есть хорошее обсуждение:
https://issuetracker.google.com/issues/36708951
Вам нужно добавить эту строку:
OAEPParameterSpec sp = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
И измените эти строки, добавив параметр sp:
encrypt.init(Cipher.ENCRYPT_MODE,pubKey,sp);
decrypt.init(Cipher.DECRYPT_MODE,privkey,sp);
Ваш полный код с изменениями здесь:
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore");
generator.initialize(new KeyGenParameterSpec.Builder(
"1",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setDigests(KeyProperties.DIGEST_SHA256)
.setKeySize(2048)
.build());
KeyPair kp = generator.generateKeyPair();
KeyStore store = KeyStore.getInstance("AndroidKeyStore");
store.load(null);
PublicKey pubKey = store.getCertificate("1").getPublicKey();
PrivateKey privkey = ((KeyStore.PrivateKeyEntry) store.getEntry("1",null)).getPrivateKey();
byte[] content = "123456789".getBytes();
OAEPParameterSpec sp = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
Cipher encrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
encrypt.init(Cipher.ENCRYPT_MODE,pubKey,sp);
Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
decrypt.init(Cipher.DECRYPT_MODE,privkey,sp);
byte[] A = encrypt.doFinal(content);
byte[] B = decrypt.doFinal(A);
String resultA = new String(A);
String resultB = new String(B);
Toast.makeText(getApplicationContext(), resultA + "\n" + resultB, Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), e.toString()+"\n"+e.getMessage(), Toast.LENGTH_LONG).show();
}
Шифрование асимметричного ключа доступно с Android 6+(API 23+). Это хорошее руководство для шифрования Android. Кроме того, после вызова getBytes необходимо кодировать байты с помощью Base64.
Итак, я сделал еще несколько тестов... получается, что: PrivateKey
независимо от того, был ли он сгенерирован KeyPairGenerator
, импортированные из файла или импортированные из KeyStore
, не может быть использован для расшифровки, когда вы используете AndroidKeyStore
в качестве поставщика, даже если у вас есть KeyProperties.PURPOSE_ENCRYPT
а также KeyProperties.PURPOSE_DECRYPT
свойства добавлены.
Тем не мение, SecretKey
(который является симметричным алгоритмом) не имеет проблем для использования для шифрования / дешифрования при сохранении / генерировании в AndroidKeyStore
,
Так выглядит AndroidKeyStore
в конце концов, не было разрешено шифрование / дешифрование в асимметричном алгоритме, что не было четко объяснено в руководстве по системе хранилища ключей Android.
Если кто-то хочет использовать асимметричный алгоритм шифрования / дешифрования с AndroidKeyStore
Вот код, который я использовал для тестирования. Зашифровывает строку "123456789"
а затем расшифровывает его, затем показывает расшифрованную строку в тосте, который должен быть "123456789"
Работал хорошо для меня
String alias = "2";
KeyGenerator generator = KeyGenerator.getInstance("AES","AndroidKeyStore");
generator.init(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setKeySize(128)
.build());
generator.generateKey();
KeyStore store = KeyStore.getInstance("AndroidKeyStore");
store.load(null);
SecretKey secret = ((KeyStore.SecretKeyEntry)store.getEntry(alias,null)).getSecretKey();
byte[] content = "123456789".getBytes();
Cipher encrypt = Cipher.getInstance("AES/CBC/PKCS7Padding");
encrypt.init(Cipher.ENCRYPT_MODE,secret);
Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS7Padding");
decrypt.init(Cipher.DECRYPT_MODE,secret,new IvParameterSpec(encrypt.getIV()));
byte[] A = encrypt.doFinal(content);
byte[] B = decrypt.doFinal(A);
String resultA = new String(A);
String resultB = new String(B);
Toast.makeText(getApplicationContext(), resultB, Toast.LENGTH_LONG).show();