IllegalBlockSizeException "null" in RSA decryption on Android - android

I'm currently working on an Android client of my encryption software, but I kept getting IllegalBlockSizeException, and e.getMessage() always returns null
Here's the code I used to find the problem
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();
}
As I said there was an IllegalBlockSizeException, which I found was thrown by decrypt.doFinal() , and e.getMessage() returns null
Here's what I get from the debug console
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
At first, I thought it was some restriction of AndroidKeyStore that prevents me from using the private key, so I tried using directly the one generated by KeyPairGenerator but nothing changed
What could cause this problem?
I've been searching online for days and I found nothing wrong with the code above (apart from not being the final product)
My device is Android 8.1.0
Edit: Thanks Steve Miskovetz a lot for the solution, and I just found the Android official guide for this topic:
Cryptography
(don't know why I wasn't able to find this earlier)

It looks like your issue was introduced with Android Oreo, but has a workaround.
This stackoverflow post discusses it:
Android 8.0: IllegalBlocksizeException when using RSA/ECB/OAEPWithSHA-512AndMGF1Padding
This Google issue tracker has good discussion on it:
https://issuetracker.google.com/issues/36708951
You need to add this line:
OAEPParameterSpec sp = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
And modify these lines, adding the sp parameter:
encrypt.init(Cipher.ENCRYPT_MODE,pubKey,sp);
decrypt.init(Cipher.DECRYPT_MODE,privkey,sp);
Your full code with modifications here:
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();
}

asymmetric key encryption available from android 6+( api 23+). This is a good guide for android's encryption. Additionsly after getBytes called you need to encode bytes with Base64.

Related

Decrypting message with RSA-OAEP and Android Keystore: IllegalBlockSizeException

I have the following code inside a try block that should generate a RSA public/private keypair use the public key to encrypt a message and decrypt again with the private key:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(
"key1",
KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.build());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
byte[] src = "hello world".getBytes();
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] cipherData = cipher.doFinal(src);
Cipher cipher2 = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher2.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] msg = cipher2.doFinal(cipherData);
Taken mostly from here and here.
The final line throws an exception of type javax.crypto.IllegalBlockSizeException with no message/further details. The three lines in logcat before the exception are
E keymaster1_device: Finish send cmd failed
E keymaster1_device: ret: 0
E keymaster1_device: resp->status: -1000
in case that matters at all.
Does anyone have an idea what could be going wrong?
Using minSdkVersion 23
Edit:
I just realised, if I use PKCS#1 v1.5 padding it works. That helps me for now, but I'd still like to try get it work with OAEP.
You need to put into the cipher the algorithm parameter spec when you encrypt
if (algorithmParameterSpec != null) {
encrypter.init(Cipher.ENCRYPT_MODE, getKey(), algorithmParameterSpec)
}
algorithmParameterSpec is
OAEPParameterSpec("SHA-256",
"MGF1",
MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT)

How to add a owner AES key to AndroidKeyStore?

I want to store my AES-256 key to AndroidKeyStore, this AES-256 key is raw key (a random 32 byte). I try some code like this.
public foo () {
SecureRandom sr = new SecureRandom();
byte[] key = new byte[32];
sr.nextBytes(key);
try {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
SecretKeySpec sks = new SecretKeySpec(key, "AES");
SecretKeyFactory skf = SecretKeyFactory.getInstance("AES");
SecretKey sk = skf.generateSecret(sks);
ks.setEntry("key", new KeyStore.SecretKeyEntry(sk), new
KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT).build());
KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry) ks.getEntry("key", null);
SecretKey skLoad = (SecretKey) ks.getKey("key", null);
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skLoad);
Log.i(TAG, Arrays.toString(cipher.doFinal(plainBytes)));
} catch (Exception e) {
e.printStackTrace();
}
}
I get the exception at line SecretKey sk = skf.generateSecret(sks);
java.security.spec.InvalidKeySpecException: To generate secret key in Android Keystore, use KeyGenerator initialized with android.security.keystore.KeyGenParameterSpec
I know we can save key with using KeyGenerator with KeyGenParameterSpec, but I have some reason to use owner key and KeyGenParameter seem can't import my owner key. So have any idea for this problem, thank all!
Generally you should not import keys from outside the key store, as they are insecure before they enter. So adding them later has limited benefits.
However, you can do a little trick: create a wrapping key in the key store and use it to wrap your symmetric key, and store the result. Then you can simply reverse the process when the key is needed again.
Unfortunately the best methods for storing keys such as (GCM-)SIV mode is generally not implemented, but hey, now you've at least heard about it.

