【问题标题】:Android - Encode & Decode RSA with Private Key?Android - 使用私钥对 RSA 进行编码和解码?
【发布时间】:2014-09-25 17:33:01
【问题描述】:

我正在尝试使用在 Android 4.3 中引入的 Android Key Store Provider 生成和存储的私钥对 Android 上的字符串进行编码和解码

我可以使用以下代码成功生成并获取私钥:

 private void generatePrivateKey(Activity context, String alias){
    /** Generate a new entry in the KeyStore by using the  * KeyPairGenerator API. We have to specify the attributes for a  * self-signed X.509 certificate here so the KeyStore can attach  * the public key part to it. It can be replaced later with a  * certificate signed by a Certificate Authority (CA) if needed.  */

    Calendar cal = Calendar.getInstance();
    Date now = cal.getTime();
    cal.add(Calendar.YEAR, 1);
    Date end = cal.getTime();

    KeyPairGenerator kpg = null;
    try {
        kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    }
    try {
        kpg.initialize(new KeyPairGeneratorSpec.Builder(context)
                .setAlias(alias)
                .setStartDate(now)
                .setEndDate(end)
                .setSerialNumber(BigInteger.valueOf(1))
                .setSubject(new X500Principal("CN=" + alias))
                .build());
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }

    KeyPair kp = kpg.generateKeyPair();

    /*
 * Load the Android KeyStore instance using the the
 * "AndroidKeyStore" provider to list out what entries are
 * currently stored.
 */
    KeyStore ks = null;
    try {
        ks = KeyStore.getInstance("AndroidKeyStore");
        ks.load(null);
        Enumeration<String> aliases = ks.aliases();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    /*
 * Use a PrivateKey in the KeyStore to create a signature over
 * some data.
 */

    KeyStore.Entry entry = null;
    try {
        entry = ks.getEntry(alias, null);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (UnrecoverableEntryException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    }
    if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
        Log.w("E", "Not an instance of a PrivateKeyEntry");
    }
    else{
        Log.w("E", "Got Key!");
        privateKeyEntry = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
    }

}

这是我用于加密(编码)和解密(解码)的代码:

private String encryptString(String value){
    byte[] encodedBytes = null;
    try {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
        cipher.init(Cipher.ENCRYPT_MODE,  privateKeyEntry );
        encodedBytes = cipher.doFinal(value.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }

    return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}

private String decryptString(String value){
    byte[] decodedBytes = null;
    try {
        Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
        c.init(Cipher.DECRYPT_MODE,  privateKeyEntry );
        decodedBytes = c.doFinal(Base64.decode(value, Base64.DEFAULT));
    } catch (Exception e) {
        e.printStackTrace();
    }

    return new String(decodedBytes);
}

加密似乎工作正常,但是当我尝试解密它时,我收到以下错误:

javax.crypto.BadPaddingException: error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02

谷歌搜索这似乎表明用于解密的私钥与用于解密的私钥不同,但在我的代码中,我对两者使用完全相同的私钥。我还看到它建议手动设置密钥大小,但在 KeyPairGenerator 构建器中这样做:

.setKeySize(1024);

不起作用,似乎只在 API 19 上可用,我需要以 API 18 为目标。

谁能帮我指出解决方案的正确方向?

【问题讨论】:

    标签: android encryption rsa keystore android-keystore


    【解决方案1】:

    没有使用公钥进行加密。

    当您使用非对称加密算法时,您需要使用 公钥加密您的数据,而 私有密钥只是为了再次解密它。

    除了加密之外,您还可以使用 私钥 进行签名,但这不是您想要的,所以我们暂时忘记它。

    如果你从生成的对中获取公钥,当你加密你的字符串时,以及解密时的私钥,你应该得到想要的结果。您可以通过从保存您的私钥的密钥库对象中访问 证书 来提取公钥。

    或者,您也可以使用像 AES 这样的对称算法,这样可以让您的工作更轻松。另外,对称算法通常要快得多,这就是为什么从不纯粹使用非对称算法,而是与对称算法结合使用,构建所谓的混合算法。

    【讨论】:

    • 完美的 Mjoellnir 可以工作,我不知道您也可以从 KeyPairGenerator 获取公钥,并认为从它返回的单个密钥可能会同时处理这两个问题。我也有兴趣了解有关 AES 的更多信息,您知道是否可以将其与 Android Key Store Provider 一起使用?我对它的阅读表明不支持 AES,所有示例似乎都使用 RSA。我之前使用 AES 实现了一个可行的解决方案,但无法找到安全存储我的私钥的方法,因此我求助于 Android Key Store Provider 和 RSA。
    • 好吧,我在几个 Android 应用程序中使用 AES 完全没有问题,而且它已成为整个互联网上日常 SSL/TLS 加密通信中不可或缺的一部分,所以在像 Android 这样的平台上不支持它真的是没有选择的。但是,我以前没有经常使用 KeyStore 对象,所以我不能真正告诉你太多关于它的特定怪癖。但是,如果您在密码实例中使用“AES/CBC/PKCS5Padding”这样的字符串,在需要的算法中使用“AES”,它应该会做得很好。
    • 您是否需要将您的私钥存储在设备上的任何 Android 应用程序中?如果是这样,您如何安全地做到这一点并且仍然能够使用 AES?
    • 视情况而定。大多数情况下,我使用 AES 进行文件加密。我让用户输入密码,并使用该密码、盐和 IV 生成 AES 密钥。我实际上并没有存储密钥,只有盐和 IV(在文件中),并且密码必须始终由用户手动输入。解密后,我可以根据密码(由用户再次输入)重建密钥,从文件中读取盐和 IV(没有密码完全没用,所以可以安全地存储在文件中),然后我可以使用密钥用于解密。
    • 这实际上导致您的原始问题有点偏离,应该避免。但简而言之:我正在使用 Android 的板载工具,如 Cipher、CipherInputStream、CipherOutputStream 等。如果你朝这个方向看,我相信你会找到答案。
    【解决方案2】:

    签名生成与加密不同。如果要加密,则需要使用公钥加密并使用私钥解密。如果要生成签名,则需要使用私钥签名并使用公钥进行验证。此顺序不能颠倒,也不能混合(安全)。

    【讨论】:

    • 谢谢 owlstead,我的目标是加密密码,将其保存到 sharedpreferences 中,然后在我将其从 sharedpreferences 中取出时对其进行解密。我可以使用 SecrectKey 成功地做到这一点,但我希望我上面采用的方法能够让我安全地存储密钥并使用它来加密和解密。我采取的方法根本不可能吗?
    • 我想这已经回答了,现在你掌握了公钥?请注意,通常只应为其他服务保存密码加密。要将它们安全地存储在数据库中,请改用 PBKDF2 或 bcrypt。
    • 嗨,owlstead,是的,这正在工作,但我也有兴趣进一步了解我在这方面的知识。你能澄清一下你所说的其他服务是什么意思吗?我这样做的原因是在设备上存储用户登录详细信息,这样他们就不必每次都通过繁琐的登录屏幕。对于从 PBKDF2 或 bcrypt 特别关注 Android 开始,您有什么好的参考资料吗?
    • 嗯,不,你需要加密。 PBKDF2 或 bcrypt 更多的是服务器端(抱歉,这里忘记了 Android 标签),尽管您可以将执行 PBKDF2 所需的一些 CPU 能力卸载到 android 设备。
    猜你喜欢
    • 2014-10-16
    • 1970-01-01
    • 2016-05-18
    • 2012-07-13
    • 2020-06-26
    • 2015-03-10
    • 1970-01-01
    • 2013-01-16
    相关资源
    最近更新 更多