【问题标题】:AES/CBC/PKCS5Padding encrypt in java decrypt in python goes wrongjava中的AES/CBC/PKCS5Padding加密python中的解密出错了
【发布时间】:2017-12-07 23:42:59
【问题描述】:

我正在尝试在 Java 中加密数据文件,然后在 Python 中解密。

但是解密后的数据文件总是有一些填充字节是这样的

my normal content^@^@^@^@^@

我实际上是在 python 代码中执行 UNPAD 操作 ( func decrypt_file() )

当我删除 UNPAD 操作时,我得到了这个:

my normal content^@^@^@^@^@^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P

所以,Java encrypt 方法似乎做了两次填充。

我很困惑,卡在这里。有人可以帮忙吗?非常感谢!

这是我的代码。

(1)java版本

import java.io.File;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESUtil {
    private static final String key = "wesurexZ5!Hcurfit";
    private static final String ivs = "zK2hzBvP%FRJ5%lD";
    public static byte[] encrypt(byte[] strInBytes) throws Exception {
        SecretKeySpec skeySpec = getKey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(ivs.getBytes("UTF-8"));
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        return cipher.doFinal(strInBytes);
    }

    public static byte[] decrypt(byte[] strIn) throws Exception {
        SecretKeySpec skeySpec = getKey(key);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(ivs.getBytes("UTF-8"));
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] decrypted = cipher.doFinal(strIn);
        return decrypted;
    }

    private static SecretKeySpec getKey(String strKey) throws Exception {
        byte[] arrBTmp = strKey.getBytes();
        byte[] arrB = new byte[16];
        for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }
        SecretKeySpec skeySpec = new SecretKeySpec(arrB, "AES");
        return skeySpec;
    }
}

(2)python版本

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

reload(sys)
sys.setdefaultencoding("utf-8")

from Crypto.Cipher import AES


class AESTool(object):

    def __init__(self, iv, key):
        self.iv = iv.decode("utf8")
        self.key = key
        bs = AES.block_size
        self.pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
        self.unpad = lambda s: s[0:-ord(s[-1])]

    def encrypt(self, plain_text):
        try:
            encryptor = AES.new(self.key, AES.MODE_CBC, self.iv)
            encrypted_text = encryptor.encrypt(self.pad(plain_text))
        except Exception as ex:
            raise Exception(ex.message)

        return encrypted_text

    def decrypt(self, decrypted_text):
        try:
            decryptor = AES.new(self.key, AES.MODE_CBC, self.iv)
            plain_text = decryptor.decrypt(decrypted_text)
        except Exception as ex:
            raise Exception(ex.message)

        return self.unpad(plain_text)

    def encrypt_file(self, from_full_path, to_full_path, chunksize=64*1024):
        encryptor = AES.new(self.key, AES.MODE_CBC, self.iv)
        try:
            with open(from_full_path, 'rb') as infile:
                with open(to_full_path, 'wb') as outfile:
                    while True:
                        chunk = infile.read(chunksize)

                        if len(chunk) == 0:
                            break

                        if len(chunk) < chunksize:
                            outfile.write(encryptor.encrypt(self.pad(chunk)))
                            break

                        outfile.write(encryptor.encrypt(chunk))
        except Exception as ex:
            return -1

        return 0

    def decrypt_file(self, from_full_path, to_full_path, chunksize=64*1024):
        decryptor = AES.new(self.key, AES.MODE_CBC, self.iv)
        try:
            with open(from_full_path, 'rb') as infile:
                with open(to_full_path, 'wb') as outfile:
                    prev_chunk = None
                    while True:
                        chunk = infile.read(chunksize)

                        if len(chunk) == 0 and prev_chunk:
                            outfile.write(self.unpad(decryptor.decrypt(prev_chunk)))
                            break

                        if prev_chunk:
                            outfile.write(decryptor.decrypt(prev_chunk))

                        if len(chunk) < chunksize:
                            outfile.write(self.unpad(decryptor.decrypt(chunk)))
                            break

                        prev_chunk = chunk
        except Exception as ex:
            return -1

        return 0

【问题讨论】:

  • IV 需要是随机的,并且每次加密都不同。
  • @zaph 我只知道重复使用固定的 IV 可能会导致安全问题。但这怎么会带来这样的功能问题呢?不是很懂,可以补充一下吗?非常感谢。
  • 该评论不仅适用于 OP,还适用于未来的读者,指出问题中的固定 IV 安全性差。
  • @RonnieTsang : 在 python 代码的情况下 iv 的值是多少?

标签: java python aes padding pkcs#5


【解决方案1】:

您知道当您尝试使用默认模式进行编码/解码时是否会发生这种情况?很想知道,因为如果是这样,那么我们遇到了同样的问题。我的意思是默认使用:

Cipher cipher = Cipher.getInstance("AES");

代替;

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

【讨论】:

  • 感谢您的信息。我不知道默认模式以前可能会遇到同样的问题。但是,不同平台上的加密/解密肯定会导致与填充相关的不确定性问题。我正在寻找一种跨平台的方式来使用 aes,我找到了aes-cross。作者的结论是“如果你在一个平台上找到了 AES/CBC/PKCS5Padding 的方法,那么你已经得到了跨平台的 AES 解决方案。”我的代码在上面
  • 最好完全指定加密属性,例如模式和填充。
猜你喜欢
  • 2018-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多