App throwing "Unsupported MGF1 digest: SHA-256. Only SHA-1 supported" although I use recommended approach

In my android app I want to store secret keys in android key store. But My App throws java.security.InvalidAlgorithmParameterException: Unsupported MGF1 digest: SHA-256. Only SHA-1 supported when I use AndroidKeyStoreRSAPrivateKey in Cipher initialization:
KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA, "AndroidKeyStore");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(ALIAS)
.setKeyType(KeyProperties.KEY_ALGORITHM_RSA)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, F4))
.setSubject(new X500Principal("CN=" + ALIAS))
.setSerialNumber(BigInteger.valueOf(Math.abs(ALIAS.hashCode())))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
try {
kpg.initialize(spec);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
KeyPair kp = kpg.generateKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
//......................
// creating and initalizing Cipher
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
// in this place exception is thrown
cipher.init(Cipher.DECRYPT_MODE,
key,
new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT));
Exception is thrown in cipher.init(). But when my private key is instance of OpenSSLRSAPrivateKey generated by KeyFactory.getInstance(RSA) issue is not reproduced and all works fine, excepting that I need exactly AndroidKeyStoreRSAPrivateKey. I read developer doc https://developer.android.com/guide/topics/security/cryptography , but it doesn't contain any info regarding to my problem.
How should I change the code to solve the issue?

Android KeyStore: Unsupported secret key algorithm: AES/CBC/PKCS5Padding

I am trying to store an AES key in the Android KeyStore using following code:
SecretKey AESkey = new SecretKeySpec(
byteKey, 0, byteKey.length, "AES/CBC/PKCS5Padding");
if (ks == null)
{
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
}
ks.deleteEntry("aes_key");
ks.setEntry("aes_key",
new KeyStore.SecretKeyEntry(AESkey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
The line with 'setEntry(...)' fails throwig:
java.security.KeyStoreException: java.lang.IllegalArgumentException: Unsupported secret key algorithm: AES/CBC/PKCS5Padding
How can I store my key in the Android.KeyStore?
CBC and PKCS5Padding are not part of a key but key size is.
Somewhat guessing given the error message just use "AES".
SecretKey AESkey = new SecretKeySpec(byteKey, 0, byteKey.length, "AES");
The documentation is thin at best and the closest I can find is SecretKeyFactory Algorithms: "AES" Constructs secret keys for use with the AES algorithm. See: SecretKeyFactory Algorithms.

encoded text of secretkey from keystore is null in android M

I need to implement 256 bit AES encryption, I refer this- http://nelenkov.blogspot.jp/2015/06/keystore-redesign-in-android-m.html
The KeyGenerator looks like this .
KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES,"AndroidKeyStore");
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setUserAuthenticationValidityDurationSeconds(5 *11160)
.build();
keyGenerator.init(spec);
SecretKey key1 = keyGenerator.generateKey();
When I import encoded value of the key from keystore, it returns null to me.
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey key3 = (SecretKey) keyStore.getKey(keyName, null);
Log.d("Test MM",key3.getEncoded()+",");
I found null value of key3.getEncoded() in logcat.Please give me some suggestions.
Symmetric keys generated in the keystore are unexportable in Android M. So it works exactly as it is supposed to. You can still use the key with a Cipher to encrypt/decrypt, but you cannot get the raw key bytes. Generally you should need to either.

Categories

Resources