Shared Preferences Security - android

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!

Related

PlayStore reported Unsafe Cryptographic Encryption Error but i have to use static key, iv and salt

PlayStore reported Unsafe Cryptographic Encryption Error because i'm using static key, iv and salt. but i have to use the static key, iv and salt because the encryption result should be the same in other platform.
I am looking for the solution in Google but it seems there is no any solution for this case. Please help me.
I solved it with XML file.
I stored encryption key, salt and iv after encoding them with Base64.
res/raw/config.properties
secret_key=VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4=
salt=PYSiYA==
iv=AAAAAAAAAAAAAAAAAAAAAA==
java code
private String getConfigValue(Context context, String name) {
Resources resources = context.getResources();
try {
InputStream rawResource = resources.openRawResource(R.raw.config);
Properties properties = new Properties();
properties.load(rawResource);
return properties.getProperty(name);
} catch (Resources.NotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Base64.decode(getConfigValue(context, "secret_key"), Base64.DEFAULT));
Base64.decode(getConfigValue(context, "salt"), Base64.DEFAULT));
Base64.decode(getConfigValue(context, "iv"), Base64.DEFAULT));

Android: Store SecretKey in KeyStore

I use a SecretKey to encrypt sensitive data in my application. Currently I am storing my SecretKey in Base64 encoded format in DB or SharedPrefs which is not a safe place to store Secret on a rooted phone. Hence, I want to move my SecretKey to Android KeyStore. The problem I am facing is when I try this sample code from Google, it expects a PrivateKey instead of SecretKey. I couldn't figure out a way to store my SecretKey in KeyStore and fetch it for later use. I tried this:
private static void writeSecretKeyToKeystore(SecretKey secretKey, Context context) {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry(secretKey);
keyStore.setKeyEntry("Key", secretKeyEntry.getSecretKey().getEncoded(), null);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
When I try above code, it throws an exception Operation not supported because encoding is unknown.
Any sample code would be of great help.
WRONG
java.security.KeyStore can store both symmetric and asymmetric keys. You just need to instantiate KeyStore.SecretKeyEntry passing it your SecretKey in the constructor and then use the KeyStore#setEntry method to save it:
keyStore.setEntry(
"key1",
new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockMode(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
To get it back out use:
SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null);
UPDATE
After some research I was surprised to find out, that AndroidKeyStore doesn't support symmetric keys. (see the discussion: https://groups.google.com/forum/#!topic/android-developers/gbmIRKRbfq8)
The work-around would be to encrypt your SecretKey and store it in SharedPreferences. Then store the key to decrypt your key in the Keystore. Here's an implementation using scytale.
public static String getBase64EncodedSecretKey(){
Store store = new Store(context);
Crypto crypto = new Crypto(Options.TRANSFORMATION_SYMMETRIC);
SecretKey key = store.getSymmetricKey("key_alias", null);
String encryptedData = PreferenceManager.getDefaultSharedPreferences(context).getString("myEncryptedSecretKey", "");
return crypto.decrypt(encryptedData, key);
}
public static void storeKey(String base64EncodedSecretKey){
Store store = new Store(context);
if (store.hasKey("key_alias")) {
store.deleteKey("key_alias");
}
SecretKey key = store.generateSymmetricKey("key_alias", null);
Crypto crypto = new Crypto(Options.TRANSFORMATION_SYMMETRIC);
String encryptedData = crypto.encrypt(base64EncodedSecretKey, key);
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("myEncryptedSecretKey",encryptedData).apply();
}
// Usage:
//store SecretKey
byte[] encodedKey = secretKeyEntry.getSecretKey().getEncoded();
String base64EncodedKey = Base64.encodeToString(encodedKey);
storeKey(base64EncodedKey);
//get SecretKey
String base64EncodedKey = getBase64EncodedSecretKey();
byte[] encodedKey = Base64.decode(base64EncodedKey);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");

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).

How to Provide security to sms in android by using RSA algorithm

I want to send encrypted Sms data by using one key(public) for encryption and this sms wnat to decrypt by using different private key...specifically i want to use RSA algorithm scenario...
So can u tell me how to encrypt and decrypt data by using different keys in android...
I use DES algorithm but its using same key ....
can anybody suggest how to do this.... me blank value.....
`
public String encrypt(String smsbody) {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);// initialize key pairs to 512 bits ,you can
// also take 1024 or 2048 bits
KeyPair kp = kpg.genKeyPair();
PublicKey publi = kp.getPublic();
System.out.println(publi.serialVersionUID);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publi);
byte[] src = smsbody.getBytes();// converting source data into byte
// array
byte[] cipherData = cipher.doFinal(src);// use this method to
// finally encrypt data
String srco = new String(cipherData);// converting byte array into
// string
// System.out.println();
// System.out.println("Encrypted data is:-" + srco);
return srco;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "";
}
public PrivateKey Privatekey() {
try {
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);// initialize key pairs to 512 bits ,you can
// also take 1024 or 2048 bits
KeyPair kp = kpg.genKeyPair();
privatei = kp.getPrivate();// Generating private key
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return privatei;
}
public String decrypt(String smsbody, PrivateKey privatei) {
try {
Cipher cipheri = Cipher.getInstance("RSA");
cipheri.init(Cipher.DECRYPT_MODE, privatei);// Setting to
// decrypt_mode
System.out.println(smsbody);
byte[] cipherDat = cipheri.doFinal(smsbody.getBytes());// Finally
// decrypting
// data
System.out.println(cipherDat);
String decryptdata = new String(cipherDat);
return decryptdata;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "";
} `
I am trying this following code but when i use decrypt it will give
Thanks in advance...

encryption in android

How is encryption using Triple DES done in android? Are there any predefined classes available?
This may or may not work for you.
Have fun, JAL
public BoolString tryEncrypt(String inString, String key){
boolean success= true;
String err="";
String outString="Encrypted"; // BoolString.value
try {
byte[] byteKey= key.getBytes("UTF8");
if (byteKey.length != 24) {
success= false;
err= "Key is "+byteKey.length+" bytes. Key must be exactly 24 bytes in length.";
throw new Exception(err); // could also return here
}
KeySpec ks= new DESedeKeySpec(byteKey);
SecretKeyFactory skf= SecretKeyFactory.getInstance("DESede");
SecretKey sk= skf.generateSecret(ks);
Cipher cph=Cipher.getInstance("DESede");
cph.init(Cipher.ENCRYPT_MODE, sk);
byte[] byteInString= inString.getBytes("UTF8");
byte[] byteEncoded= cph.doFinal(byteInString);
outString= Base64.encodeToString(byteEncoded, Base64.DEFAULT);
}
catch (UnsupportedEncodingException e){err="Unable to convert key to byte array."; success= false;}
catch (InvalidKeyException e){err="Unable to generate KeySpec from key";success= false;}
catch (NoSuchAlgorithmException e){err="Unable to find algorithm.";success= false;}
catch (InvalidKeySpecException e){err="Invalid Key Specification";success= false;}
catch (NoSuchPaddingException e){err="No such padding";success= false;}
catch (IllegalArgumentException e){err="Illegal argument";success= false;}
catch (Exception e){err=e.getMessage();success= false;}
return new BoolString(success,err,outString);
}
// a utility class to signal success or failure, return an error message, and return a useful String value
// see Try Out in C#
public final class BoolString {
public final boolean success;
public final String err;
public final String value;
public BoolString(boolean success, String err, String value){
this.success= success;
this.err= err;
this.value= value;
}
}
You need to specify "DESede" as the cipher; see DESedeKeySpec and this example.
There are much more encryption algorithms that can be used to encrypt any kind of data.
I prefer to use AES(Advance Encryption Standard) for many reasons. First of all it provides larger key sizes, there are no weak and semi-weak keys which must be identified before the creation of an encryption app. Moreover, AES is not vulnerable to some other theoretical attacks such as differential cryptanalysis etc...

Categories

Resources