Android 密钥库无法从共享首选项中解密字符串

Android keystore can't decrypt strings from the shared preferences

我正在尝试使用 Keystore 加密一个字符串,并将该字符串保存到共享首选项中,然后在随后的应用程序启动时解密该字符串。然而,我相信我错过了密钥库的要点。

我主要是基于下面这个link:

https://medium.com/@ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3#.80y72xi61

我使用从不同问题发布的另一个线程编写了这个包装器

然而,我找到的所有示例代码都是在同一个应用程序中加密和解密的运行。这永远没有用。我需要加密我的字符串,保存在某个地方并在以后解密。所以这个包装器尝试这样初始化 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?

所以我错过了要点,长话短说有人可以指导我了解以下基本算法吗?

  1. 初始化密钥库
  2. 得到对
  3. 加密字符串并保存到首选项
  4. 退出应用程序
  5. 初始化密钥库获取正确的解密密钥对
  6. 从首选项中获取加密字符串
  7. 解密到内存

我不知道如何使用两个不同的应用程序启动来完成此操作。我总是在同一个应用程序中找到加密和解密代码 运行。

谢谢!

正如您在问题中指出的,您需要从密钥库中恢复密钥,而不是每次都初始化它。

使用此代码加载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();