Android 密钥库无法从共享首选项中解密字符串
Android keystore can't decrypt strings from the shared preferences
我正在尝试使用 Keystore 加密一个字符串,并将该字符串保存到共享首选项中,然后在随后的应用程序启动时解密该字符串。然而,我相信我错过了密钥库的要点。
我主要是基于下面这个link:
我使用从不同问题发布的另一个线程编写了这个包装器
然而,我找到的所有示例代码都是在同一个应用程序中加密和解密的运行。这永远没有用。我需要加密我的字符串,保存在某个地方并在以后解密。所以这个包装器尝试这样初始化 KeyStore:
@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;
}
然后我尝试加密保存到首选项的一些字符串,如下
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);
}
}
最后我用这个 运行 做了一个:
testKeystoreHelper(true);
我退出了该应用程序,然后再次 运行:
testKeystoreHelper(假);
但这对我不起作用:
E/Error: java.io.IOException: Error while finalizing cipher at
javax.crypto.CipherInputStream.fillBuffer(CipherInputStream.java:104)
因为每次应用程序启动时,虽然键名相同,但这些键名总是不同的:
KeyPair keyPair = keyPairGenerator.generateKeyPair();
因为我每次都在初始化密钥。但是,如果不初始化它们,我还能如何获得 KeyPair?
所以我错过了要点,长话短说有人可以指导我了解以下基本算法吗?
- 初始化密钥库
- 得到对
- 加密字符串并保存到首选项
- 退出应用程序
- 初始化密钥库获取正确的解密密钥对
- 从首选项中获取加密字符串
- 解密到内存
我不知道如何使用两个不同的应用程序启动来完成此操作。我总是在同一个应用程序中找到加密和解密代码 运行。
谢谢!
正如您在问题中指出的,您需要从密钥库中恢复密钥,而不是每次都初始化它。
使用此代码加载AndroidKeyStore
并获取私钥
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();
如果已创建密钥,则不生成密钥。检查是否存在
keyStore.containsAlias(MY_KEY_NAME_INSIDE_KEYSTORE);
用
恢复public密钥
PublicKey publicKey = keyStore.getCertificate(MY_KEY_NAME_INSIDE_KEYSTORE).getPublicKey();
我正在尝试使用 Keystore 加密一个字符串,并将该字符串保存到共享首选项中,然后在随后的应用程序启动时解密该字符串。然而,我相信我错过了密钥库的要点。
我主要是基于下面这个link:
我使用从不同问题发布的另一个线程编写了这个包装器
然而,我找到的所有示例代码都是在同一个应用程序中加密和解密的运行。这永远没有用。我需要加密我的字符串,保存在某个地方并在以后解密。所以这个包装器尝试这样初始化 KeyStore:
@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;
}
然后我尝试加密保存到首选项的一些字符串,如下
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);
}
}
最后我用这个 运行 做了一个:
testKeystoreHelper(true);
我退出了该应用程序,然后再次 运行:
testKeystoreHelper(假);
但这对我不起作用:
E/Error: java.io.IOException: Error while finalizing cipher at
javax.crypto.CipherInputStream.fillBuffer(CipherInputStream.java:104)
因为每次应用程序启动时,虽然键名相同,但这些键名总是不同的:
KeyPair keyPair = keyPairGenerator.generateKeyPair();
因为我每次都在初始化密钥。但是,如果不初始化它们,我还能如何获得 KeyPair?
所以我错过了要点,长话短说有人可以指导我了解以下基本算法吗?
- 初始化密钥库
- 得到对
- 加密字符串并保存到首选项
- 退出应用程序
- 初始化密钥库获取正确的解密密钥对
- 从首选项中获取加密字符串
- 解密到内存
我不知道如何使用两个不同的应用程序启动来完成此操作。我总是在同一个应用程序中找到加密和解密代码 运行。
谢谢!
正如您在问题中指出的,您需要从密钥库中恢复密钥,而不是每次都初始化它。
使用此代码加载AndroidKeyStore
并获取私钥
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();
如果已创建密钥,则不生成密钥。检查是否存在
keyStore.containsAlias(MY_KEY_NAME_INSIDE_KEYSTORE);
用
恢复public密钥PublicKey publicKey = keyStore.getCertificate(MY_KEY_NAME_INSIDE_KEYSTORE).getPublicKey();