I am trying to use the Keystore to encrypt a string, and save this string to the shared preferences, then later on on a sub-subsequent app launch, decrypt the string. Yet, I believe I am missing the main point the keystore.
Mostly, I am based on this link below:
I wrote this wrapper using another thread I posted from a different issue
java.lang.IllegalArgumentException: bad base-64 when decrypting string
Yet, all the sample code I find, encrypt and decrypt during the same App run. This is never useful. I need to encrypt my string, save somewhere and decrypt it at later time. So this wrapper tries to initialize the KeyStore as such:
@TargetApi(Build.VERSION_CODES.M)
public KeyStoreHelper(boolean encrypt) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
if (encrypt) {
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
MY_KEY_NAME_INSIDE_KEYSTORE,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build());
} else {
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(
MY_KEY_NAME_INSIDE_KEYSTORE,
KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.build());
}
KeyPair keyPair = keyPairGenerator.generateKeyPair();
String provider = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? "AndroidOpenSSL" : "AndroidKeyStoreBCWorkaround";
if (encrypt) {
PublicKey publicKey = keyPair.getPublic();
mInCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
mInCipher.init(Cipher.ENCRYPT_MODE, publicKey);
} else {
PrivateKey privateKey = keyPair.getPrivate();
mOutCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
mOutCipher.init(Cipher.DECRYPT_MODE, privateKey);
}
} catch (Exception e) {
Log.e(ERROR_TAG, Log.getStackTraceString(e));
}
}
public static KeyStoreHelper getInstance(boolean encrypt) {
if (mKeyStoreHelperInstance == null) {
mKeyStoreHelperInstance = new KeyStoreHelper(encrypt);
}
return mKeyStoreHelperInstance;
}
Then I tried to encrypt some strings save to the preferences, as below
private SharedPreferences mSharedPreferences;
private void testKeystoreHelper(boolean encrypt) {
KeyStoreHelper keyStoreHelper;
initSharedPreferences();
final String sharedPreferencesAlias = "mysecret";
String plainText;
String secretString;
if (encrypt) {
plainText = "my secret string";
keyStoreHelper = KeyStoreHelper.getInstance(true);
secretString = keyStoreHelper.encrypt(plainText);
Log.v(TAG, "Encrypted = " + secretString);
mSharedPreferences.edit().putString(sharedPreferencesAlias, secretString).apply();
} else {
keyStoreHelper = KeyStoreHelper.getInstance(false);
secretString = mSharedPreferences.getString(sharedPreferencesAlias, null);
plainText = keyStoreHelper.decrypt(secretString);
Log.v(TAG, "Decrypted" + plainText);
}
}
finally I do one run with this:
testKeystoreHelper(true);
I quit the app and run again with this:
testKeystoreHelper(false);
But this never works giving me:
E/Error: java.io.IOException: Error while finalizing cipher at
javax.crypto.CipherInputStream.fillBuffer(CipherInputStream.java:104)
Because everytime the app launches although the keyname is the same these pairs are always different:
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Since I am initializing the key every time. But how else can I get the KeyPair without initializing them?
So I missed the main point, long story short can someone lead me on the following basic algorithm?
- Init keystore
- get the pairs
- ecrypt the string and save to the preferences
- quit the app
- Init keystore getting the correct keypairs for decryption
- Get encrypted string from preferences
- decrypt it to memory
I have no idea how this can be done using two different app launches. I always find code the encrypt and decrypt within the same app run.
thank you!