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?
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!
As you indicated in your question, you need to recover the key from the keystore and not initialize it every time.
Use this code to load AndroidKeyStore
and get the private key
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry entry = keyStore.getEntry(MY_KEY_NAME_INSIDE_KEYSTORE, null);
PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
Do not generate the key if it has been created. Check if exist with
keyStore.containsAlias(MY_KEY_NAME_INSIDE_KEYSTORE);
Recover public key with
PublicKey publicKey = keyStore.getCertificate(MY_KEY_NAME_INSIDE_KEYSTORE).getPublicKey();