Android密钥库无法解密共享首选项

时间:2016-12-12 04:31:57

标签: android cryptography android-keystore

我正在尝试使用密钥库来加密字符串,并将此字符串保存到共享首选项,然后在后续的子应用程序启动时解密字符串。然而,我相信我错过了密钥库的主要观点。

大多数情况下,我基于以下链接:

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

我使用另一个我从其他问题

发布的帖子编写了这个包装器

java.lang.IllegalArgumentException: bad base-64 when decrypting string

然而,我在同一个App运行期间找到,加密和解密的所有示例代码。这永远不会有用。我需要加密我的字符串,保存在某处并在以后解密。所以这个包装器尝试初始化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,我怎么能得到KeyPair呢?

所以我错过了主要观点,长话短说,有人会引导我使用以下基本算法吗?

  1. Init keystore
  2. 获得对
  3. 生成字符串并保存到首选项
  4. 退出应用
  5. Init keystore获取正确的密钥对进行解密
  6. 从首选项中获取加密字符串
  7. 解密到内存
  8. 我不知道如何使用两个不同的应用程序启动来完成此操作。我总是在相同的应用程序运行中找到加密和解密的代码。

    谢谢你!

1 个答案:

答案 0 :(得分:0)

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

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

使用

恢复公钥
PublicKey publicKey = keyStore.getCertificate(MY_KEY_NAME_INSIDE_KEYSTORE).getPublicKey();