Issue with Robolectric with cipher in android application - android

Hi I am using Robolectric for test cases. I am facing some issue while simulating for encryption relateed test cases. I tried to use cipher with AES for encryption. And it is giving me some error. I tried it in following manner :
#Test
public void testGet() {
Cipher cipher = null;
try {
SecretKey sks= getKeySpec(pass, salt);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sks);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
public SecretKey getKeySpec(char[] pass, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
//generate key spec...
return secretKeyFactory.generateSecret(keySpec);
}
It gives me following error :
Illegal key size or default parameters
I already added JCE for illegal size exception. It is working if I run it on device and properly working in my application. Only thing when I try with robolectric it is giving me this error.Need some help. Thank you.

The reason behind this is Robolectric runs on JVM. JVM supports up to 128 bit key encryption only. Therefore if you are using 256 bit key encryption you need to use Java Cryptography Extension (JCE). follow this answer to how to do that

Related

decodeHex method not found on Device, but working on Emulator

I'm working on the project with RSA encryption, and need to use the method from Apache commons-codec, which is:
Hex.encodeHex(byte[])
Hex.decodeHex(String)
Both methods are working fine on Android emulator, but it will return NoSuchMethodError on Device
public String RSADecrypt(final String message) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
byte[] decryptedBytes = cipher.doFinal(Hex.decodeHex(message));
return new String(decryptedBytes);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (DecoderException e) {
e.printStackTrace();
}
return "";
}
java.lang.NoSuchMethodError: No static method decodeHex(Ljava/lang/String;)[B in class Lorg/apache/commons/codec/binary/Hex; or its super classes (declaration of 'org.apache.commons.codec.binary.Hex' appears in /system/framework/org.apache.http.legacy.boot.jar)
My emulator run on Pie, Oreo, & Nougat
My device run on Nougat & Marshmallow
Some Android versions contain an older version of the Apache commons-codec library (1.3) where the decodeHex(String) method didn't yet exist. Try calling decodeHex(char[]) instead. I.e. modify your code like this:
byte[] decryptedBytes = cipher.doFinal(Hex.decodeHex(message.toCharArray()));
That should work with commons-codec v1.3.

Shared Preferences Security

I am developing an android module, and I have to use a shared preferences to store a jwt token for auto login, and for some other thing.
I store it with a key, like "token" or something like that.
The problem is :
If the developer import my module for his application, and find out the key, he can easily read my jwt token, and It would not to good for me.
Could you provide me some alternative solution?
Edit : My minimum API level must be 14.
This problem is not as easy as it seems to be. For what I know the best solution is to store your key some way by using NDK; C code is harder to decompile and your protection level is higher than using simple Java.
Obfuscating Android Applications using O-LLVM and the NDK
Another solution could be to use a String obfuscator; but, generally speaking, security through obscurity is never a good idea.
Protect string constant against reverse-engineering
You can encrypt your token before save to shared preferences and when you need to use you can decrypt and use it.
I suggest you to use an unpredictible key when you're saving to shared preferences instead of "token"
Here's an Encryption class which can be used in Android apps to encrypt and decrypt data.
public final class Encryption {
private static final String CHIPHER_TRANSFORMATION = "AES/ECB/PKCS5Padding";
private static final String GENERATE_KEY__ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String GENERATE_KEY_ALGORITHM = "AES";
public static final int CRYPTO_TYPE_ENCRYPT = 0;
public static final int CRYPTO_TYPE_DECRYPT = 1;
public static String crypto(String inString, int type, String hashKey, String salt, String charset) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance(CHIPHER_TRANSFORMATION);
byte[] inputByte = inString.getBytes(charset);
switch (type) {
case CRYPTO_TYPE_DECRYPT:
cipher.init(Cipher.DECRYPT_MODE, initKey(hashKey, salt));
return new String(cipher.doFinal(Base64.decode(inputByte, Base64.DEFAULT)));
case CRYPTO_TYPE_ENCRYPT:
cipher.init(Cipher.ENCRYPT_MODE, initKey(hashKey, salt));
return new String(Base64.encode(cipher.doFinal(inputByte), Base64.DEFAULT));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
private static SecretKey getSecretKey(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(GENERATE_KEY__ALGORITHM);
KeySpec spec = new PBEKeySpec(password, salt, 1024, 128);
SecretKey tmp = factory.generateSecret(spec);
return (new SecretKeySpec(tmp.getEncoded(), GENERATE_KEY_ALGORITHM));
}
private static SecretKey initKey(String hashKey, String salt) {
try {
return getSecretKey(hashKey.toCharArray(), salt.getBytes());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
}
The Android Keystore system lets you store private keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the private key material remaining non-exportable.(Note: One problem, It was introduced in API level 18)
Android secure shared preferences using Android Keystore system
https://github.com/ophio/secure-preferences
Refer this article, for detailed information,
https://medium.com/#vashisthg/android-secure-shared-preferences-10f8356a4c2b#.8nf88g4g0
Another solution [API level 8]:
Obscured Shared Preferences for
Android
[ObscuredSharedPreferences.java]
https://github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/com/worxforus/android/ObscuredSharedPreferences.java
Hope this would help you!

Android fingerprint on SDK<23

I have a project on Android with minSDK=17 and targetSDK=23. We have a fingerprint authentication in this project made using FingerprintManager class (it was added in SDK23). We added SDK version check, so we are not using anything related to fingerprint if SDK<23. But in older SDK versions app behaviour is unpredictable: on some versions app just crashing, on other -- fingerprint not working (so, it's ok).
My question:
1) Is it any good and easy-to-implement libraries for minSDK=17, that can recognize fingerprints?
2) How can I avoid app crashing in devices with SDK<23?
Crash error:
E/dalvikvm: Could not find class 'android.hardware.fingerprint.FingerprintManager', referenced from method nl.intratuin.LoginActivity.loginByFingerprint
E/AndroidRuntime: FATAL EXCEPTION: main java.lang.VerifyError:
LoginActivity at java.lang.Class.newInstanceImpl(Native Method)
Some new info: created HelloWorld fingerprint project using this tutorial:
http://www.techotopia.com/index.php/An_Android_Fingerprint_Authentication_Tutorial
Found the root of the problem:
FingerprintDemoActivity->cipherInit:
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
First catch block breacking whole app with error I mentioned above. Of course, I can just remove this catch (this exception extends InvalidKeyException, so it will be handled), and return false in case of any exceptions. Is it any better way?
I think, I found acceptable solution: catch not KeyPermanentlyInvalidatedException, but InvalidKeyException. Everything working fine this way. Still have no idea how this exception crashed whole app...
It happened to me also..even when i used : if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M).. my app crashes on android 4.4- kitkat. so eventually the problem was in the initCipher method in the catches part - see the following code (even though i m not suppose to get there as it targeted to M and above... very strange behaviour..) :
#TargetApi(Build.VERSION_CODES.M)
private boolean initCipher() {
try {
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
mCipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
| NoSuchAlgorithmException e) {
throw new RuntimeException("Failed to init Cipher", e);
} catch (InvalidKeyException e) {
e.printStackTrace();
return false;
}
}
apparently the order off the catches matter..so just make sure to write it as i mentioned.
Reason for Crash:
The FingerprintManager class works with Android version 23 and Higher.
If your app is using FingerprintManager class and runs on older version of Android then you will encounter this exception.
Supporting older versions of Android:
Use FingerprintManagerCompat instead of FingerprintManager if you are planning to support Android <23. The FingerprintManagerCompat class internally checks for the Android version and handle Authentication part with ease.
How to Use it:
Replace android.hardware.fingerprint.FingerprintManager With android.support.v4.hardware.fingerprint.FingerprintManagerCompat
Replace android.os.CancellationSignal With android.support.v4.os.CancellationSignal
See Sample Code
https://github.com/hiteshsahu/FingerPrint-Authentication-With-React-Native-Android/blob/master/android/app/src/main/java/com/aproject/view/Fragments/FingerprintAuthenticationDialogFragment.java
Have a look at a library created by afollestad called digitus.
This library can fall back to a password if fingerprints are not available.
Any devices prior to SDK 23 need to use their own separate device manufacturer based sdk.
just follow android studio hint, it will be OK.
try {
mKeyStore.load(null);
SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (IOException | NoSuchAlgorithmException | CertificateException
| UnrecoverableKeyException | KeyStoreException | InvalidKeyException e) {
e.printStackTrace();
throw new RuntimeException("Failed to init Cipher", e);
}
To answer the second part of the question
How can I avoid app crashing in devices with SDK<23?
This simplistic logic check will suffice:
if (Build.VERSION.SDK_INT < 23) {
// Handle the mechanism where the SDK is older.
}else{
// Handle the mechanism where the SDK is 23 or later.
}
I solved this by moving all the fingerprint code to a helper class so that the classes related to the fingerprint code are not imported in the activity and by instantiating the helper class only when the SDK_INT is greater than 23 (In my case, as I'm supporting only Android 6+)
I also had this problem.Even when i used : if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M).. my app crashes on lower API's. I solved that as follow:
Replaced these codes:
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
with:
try {
keyStore.load(null);
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey key = null;
try {
key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return true;

android.security.KeyStoreException: Invalid key blob

I cannot obtain a (private) key from KeyStore on Android. Problem occurs mainly
on Samsung devices (S6, S6 Edge) and Android 6.
android.security.KeyStoreException: Invalid key blob
is thrown when following line is called (where alias is name for store key).
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
The KeyStore itself is obtained by
KeyStore.getInstance("AndroidKeyStore");
And key is generated by the following method:
private static void createKey(String alias, String subject, KeyStore keyStore, BigInteger serialNumber, Date startDate, Date endDate, String algorithm, String keyStoreProvider, Context context)
throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
if (keyStore.containsAlias(alias)) {
// Key already exists.
return;
}
// Generate keys.
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal(subject))
.setSerialNumber(serialNumber)
.setStartDate(startDate)
.setEndDate(endDate)
.build();
KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithm, keyStoreProvider);
generator.initialize(spec);
KeyPair keyPair = generator.generateKeyPair();
}
Where algorithm is "RSA" and keyStoreProvider is "AndroidKeyStore".
The part of the stacktrace:
android.security.KeyStoreException: Invalid key blob
at android.security.KeyStore.getKeyStoreException(KeyStore.java:939)
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePublicKeyFromKeystore(AndroidKeyStoreProvider.java:216)
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(AndroidKeyStoreProvider.java:252)
at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(AndroidKeyStoreProvider.java:263)
at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:93)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:372)
at java.security.KeyStore.getEntry(KeyStore.java:645)
The exception causes java.security.UnrecoverableKeyException: Failed to obtain information about private key to be thrown.
I was not able to find any closer information about "Invalid key blob",
only that the message itself is defined here: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/security/keymaster/KeymasterDefs.java
This problem is occurred when user tries to UNLOCK from LOCK/UNINITIALIZED. It is by default defined as 30 secs for timing. This problem is it's API related implementation issue.
This error is generated from InvalidKeyException. By bypassing this exception and call the method again, you can get rid of this error.
You have to remove the InvalidKeyException class from the catch argument. This will still allow you to check for InvalidKeyException. After checking you have to try for second time with code so that the problem is not shown in eye but doing 2 times checking it may solve your issue. Code is given below.
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore
.getEntry("alias", null);
} catch (InvalidKeyException ex) {
ex.printStackTrace();
if (ex instanceof InvalidKeyException) { // bypass
// InvalidKeyException
// You can again call the method and make a counter for deadlock
// situation or implement your own code according to your
// situation
if (retry) {
keyStore.deleteEntry(keyName);
return getCypher(keyName, false);
} else {
throw ex;
}
}
} catch (final Exception e) {
e.printStackTrace();
throw e;
}
You can see my another answer that describes one by one occurring
issue and solution.
UPDATE from #Ankis:
As you solved the issue by changing InvalidKeyException to UnrecoverableKeyException. So I have updated as per your suggestion so that world can know the actual answer. Thanks for sharing :).
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) this.keyStore
.getEntry("alias", null);
} catch (UnrecoverableKeyException ex) {
ex.printStackTrace();
// You can again call the method and make a counter for deadlock
// situation or implement your own code according to your
// situation
if (retry) {
keyStore.deleteEntry(keyName);
return getCypher(keyName, false);
}
} catch (final Exception e) {
e.printStackTrace();
throw e;
}

