【问题标题】:java.lang.IllegalArgumentException: bad base-64 when decrypting stringjava.lang.IllegalArgumentException:解密字符串时 base-64 错误
【发布时间】:2016-12-12 17:06:39
【问题描述】:

我正在尝试使用 KeyStore 加密一个字符串,并将这篇文章用作参考。

KeyPairGeneratorSpec replacement with KeyGenParameterSpec.Builder equivalents - Keystore operation failed

然而,当我解密字符串时,我不断得到这个“bad base-64”。我不完全了解如何解决此问题。我了解加密字符串包含解密器不知道的字符。但我不明白修复方法。

我看到了一些类似的帖子,但没有太大帮助,因为答案上没有代码。

java.lang.IllegalArgumentException: bad base-64

这是我的测试代码的片段,有人可以告诉我如何解密我的字符串吗?

Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
inCipher.init(Cipher.ENCRYPT_MODE, publicKey);

Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");
outCipher.init(Cipher.DECRYPT_MODE, privateKey);

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(
        outputStream, inCipher);
cipherOutputStream.write(plainText.getBytes("UTF-8"));
cipherOutputStream.close();

String ecryptedText = outputStream.toString();
Log.d(TAG, "Encrypt = " + ecryptedText);

String cipherText = ecryptedText;
CipherInputStream cipherInputStream = new CipherInputStream(
        new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), outCipher);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
    values.add((byte)nextByte);
}

byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
    bytes[i] = values.get(i).byteValue();
}

String finalText = new String(bytes, 0, bytes.length, "UTF-8");
Log.d(TAG, "Decrypt = " + ecryptedText);

【问题讨论】:

  • 50 分我的声望,谁可以粘贴代码,加密和解密 in memory 使用 android 密钥库的字符串。我不能将文件访问或共享首选项用于我的应用程序。我再说一遍,它必须在内存中完成。密钥保存在 KeyStore 中。
  • 您从ecryptedText 设置cipherText 并尝试对其进行base64 解码,但那首先不是base64 编码。使用类似ecryptedText = Base64.encode (/*BAOS*/ outputStream.toByteArray, Base64.DEFAULT);

标签: encryption android-keystore


【解决方案1】:

这是一个工作示例,说明如何使用 Android KeyStore 通过使用 ByteArrayOutputStreamByteArrayInputStream 来加密/解密内存字符串。请注意提供程序更改,&gt;= 6 使用 "AndroidKeyStoreBCWorkaround",旧版本使用 "AndroidOpenSSL"。此外,您必须使用 Base64.encodeToString 将加密数据编码为 Base64 字符串,如下所示:

String ecryptedText = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);

我基于您的代码的最终工作示例

