安卓上的AES加密/解密无法运行

3

我需要帮助解决Android应用程序的加密/解密问题。

让我来解释一下情况。我实际上编写了一个应用程序,该应用程序使用由iPhone应用程序生成和加密的内容。

为了保证安全性,用户提供自己的密码短语以正确加密/解密不同平台之间的数据...

但是,在Android上遇到了加密/解密此密码短语的问题。

我有两个功能:

public byte[] crypt(String pStringToCrypt) throws Exception{

    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    System.arraycopy(this.passphrase.getBytes(), 0, key, 0, this.passphrase.getBytes().length);
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(pStringToCrypt.getBytes());
    return encrypted;

}

用于加密字符串的函数是:

public String decrypt(byte[] pCryptedStringtoDecrypt) throws Exception{
    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    System.arraycopy(this.passphrase.getBytes(), 0, key, 0, this.passphrase.getBytes().length);
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    byte[] encrypted = pCryptedStringtoDecrypt;
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    return originalString;
}

对于字符串的解密。

当我使用加密方法加密数据时,没有错误并且字符串已经被加密:

encrypted = [26, 119, -108, -24, 81, -128, 18, 35, -96, 10, -38, 69, 111, 40, 109, 107]

如果我尝试将这个字节转换为字符串,我会得到这个字符串:
encryptedString = "w��Q�#�\n�Eo(mk"

我认为加密阶段做得不错。现在当我尝试解密这个加密的字符串时,应用程序崩溃了:

javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
    at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:711)
    at javax.crypto.Cipher.doFinal(Cipher.java:1090)
    at org.vincentsaluzzo.lightrpc.common.security.AES256.decrypt(AES256.java:61)
    at com.vincentsaluzzo.LoginBox.model.SettingsManager.getUserPassphrase(SettingsManager.java:67)
    at com.vincentsaluzzo.LoginBox.mainActivity.onCreate(mainActivity.java:26)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1586)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)
    at android.app.ActivityThread.access$1500(ActivityThread.java:117)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:3647)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
    at dalvik.system.NativeStart.main(Native Method)

我不明白为什么会出现这个错误...

你有解决方案吗?或者可以给我一些解释吗?


你应该发布使用过的密码短语,否则很难追溯你的代码和加密结果。顺便说一下:永远不要使用密码短语作为密钥!使用密钥派生函数从密码生成密钥。 - Robert
@Robert 一种从密码生成密钥的关键派生函数:你有一些例子吗? - Vincent Saluzzo
PBKDF2:https://dev59.com/D2sz5IYBdhLWcg3wJUco - Robert
@Robert 好的,但是我在Objective-C中使用相同的代码(当然是使用苹果函数),结果很好...所以,即使为密钥使用短语不好,它仍然可以工作,对吗? - Vincent Saluzzo
1
大多数情况下,出问题的是(字符)编码部分,而不是实际的加密/解密。这只是实现的一部分。就安全而言,仍然可能存在许多问题,例如使用ECB模式加密、忘记添加完整性/身份验证、忘记创建随机IV、直接将密码用作密钥等等。 - Maarten Bodewes
2个回答

2

好的,借助您所有的评论,我解决了我的问题。

我来解释一下。我已经将我的两个方法转换为尽可能简单的形式:

public byte[] crypt(byte[] toCrypt) throws Exception {
    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    System.arraycopy(this.passphrase.getBytes(), 0, key, 0, ((this.passphrase.getBytes().length < 16) ? this.passphrase.getBytes().length : 16));
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(toCrypt);
    return encrypted;
}

并且

public byte[] decryptt(byte[] toDecrypt) throws Exception {
    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    System.arraycopy(this.passphrase.getBytes(), 0, key, 0, ((this.passphrase.getBytes().length < 16) ? this.passphrase.getBytes().length : 16));
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    byte[] original = cipher.doFinal(toDecrypt);
    return original;
}

简而言之,我在这些方法中删除了所有转换为字符串的内容。在我的其他Objective-C项目中,我检查了这些方法。我找到了问题:编码字符串!
有时,如果我加密/解密,我会得到相同的字节数组,但有时候数组是不同的,因为我把这个加密的字节数组存储在SharedPreferences的一个String中。
当然可能会这样,但我只是把字节放入一个新的String(bytes)中,基本的编码是UTF-8,所以那就是问题所在。
我使用Base64编码解决了这个问题。我使用了在此处找到的Base64类。
在保存加密的字节数组到SharedPreferences之前,我将它们编码成Base64编码,并在解密过程中也是如此。

这里的 this.passphrase 是什么?我在它上面得到了错误,无法解析或不是一个字段。 - Jayesh

0
一般来说,在密码学中使用 getBytes() 将字符串转换为字节并不是一个好习惯。因为您受限于计算机上的默认字符编码。更好的做法是为加密和解密都指定一个编码方式,如 getBytes("UTF-8")
您没有展示调用代码,您确定将字节数组(encrypted)传递给了解密函数而不是传递了字节数组的字符串,即"w��Q�#�\n�Eo(mk"?
ECB模式是不安全的,会泄露数据。您需要使用 CBC 模式或 CTR 模式来保证安全。

是的,但更好的方法是使用base64编码! - Vincent Saluzzo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接