Android - Encode & Decode RSA with Private Key?

I am trying to encode and decode Strings on Android using a Private Key generated and stored using the Android Key Store Provider that was introduced in Android 4.3
I can successfully generate and get the private key using the following code:
private void generatePrivateKey(Activity context, String alias){
/** Generate a new entry in the KeyStore by using the * KeyPairGenerator API. We have to specify the attributes for a * self-signed X.509 certificate here so the KeyStore can attach * the public key part to it. It can be replaced later with a * certificate signed by a Certificate Authority (CA) if needed. */
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date end = cal.getTime();
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
}
try {
kpg.initialize(new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setStartDate(now)
.setEndDate(end)
.setSerialNumber(BigInteger.valueOf(1))
.setSubject(new X500Principal("CN=" + alias))
.build());
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
KeyPair kp = kpg.generateKeyPair();
/*
* Load the Android KeyStore instance using the the
* "AndroidKeyStore" provider to list out what entries are
* currently stored.
*/
KeyStore ks = null;
try {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
/*
* Use a PrivateKey in the KeyStore to create a signature over
* some data.
*/
KeyStore.Entry entry = null;
try {
entry = ks.getEntry(alias, null);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
Log.w("E", "Not an instance of a PrivateKeyEntry");
}
else{
Log.w("E", "Got Key!");
privateKeyEntry = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
}
}
And here is the code I am using for encrypt (encode) and decrypt (decode):
private String encryptString(String value){
byte[] encodedBytes = null;
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
cipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry );
encodedBytes = cipher.doFinal(value.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}
private String decryptString(String value){
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
c.init(Cipher.DECRYPT_MODE, privateKeyEntry );
decodedBytes = c.doFinal(Base64.decode(value, Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return new String(decodedBytes);
}
The Encryption appears to work fine but when I try to decrypt it I get the following error:
javax.crypto.BadPaddingException: error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
Googling this seems to suggest that the private key used for decryption is different to the one used for decryption but in my code I use the exact same private key for both. I have also seen it suggested to set the key size manually but doing this in the KeyPairGenerator builder like this:
.setKeySize(1024);
Did not work and seems to be only available on API 19, I need to target API 18.
Can anyone help point me in the right direction as to a solution?
You are not using the public key for encryption.
When you are using asymmetric encryption algorithms, you need to use the public key to encrypt your data, and the private key only to decrypt it again.
Besides encryption, you can also use the private key for signing, but that's not what you want here, so let's forget about that for the moment.
If you take the public key from the generated pair, when you encrypt your string, and the private key when decrypting, you should get the desired result. The public key you can extract by accessing the certificate from the keystore-object that holds your private key.
Alternatively you could also use a symmetric algorithm like AES and by that make your work a lot easier. Plus, symmetric algorithms are usually much faster, which is why asymmetric algorithms are never used purely, but in conjunction with symmetric algorithms, building so-called hybrid algorithms.
Signature generation is not the same thing as encryption. You need to encrypt with the public key and decrypt with the private key if you want encryption. If you want signature generation, you need to sign with the private key and verify with the public key. This order cannot be reversed nor can it be mixed (securely).

Categories

Resources