try {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
    keyPairGenerator.initialize(
            new KeyGenParameterSpec.Builder(
                    "key1",
                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                    .build());
    KeyPair keyPair = keyPairGenerator.generateKeyPair();

    // error in android 6: InvalidKeyException: Need RSA private or public key AndroidOpenSSL
    // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
    String provider = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? "AndroidOpenSSL" : "AndroidKeyStoreBCWorkaround";

    Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
    inCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());

    Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
    outCipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CipherOutputStream cipherOutputStream = new CipherOutputStream(
            outputStream, inCipher);

    String plainText = "This is a text";

    cipherOutputStream.write(plainText.getBytes("UTF-8"));
    cipherOutputStream.close();

    String ecryptedText = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
    Log.d(TAG, "Encrypt = " + ecryptedText);

    String cipherText = ecryptedText;
    CipherInputStream cipherInputStream = new CipherInputStream(
            new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), outCipher);

    ArrayList<Byte> values = new ArrayList<>();
    int nextByte;
    while ((nextByte = cipherInputStream.read()) != -1) {
        values.add((byte)nextByte);
    }

    byte[] bytes = new byte[values.size()];
    for(int i = 0; i < bytes.length; i++) {
        bytes[i] = values.get(i).byteValue();
    }

    String finalText = new String(bytes, 0, bytes.length, "UTF-8");
    Log.d(TAG, "Decrypt = " + finalText);
} catch (javax.crypto.NoSuchPaddingException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (IOException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (NoSuchAlgorithmException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (NoSuchProviderException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (InvalidAlgorithmParameterException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (InvalidKeyException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (UnsupportedOperationException e) {
    Log.e(TAG, Log.getStackTraceString(e));
}

输出

D/MainActivity: Encrypt = rejkfeas3HgYnZOlC4S/R3KvlMTyiBjr5T6LqWGj9bq6nvpM0KBsoeYtr4OdCLITFX5GojuO4VpB
                Hy11n8zc9JcAx4IFW0Aw0/DfCmMDvIomQItBAaIWewZqNHc0UwS0y/JRhAe8SiTz5sFJ6Abvgax6
                vEfbYT0gzok+qtlfBNQLPvXejquhc0pZBaX1RgKDZyEJh3DBVRaFDgogK8XphaI/xtd1Cww9uO63
                QxA7HfrFUN8rJXrHF4EMi/yrDxs2xVHGF0v21xeuXRwLW9JXYn4fFAJJ0Jr8N5f03UDuKeNlI568
                RFVOGH7WpOLvKN4CDlsC+DT4Z8YVIOdtS/tO+Q==
D/MainActivity: Decrypt = This is a text

更新

对于 Android API 19,您只需要像这样使用之前的 KeyStore API KeyPairGeneratorSpec 而不是 KeyGenParameterSpec

try {
    Calendar start = Calendar.getInstance();
    Calendar end = Calendar.getInstance();
    end.add(Calendar.YEAR, 1);

    KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)
        .setAlias("key1")
        .setSubject(new X500Principal("CN=Sample Name, O=Android Authority"))
        .setSerialNumber(BigInteger.ONE)
        .setStartDate(start.getTime())
        .setEndDate(end.getTime())
        .build();

    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
    generator.initialize(spec);

    // error in android 6: InvalidKeyException: Need RSA private or public key AndroidOpenSSL
    // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
    String provider = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? "AndroidOpenSSL" : "AndroidKeyStoreBCWorkaround";

    KeyPair keyPair = generator.generateKeyPair();

    Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
    inCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());

    Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
    outCipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());


    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CipherOutputStream cipherOutputStream = new CipherOutputStream(
            outputStream, inCipher);

    String plainText = "This is a text";

    cipherOutputStream.write(plainText.getBytes("UTF-8"));
    cipherOutputStream.close();

    String ecryptedText = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
    Log.d(TAG, "Encrypt = " + ecryptedText);

    String cipherText = ecryptedText;
    CipherInputStream cipherInputStream = new CipherInputStream(
            new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), outCipher);

    ArrayList<Byte> values = new ArrayList<>();
    int nextByte;
    while ((nextByte = cipherInputStream.read()) != -1) {
        values.add((byte)nextByte);
    }

    byte[] bytes = new byte[values.size()];
    for(int i = 0; i < bytes.length; i++) {
        bytes[i] = values.get(i).byteValue();
    }

    String finalText = new String(bytes, 0, bytes.length, "UTF-8");
    Log.d(TAG, "Decrypt = " + finalText);
} catch (javax.crypto.NoSuchPaddingException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (IOException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (NoSuchAlgorithmException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (NoSuchProviderException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (InvalidAlgorithmParameterException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (InvalidKeyException e) {
    Log.e(TAG, Log.getStackTraceString(e));
} catch (UnsupportedOperationException e) {
    Log.e(TAG, Log.getStackTraceString(e));
}

【讨论】:

  • 克里斯托斯,感谢您的代码。我忘记提到的一件事是它需要在 API 19 上运行。上面的代码仅适用于 API 23 及更高版本。我需要支持旧设备。你会碰巧知道如何让它在 API 级别 19 上工作吗?
  • @gmmo 这真的很简单,你只需要使用旧的 KeyStore Spec API。请看看我更新的答案。
猜你喜欢
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-05
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多