Android上的RSA解密中的IllegalBlockSizeException“ null”

时间:2018-10-09 10:27:09

标签: android encryption rsa android-keystore encryption-asymmetric

我目前正在使用我的加密软件的Android客户端,但是我一直收到IllegalBlockSizeException,而e.getMessage()总是返回null

这是我用来查找问题的代码

try {
    KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore");
    generator.initialize(new KeyGenParameterSpec.Builder(
            "1",
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
            .setDigests(KeyProperties.DIGEST_SHA256)
            .setKeySize(2048)
            .build());
    KeyPair kp = generator.generateKeyPair();

    KeyStore store = KeyStore.getInstance("AndroidKeyStore");
    store.load(null);
    PublicKey pubKey = store.getCertificate("1").getPublicKey();
    PrivateKey privkey = ((KeyStore.PrivateKeyEntry) store.getEntry("1",null)).getPrivateKey();

    byte[] content = "123456789".getBytes();
    Cipher encrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
    encrypt.init(Cipher.ENCRYPT_MODE,pubKey);
    Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
    decrypt.init(Cipher.DECRYPT_MODE,privkey);

    byte[] A = encrypt.doFinal(content);
    byte[] B = decrypt.doFinal(A);

    String resultA = new String(A);
    String resultB = new String(B);
    Toast.makeText(getApplicationContext(), resultA + "\n" + resultB, Toast.LENGTH_LONG).show();
} catch (Exception e) {
    e.printStackTrace();
    Toast.makeText(getApplicationContext(), e.toString()+"\n"+e.getMessage(), Toast.LENGTH_LONG).show();
}

我说过有一个IllegalBlockSizeException,我发现它是由decrypt.doFinal()抛出的,而e.getMessage()返回null

这是我从调试控制台获得的信息

W/System.err: javax.crypto.IllegalBlockSizeException
    at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
    at javax.crypto.Cipher.doFinal(Cipher.java:1741)
W/System.err:     at storm.cyanine.decryptor.MainActivity$override.onJob(MainActivity.java:187)
    at storm.cyanine.decryptor.MainActivity$override.onOpen(MainActivity.java:115)
    at storm.cyanine.decryptor.MainActivity$override.access$dispatch(Unknown Source:50)
    at storm.cyanine.decryptor.MainActivity.onOpen(Unknown Source:15)
    at java.lang.reflect.Method.invoke(Native Method)
    at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
W/System.err:     at android.view.View.performClick(View.java:6329)
    at android.view.View$PerformClick.run(View.java:24996)
    at android.os.Handler.handleCallback(Handler.java:809)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:166)
    at android.app.ActivityThread.main(ActivityThread.java:7377)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)
W/System.err: Caused by: android.security.KeyStoreException: Unknown error
    at android.security.KeyStore.getKeyStoreException(KeyStore.java:709)
    at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
    at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
    ... 16 more

起初,我认为是AndroidKeyStore的某些限制使我无法使用私钥,所以我尝试直接使用KeyPairGenerator生成的私钥,但没有任何改变

什么可能导致此问题?

我已经在网上搜索了好几天,但我发现上面的代码没什么问题(除了不是最终产品)

我的设备是Android 8.1.0

  

编辑:非常感谢Steve Miskovetz提供的解决方案,我刚刚找到了该主题的Android官方指南:   Cryptography

     

(不知道为什么我无法更早找到它)

3 个答案:

答案 0 :(得分:1)

您的问题似乎是由Android Oreo引入的,但是有一种解决方法。

此stackoverflow帖子对此进行了讨论:
Android 8.0: IllegalBlocksizeException when using RSA/ECB/OAEPWithSHA-512AndMGF1Padding

此Google问题跟踪工具对此进行了很好的讨论:
https://issuetracker.google.com/issues/36708951

您需要添加以下行:

OAEPParameterSpec sp = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);

并修改这些行,添加sp参数:

encrypt.init(Cipher.ENCRYPT_MODE,pubKey,sp);
decrypt.init(Cipher.DECRYPT_MODE,privkey,sp);

您的完整代码,此处有修改:

try {
    KeyPairGenerator generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore");
    generator.initialize(new KeyGenParameterSpec.Builder(
            "1",
            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
            .setDigests(KeyProperties.DIGEST_SHA256)
            .setKeySize(2048)
            .build());
    KeyPair kp = generator.generateKeyPair();

    KeyStore store = KeyStore.getInstance("AndroidKeyStore");
    store.load(null);
    PublicKey pubKey = store.getCertificate("1").getPublicKey();
    PrivateKey privkey = ((KeyStore.PrivateKeyEntry) store.getEntry("1",null)).getPrivateKey();

    byte[] content = "123456789".getBytes();
    OAEPParameterSpec sp = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
    Cipher encrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
    encrypt.init(Cipher.ENCRYPT_MODE,pubKey,sp);
    Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
    decrypt.init(Cipher.DECRYPT_MODE,privkey,sp);

    byte[] A = encrypt.doFinal(content);
    byte[] B = decrypt.doFinal(A);

    String resultA = new String(A);
    String resultB = new String(B);
    Toast.makeText(getApplicationContext(), resultA + "\n" + resultB, Toast.LENGTH_LONG).show();
} catch (Exception e) {
    e.printStackTrace();
    Toast.makeText(getApplicationContext(), e.toString()+"\n"+e.getMessage(), Toast.LENGTH_LONG).show();
}

答案 1 :(得分:0)

因此,我进行了更多测试...结果表明:PrivateKey,无论它是由KeyPairGenerator生成,从文件导入还是从KeyStore导入,都无法一旦将AndroidKeyStoreKeyProperties.PURPOSE_ENCRYPT属性添加为KeyProperties.PURPOSE_DECRYPT作为提供者,就可用于解密。

但是,SecretKey(一种对称算法)在AndroidKeyStore中存储/生成时,可以毫无问题地用于加密/解密。

所以看来AndroidKeyStore毕竟不允许在非对称算法中进行加密/解密,这在Android keystore system指南中没有明确说明。

如果有人想对AndroidKeyStore使用非对称算法加密/解密,这是我用于测试的代码。它先对字符串"123456789"进行加密,然后对其解密,然后在烤面包中显示解密后的字符串,该文本应为"123456789"

对我来说很好

String alias = "2";
KeyGenerator generator = KeyGenerator.getInstance("AES","AndroidKeyStore");
generator.init(new KeyGenParameterSpec.Builder(
    alias,
    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
    .setKeySize(128)
    .build());
generator.generateKey();

KeyStore store = KeyStore.getInstance("AndroidKeyStore");
store.load(null);

SecretKey secret = ((KeyStore.SecretKeyEntry)store.getEntry(alias,null)).getSecretKey();

byte[] content = "123456789".getBytes();
Cipher encrypt = Cipher.getInstance("AES/CBC/PKCS7Padding");
encrypt.init(Cipher.ENCRYPT_MODE,secret);
Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS7Padding");
decrypt.init(Cipher.DECRYPT_MODE,secret,new IvParameterSpec(encrypt.getIV()));

byte[] A = encrypt.doFinal(content);
byte[] B = decrypt.doFinal(A);

String resultA = new String(A);
String resultB = new String(B);
Toast.makeText(getApplicationContext(), resultB, Toast.LENGTH_LONG).show();

答案 2 :(得分:0)

非对称密钥加密可从android 6+(api 23+)中获得。这是android加密的一个很好的指南。另外,在调用getBytes之后,您需要使用Base64对字节进行编码。