繁体   English   中英

AES 256-CBC上的密钥和iv的问题

[英]Issue with key and iv on AES 256-CBC

我从Python获得了一个加密的base64字符串。

格式为AES 256 CBC,但是当我尝试使用Android解密时,它返回的解密字符串为nil。

蟒蛇

# coding=utf-8
import base64
from random import choice
from string import letters

try:
    from Crypto import Random
    from Crypto.Cipher import AES
except ImportError:
    import crypto
    import sys

    sys.modules['Crypto'] = crypto
    from crypto.Cipher import AES
    from crypto import Random


class AESCipher(object):
    def __init__(self, key):
        self.bs = 32
        self.key = key

    def encrypt(self, raw):
        _raw = raw
        raw = self._pad(raw)

        print raw, ';'
        print _raw, ';'

        iv = "".join([choice(letters[:26]) for i in xrange(16)])
        print " iv :", iv
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        a = (self.bs - len(s) % self.bs)
        b = chr(self.bs - len(s) % self.bs)
        return s + a * b

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s) - 1:])]
def encrypt(k, t):
    o = AESCipher(k)
    return o.encrypt(t)


def decrypt(k, t):
    o = AESCipher(k)
    return o.decrypt(t)


def main():
    k = "qwertyuiopasdfghjklzxcvbnmqwerty"
    s1 = "Hello World!"

    d2 = encrypt(k, s1)

    print " Password :", k
    print "Encrypted :", d2
    print "    Plain :", decrypt(k, d2)

if __name__ == '__main__':
    main()

爪哇

在这里我使用https://github.com/fukata/AES-256-CBC-Example

final String aEcodedSting = "aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk";
String decrypted = AESUtil.decrypt(aEcodedSting);

当我尝试解密时,我得到了

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}: 
java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT

这是Python加密输出:

Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
iv : iegejanpeybezgmy
plainText : ser456&*(

如果有人可以使用其他库解决此问题,请通知我。

有四个问题:

  1. python输出和java输入之间的区别
  2. IV和键不同
  3. 不同的密钥创建
  4. 填充

1)当前,您的python代码输出是iv + encrypted_data crypto_data的base64编码

return base64.b64encode(iv + cipher.encrypt(raw))

但是在Java中,您是直接解密原始数据。

你应该这样解决

// Decode base64
byte[] array = Base64.decode(src);
// Get only encrypted data (removing first 16 byte, namely the IV)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Decrypt data
decrypted = new String(cipher.doFinal(encrypted));

2)您必须使用相同的IV和键进行输入和输出,因此您应该从python控制台输出中复制它们:

iv : qbmocwtttkttpqvv
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : anZxZHVpaWJpb2FhaWdqaCK0Un7H9J4UlXRizOJ7s8lchAWAPdH4GRf5tLAkCmm6
    Plain : Hello World!

并粘贴在Java代码中:

private static final String ENCRYPTION_KEY = "qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";

3)在python中,您将密钥用作字符串,但是在java库中,在将其用于解密之前已对其进行了哈希处理,因此应更改makeKey()方法:

static Key makeKey() {
    try {
        byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
        return new SecretKeySpec(key, "AES");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return null;
}

4)最后,您不需要在Java中使用"AES/CBC/PKCS5Padding"指定填充,因为这样您可以强制Cipher自动填充。

您可以在您的"AES/CBC/NoPadding" decrypt()方法中简单地使用"AES/CBC/NoPadding" ,因此它应如下所示:

public static String decrypt(String src) {
    String decrypted = "";
    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
        byte[] array = Base64.decode(src);
        byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
        decrypted = new String(cipher.doFinal(encrypted));
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return decrypted;
}

您的base64和IV的Java输出:

encrypted: aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
decrypted: ser456&*(

编辑:

Artjom B.的建议(谢谢),最好直接从密文中读取IV,而不是在AESUtil中进行硬编码。

您的输入内容包括前16个字节的IV和后16个字节的加密文本,因此您可以利用这一点。

public static String decrypt(String src) {
    String decrypted = "";
    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        // Decode input
        byte[] array = Base64.decode(src);
        // Read first 16 bytes (IV data)
        byte[] ivData = Arrays.copyOfRange(array, 0, 16);
        // Read last 16 bytes (encrypted text)
        byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
        // Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
        cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
        // Decrypt same old way
        decrypted = new String(cipher.doFinal(encrypted));
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return decrypted;
}

而且,正如这里所说

Python代码使用32字节的块大小进行填充,这意味着Java仍将无法解密所有可能的密文的一半。 AES块大小为16个字节,应在Python实现中更改

您可以按以下方式更改Python类( AES.block_size等于16):

class AESCipher(object):
    def __init__(self, key):
        self.bs = AES.block_size
        self.key = key

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM