Reputation: 2129
The common location where SharedPreferences
are stored in Android apps is:
/data/data/<package name>/shared_prefs/<filename.xml>
User with root privileges can navigate to this location and can change its values.Need of protecting it is of much importance.
In how many ways we can encrypt whole shared_pref's xml
file?
We all know that we can encrypt and save data in shared_pref's xml
file, but that's not only 100% safe, so need to encrypt whole file with a key. Need help in knowing various ways to encrypt whole xml
file. This is generic question, various encryption methods discussed as answers here can be helpful to all developers in securing apps.
Upvotes: 73
Views: 102255
Reputation: 15668
UPDATED ANSWER:
Android has released a security library with EncryptedSharedPreferences in their Jetpack library.
Edit: With version v1.1.0 you can support Lollipop (API level 21) and above
fun getEncryptedSharedPrefs(context: Context): SharedPreferences {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
return EncryptedSharedPreferences.create(
context,
prefsName,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
fun getEncryptedSharedPrefsEditor(context: Context): SharedPreferences.Editor {
return getEncryptedSharedPrefs(context).edit()
}
Upvotes: 97
Reputation: 7
Create a singleton class called SharePrefrencesManager as follows:
import android.content.Context;
import android.content.SharedPreferences;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
import java.util.Set;
public class SharedPreferencesManager {
private static final String PREF_NAME = "ds_maps_secret_shared_prefs";
private static SharedPreferencesManager instance;
private SharedPreferences sharedPreferences;
// Private constructor to prevent instantiation outside the class
private SharedPreferencesManager(Context context) {
try {
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
sharedPreferences = EncryptedSharedPreferences.create(
masterKeyAlias,
PREF_NAME,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
} catch (Exception e) {
e.printStackTrace();
// Handle initialization error
}
}
// Method to get the singleton instance of SharedPreferencesManager
public static synchronized SharedPreferencesManager getInstance(Context context) {
if (instance == null) {
instance = new SharedPreferencesManager(context);
}
return instance;
}
// Methods to read and edit different values in SharedPreferences
public String getString(String key, String defaultValue) {
return sharedPreferences.getString(key, defaultValue);
}
public void putString(String key, String value) {
sharedPreferences.edit().putString(key, value).apply();
}
public int getInt(String key, int defaultValue) {
return sharedPreferences.getInt(key, defaultValue);
}
public void putInt(String key, int value) {
sharedPreferences.edit().putInt(key, value).apply();
}
public long getLong(String key, long defaultValue) {
return sharedPreferences.getLong(key, defaultValue);
}
public void putLong(String key, long value) {
sharedPreferences.edit().putLong(key, value).apply();
}
public float getFloat(String key, float defaultValue) {
return sharedPreferences.getFloat(key, defaultValue);
}
public void putFloat(String key, float value) {
sharedPreferences.edit().putFloat(key, value).apply();
}
public boolean getBoolean(String key, boolean defaultValue) {
return sharedPreferences.getBoolean(key, defaultValue);
}
public void putBoolean(String key, boolean value) {
sharedPreferences.edit().putBoolean(key, value).apply();
}
public Set<String> getStringSet(String key, Set<String> defaultValue) {
return sharedPreferences.getStringSet(key, defaultValue);
}
public void putStringSet(String key, Set<String> value) {
sharedPreferences.edit().putStringSet(key, value).apply();
}
// Clear all preferences
public void clearPreferences() {
sharedPreferences.edit().clear().apply();
}
}
edit the data_extraction_rules file
<data-extraction-rules>
<cloud-backup disableIfNoEncryptionCapabilities="true">
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
</cloud-backup>
</data-extraction-rules>
edit the backup_rules
<full-backup-content>
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
</full-backup-content>
in Manifest add the following
<application
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
...
</application>
This will encrypt all the files for you while saving and decrypt while reading.
Upvotes: 0
Reputation:
If you are worried about root, there is essentially nothing you can do to prevent it from reading internal app data, encrypted or otherwise.
Specifically, encrypting shared preferences using EncryptedSharedPreferences (or by a similar means) won't do anything because it stores the encryption key on the device. Root can then read the encryption key and de-encrypt the data. (Root can access the KeyStore of any app).
Even if you do manage to safely encrypt the data with a key that root does not have access to, when you decrypt it, the data will be in the process's memory where root can then read it.
If you're worried about an attacker who has root, its already game over.
Upvotes: 0
Reputation: 4116
If you want to support Android 5.0 (API level 21) and above
Use following implementation:
implementation "androidx.security:security-crypto:1.0.0-rc04"
or get latest one from this source.
Then
First create a master key as follows:
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
After create shared preferences
as follows:
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
Then use it as you normally would for example:
with(sharedPreferences.edit()) {
putString(Values.SP_USER_ID, personId)
putString(Values.SP_USER_NAME, binding.editTextTextPersonName.text.toString())
apply()
}
Upvotes: 16
Reputation: 2809
You need to handle Verisons under API 23
fun providesSharedPreference(): SharedPreferences {
var sharedPreferences: SharedPreferences
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
sharedPreferences = EncryptedSharedPreferences.create(
application,
Constant.SHARED_PREFERENCE_NAME,
getMasterKey(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
} else {
sharedPreferences =
application.getSharedPreferences(
Constant.SHARED_PREFERENCE_NAME,
Context.MODE_PRIVATE
)
}
return sharedPreferences
}
private fun getMasterKey(): MasterKey {
return MasterKey.Builder(application)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
}
Upvotes: 13
Reputation: 1398
For below API 23 check out the Datum library. It ciphers data asynchronously:
StringDatum stringDatum = provider.new StringDatum("string_key", "default");
stringDatum.setValue("new value");
stringDatum.getValue(value->{
//got value
});
Upvotes: 0
Reputation: 330
Kotlin example for dual purpose encrypted & unencrypted shared preferences using anrdoidx's security-crypto library (min API 23).
I use Dagger2 to inject this as a @Singleton where needed.
Use the @Name annotation in your Dagger Modules to differentiate between the SharedPreferences instances and you can have 2 separate .xml files (1 encrypted, 1 unencrypted) to read/write to/from.
Add to dependenies in build.gradle:
implementation "androidx.security:security-crypto:1.0.0-beta01"
import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
class Prefs(prefsName: String, context: Context) {
private lateinit var ANDX_SECURITY_KEY_KEYSET: String
private lateinit var ANDX_SECURITY_VALUE_KEYSET: String
private lateinit var cntext: Context
private lateinit var prefName: String
private lateinit var prefs: SharedPreferences
constructor(
prefsName: String,
context: Context,
masterKeyAlias: String,
prefKeyEncryptionScheme: EncryptedSharedPreferences.PrefKeyEncryptionScheme,
prefValueEncryptionScheme: EncryptedSharedPreferences.PrefValueEncryptionScheme
): this(prefsName, context) {
ANDX_SECURITY_KEY_KEYSET = "__androidx_security_crypto_encrypted_prefs_key_keyset__"
ANDX_SECURITY_VALUE_KEYSET = "__androidx_security_crypto_encrypted_prefs_value_keyset__"
cntext = context
prefName = prefsName
prefs =
EncryptedSharedPreferences.create(
prefsName,
masterKeyAlias,
context,
prefKeyEncryptionScheme,
prefValueEncryptionScheme
)
}
init {
if (!::ANDX_SECURITY_KEY_KEYSET.isInitialized) {
prefs =
context.getSharedPreferences(
prefsName,
Context.MODE_PRIVATE
)
}
}
companion object {
const val INVALID_BOOLEAN: Boolean = false
const val INVALID_FLOAT: Float = -11111111111F
const val INVALID_INT: Int = -1111111111
const val INVALID_LONG: Long = -11111111111L
const val INVALID_STRING: String = "INVALID_STRING"
val INVALID_STRING_SET: Set<String> = setOf(INVALID_STRING)
}
/**
* OnChangeListener
* */
fun registerOnSharedPreferenceChangeListener(
listener: SharedPreferences.OnSharedPreferenceChangeListener) =
prefs.registerOnSharedPreferenceChangeListener(listener)
fun unregisterOnSharedPreferenceChangeListener(
listener: SharedPreferences.OnSharedPreferenceChangeListener) =
prefs.unregisterOnSharedPreferenceChangeListener(listener)
/**
* Read Shared Prefs
* */
fun contains(key: String): Boolean =
prefs.contains(key)
fun getAll(): Map<String, *> =
prefs.all
// Returns null if the Boolean value is not in
// Shared Preferences
fun read(key: String): Boolean? =
if (contains(key)) {
read(key, INVALID_BOOLEAN)
} else {
null
}
// Boolean
fun read(key: String, returnIfInvalid: Boolean): Boolean =
prefs.getBoolean(key, returnIfInvalid)
// Float
fun read(key: String, returnIfInvalid: Float): Float =
prefs.getFloat(key, returnIfInvalid)
// Int
fun read(key: String, returnIfInvalid: Int): Int =
prefs.getInt(key, returnIfInvalid)
// Long
fun read(key: String, returnIfInvalid: Long): Long =
prefs.getLong(key, returnIfInvalid)
// Set<String>
fun read(key: String, returnIfInvalid: Set<String>): Set<String>? =
prefs.getStringSet(key, returnIfInvalid)
// String
fun read(key: String, returnIfInvalid: String): String? =
prefs.getString(key, returnIfInvalid)
/**
* Modify Shared Prefs
* */
fun clear() {
if (::ANDX_SECURITY_KEY_KEYSET.isInitialized) {
val clearTextPrefs = cntext.getSharedPreferences(prefName, Context.MODE_PRIVATE)
val keyKeyset = clearTextPrefs.getString(ANDX_SECURITY_KEY_KEYSET, INVALID_STRING)
val valueKeyset = clearTextPrefs.getString(ANDX_SECURITY_VALUE_KEYSET, INVALID_STRING)
if (keyKeyset != null && keyKeyset != INVALID_STRING
&& valueKeyset != null && valueKeyset != INVALID_STRING) {
if (!clearTextPrefs.edit().clear().commit()) {
clearTextPrefs.edit().clear().apply()
}
if (!clearTextPrefs.edit().putString(ANDX_SECURITY_KEY_KEYSET, keyKeyset).commit()) {
clearTextPrefs.edit().putString(ANDX_SECURITY_KEY_KEYSET, keyKeyset).apply()
}
if (!clearTextPrefs.edit().putString(ANDX_SECURITY_VALUE_KEYSET, valueKeyset).commit()) {
clearTextPrefs.edit().putString(ANDX_SECURITY_VALUE_KEYSET, valueKeyset).apply()
}
}
} else {
if (!prefs.edit().clear().commit()) {
prefs.edit().clear().apply()
}
}
}
fun remove(key: String) {
if (!prefs.edit().remove(key).commit()) {
prefs.edit().remove(key).apply()
}
}
// Boolean
fun write(key: String, value: Boolean) {
if (!prefs.edit().putBoolean(key, value).commit()) {
prefs.edit().putBoolean(key, value).apply()
}
}
// Float
fun write(key: String, value: Float) {
if (!prefs.edit().putFloat(key, value).commit()) {
prefs.edit().putFloat(key, value).apply()
}
}
// Int
fun write(key: String, value: Int) {
if (!prefs.edit().putInt(key, value).commit()) {
prefs.edit().putInt(key, value).apply()
}
}
// Long
fun write(key: String, value: Long) {
if (!prefs.edit().putLong(key, value).commit()) {
prefs.edit().putLong(key, value).apply()
}
}
// Set<String>
fun write(key: String, value: Set<String>) {
if (!prefs.edit().putStringSet(key, value).commit()) {
prefs.edit().putStringSet(key, value).apply()
}
}
// String
fun write(key: String, value: String) {
if (!prefs.edit().putString(key, value).commit()) {
prefs.edit().putString(key, value).apply()
}
}
}
An alternative option to using Dagger2 to inject as a @Singleton could be to:
AppPrefs.kt
object AppPrefs {
lateinit var encryptedPrefs: Prefs
lateinit var prefs: Prefs
// Add your key strings here...
fun initEncryptedPrefs(context: Context) {
encryptedPrefs =
Prefs(
"ENCRYPTED_PREFS",
context,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
fun initPrefs(context: Context) {
prefs = Prefs("PREFS", context)
}
}
Application.kt
class Application: Application() {
override fun onCreate() {
super.onCreate()
AppPrefs.initEncryptedPrefs(this.applicationContext)
AppPrefs.initPrefs(this.applicationContext)
}
}
Then just call from where ever AppPrefs.prefs
or AppPrefs.encryptedPrefs
Upvotes: 2
Reputation: 3530
Complete answer (api level 23+). First you need crypto from androidx.
implementation "androidx.security:security-crypto:1.0.0-alpha02"
Care : there is a significant performance difference between SharedPreferences and EncryptedSharedPreferences. You should notice that EncryptedSharedPreferences.create(...) is not so fast so you should have to store one instance at all.
Then you have to use this to retrieve EncryptedSharedPreferences.
public SharedPreferences getEncryptedSharedPreferences(){
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secret_shared_prefs_file",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
return sharedPreferences;
}
The you just have to use preference like the "standard way". To save it :
getEncryptedSharedPreferences().edit()
.putString("ENCRYPTDATA", text)
.apply()
To retrieve preference value.
getEncryptedSharedPreferences().getString("ENCRYPTDATA", "defvalue")
Upvotes: 8
Reputation: 241
Google has released EncryptedSharedPreferences
as part of it's androidx, I believe this should be the preferable way of encrypting the preferences.
See https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
Upvotes: 22
Reputation: 1637
Base64 is NOT encryption! Don't use it! Yes 'root' users can access that data. One thing you can do is use AES to encrypt either that data or use a single NoSQL database file and encrypt that file. When the app opens, you decrypt the database and use that to store info or encrypt all files independently.
Look here: https://code.tutsplus.com/tutorials/storing-data-securely-on-android--cms-30558
Upvotes: 3
Reputation: 447
You should encrypt your data and write to SharedPreferences. When you want get this data then you should decrypt from SharedPreferences. you need the following helper class for this
public class Encryption {
private final Builder mBuilder;
private Encryption(Builder builder) {
mBuilder = builder;
}
public static Encryption getDefault(String key, String salt, byte[] iv) {
try {
return Builder.getDefaultBuilder(key, salt, iv).build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
private String encrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
byte[] dataBytes = data.getBytes(mBuilder.getCharsetName());
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
return Base64.encodeToString(cipher.doFinal(dataBytes), mBuilder.getBase64Mode());
}
public String encryptOrNull(String data) {
try {
return encrypt(data);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public void encryptAsync(final String data, final Callback callback) {
if (callback == null) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
String encrypt = encrypt(data);
if (encrypt == null) {
callback.onError(new Exception("Encrypt return null, it normally occurs when you send a null data"));
}
callback.onSuccess(encrypt);
} catch (Exception e) {
callback.onError(e);
}
}
}).start();
}
private String decrypt(String data) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
if (data == null) return null;
byte[] dataBytes = Base64.decode(data, mBuilder.getBase64Mode());
SecretKey secretKey = getSecretKey(hashTheKey(mBuilder.getKey()));
Cipher cipher = Cipher.getInstance(mBuilder.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey, mBuilder.getIvParameterSpec(), mBuilder.getSecureRandom());
byte[] dataBytesDecrypted = (cipher.doFinal(dataBytes));
return new String(dataBytesDecrypted);
}
public String decryptOrNull(String data) {
try {
return decrypt(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void decryptAsync(final String data, final Callback callback) {
if (callback == null) return;
new Thread(new Runnable() {
@Override
public void run() {
try {
String decrypt = decrypt(data);
if (decrypt == null) {
callback.onError(new Exception("Decrypt return null, it normally occurs when you send a null data"));
}
callback.onSuccess(decrypt);
} catch (Exception e) {
callback.onError(e);
}
}
}).start();
}
private SecretKey getSecretKey(char[] key) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance(mBuilder.getSecretKeyType());
KeySpec spec = new PBEKeySpec(key, mBuilder.getSalt().getBytes(mBuilder.getCharsetName()), mBuilder.getIterationCount(), mBuilder.getKeyLength());
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), mBuilder.getKeyAlgorithm());
}
private char[] hashTheKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(mBuilder.getDigestAlgorithm());
messageDigest.update(key.getBytes(mBuilder.getCharsetName()));
return Base64.encodeToString(messageDigest.digest(), Base64.NO_PADDING).toCharArray();
}
public interface Callback {
void onSuccess(String result);
void onError(Exception exception);
}
private static class Builder {
private byte[] mIv;
private int mKeyLength;
private int mBase64Mode;
private int mIterationCount;
private String mSalt;
private String mKey;
private String mAlgorithm;
private String mKeyAlgorithm;
private String mCharsetName;
private String mSecretKeyType;
private String mDigestAlgorithm;
private String mSecureRandomAlgorithm;
private SecureRandom mSecureRandom;
private IvParameterSpec mIvParameterSpec;
public static Builder getDefaultBuilder(String key, String salt, byte[] iv) {
return new Builder()
.setIv(iv)
.setKey(key)
.setSalt(salt)
.setKeyLength(128)
.setKeyAlgorithm("AES")
.setCharsetName("UTF8")
.setIterationCount(1)
.setDigestAlgorithm("SHA1")
.setBase64Mode(Base64.DEFAULT)
.setAlgorithm("AES/CBC/PKCS5Padding")
.setSecureRandomAlgorithm("SHA1PRNG")
.setSecretKeyType("PBKDF2WithHmacSHA1");
}
private Encryption build() throws NoSuchAlgorithmException {
setSecureRandom(SecureRandom.getInstance(getSecureRandomAlgorithm()));
setIvParameterSpec(new IvParameterSpec(getIv()));
return new Encryption(this);
}
private String getCharsetName() {
return mCharsetName;
}
private Builder setCharsetName(String charsetName) {
mCharsetName = charsetName;
return this;
}
private String getAlgorithm() {
return mAlgorithm;
}
private Builder setAlgorithm(String algorithm) {
mAlgorithm = algorithm;
return this;
}
private String getKeyAlgorithm() {
return mKeyAlgorithm;
}
private Builder setKeyAlgorithm(String keyAlgorithm) {
mKeyAlgorithm = keyAlgorithm;
return this;
}
private int getBase64Mode() {
return mBase64Mode;
}
private Builder setBase64Mode(int base64Mode) {
mBase64Mode = base64Mode;
return this;
}
private String getSecretKeyType() {
return mSecretKeyType;
}
private Builder setSecretKeyType(String secretKeyType) {
mSecretKeyType = secretKeyType;
return this;
}
private String getSalt() {
return mSalt;
}
private Builder setSalt(String salt) {
mSalt = salt;
return this;
}
private String getKey() {
return mKey;
}
private Builder setKey(String key) {
mKey = key;
return this;
}
private int getKeyLength() {
return mKeyLength;
}
public Builder setKeyLength(int keyLength) {
mKeyLength = keyLength;
return this;
}
private int getIterationCount() {
return mIterationCount;
}
public Builder setIterationCount(int iterationCount) {
mIterationCount = iterationCount;
return this;
}
private String getSecureRandomAlgorithm() {
return mSecureRandomAlgorithm;
}
public Builder setSecureRandomAlgorithm(String secureRandomAlgorithm) {
mSecureRandomAlgorithm = secureRandomAlgorithm;
return this;
}
private byte[] getIv() {
return mIv;
}
public Builder setIv(byte[] iv) {
mIv = iv;
return this;
}
private SecureRandom getSecureRandom() {
return mSecureRandom;
}
public Builder setSecureRandom(SecureRandom secureRandom) {
mSecureRandom = secureRandom;
return this;
}
private IvParameterSpec getIvParameterSpec() {
return mIvParameterSpec;
}
public Builder setIvParameterSpec(IvParameterSpec ivParameterSpec) {
mIvParameterSpec = ivParameterSpec;
return this;
}
private String getDigestAlgorithm() {
return mDigestAlgorithm;
}
public Builder setDigestAlgorithm(String digestAlgorithm) {
mDigestAlgorithm = digestAlgorithm;
return this;
}
}}
then you can write in SharedPreferences by encrypting your data as follows
Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = preferences.edit();
editor.putString("token", encryption.encryptOrNull(userModel.getToken()));
editor.apply()
you can finally read from SharedPreferences data in the following way. This way, sensitive information will be safer while kept on the hardware level in the phone
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Encryption encryption = Encryption.getDefault("Key", "Salt", new byte[16]);
String token = encryption.decryptOrNull(preferences.getString("token",""));
Upvotes: 8
Reputation: 1020
public class NodeCrypto {
private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)
public void doKey(String key)
{
ivspec = new IvParameterSpec(iv.getBytes());
key = padRight(key,16);
Log.d("hi",key);
keyspec = new SecretKeySpec(key.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text,String key) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
doKey(key);
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code,String key) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
doKey(key);
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
else
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
public static String padRight(String s, int n) {
return String.format("%1$-" + n + "s", s);
}
}
-----------------------------------------------
from your activity or class call encrypt or decrypt method before saving or retriving from SharedPreference
Upvotes: 1