Android - 编码&使用私钥解码RSA?

时间:2014-09-25 17:33:02

标签: android encryption rsa keystore android-keystore

我正在尝试使用Android 4.3中引入的Android密钥库提供程序生成和存储的私钥对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。

任何人都可以帮我指出解决方案的正确方向吗?

2 个答案:

答案 0 :(得分:3)

使用公钥进行加密。

当您使用非对称加密算法时,您需要使用 公共密钥加密您的数据, < em>私有密钥只能再次解密

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

如果您从生成的对中获取公钥,则在加密字符串时以及解密时使用私钥时,您应该获得所需的结果。您可以通过访问保存私钥的密钥库对象中的 证书 来提取公钥。

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

答案 1 :(得分:1)

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