【问题标题】:Decrypt an AES encoded message (encrypted in Python) in Java在 Java 中解密 AES 编码的消息(在 Python 中加密)
【发布时间】:2013-11-14 20:03:00
【问题描述】:

我想用 Java 解密 AES 加密消息。我一直在尝试来自standard libraryBouncyCastle 的各种算法/模式/填充选项。不走运:-(

加密实体是用 Python 编写的,并且已经投入生产。加密的消息已经出去了,所以我不能轻易改变那部分。 Python 代码如下所示:

from Crypto.Cipher import AES
import base64
import os
import sys

BLOCK_SIZE = 16
PADDING = '\f'

pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING

EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)

secret = 'XXXXXXXXXXXXXXXX'

cipher = AES.new(secret)

clear='test'
encoded = EncodeAES(cipher, clear)

print 'Encrypted string:>>{}<<'.format(encoded)

decoded = DecodeAES(cipher, encoded)

print 'Decrypted string:>>{}<<'.format(decoded)

显然使用了AES,我发现我必须使用ECB模式。但我还没有找到适用于 Java 端的填充模式。如果输入适合块大小并且没有发生填充,我可以用 Java 解密消息。如果消息需要填充,则解密失败。

要解密的 Java 代码如下所示:

public class AESPaddingTest {

    private enum Mode {
        CBC, ECB, CFB, OFB, PCBC
    };

    private enum Padding {
        NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
    }

    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = new byte[] { 'X', 'X', 'X', 'X',
            'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' };

    @BeforeClass
    public static void configBouncy() {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Test
    public void testECBPKCS5Padding() throws Exception {
        decrypt("bEpi03epVkSBTFaXlNiHhw==", Mode.ECB,
                Padding.PKCS5Padding);
    }

    private String decrypt(String valueToDec, Mode modeOption,
            Padding paddingOption) throws GeneralSecurityException {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);

        Cipher c = Cipher.getInstance(ALGORITHM + "/" + modeOption.name() + "/" + paddingOption.name());

        c.init(Cipher.DECRYPT_MODE, key);

        byte[] decValue = c.doFinal(valueToDec.getBytes());

        String clear = new String(Base64.encodeBase64(decValue));

        return clear;
    }

}

抛出的错误是:

javax.crypto.IllegalBlockSizeException:输入长度必须是 16 的倍数时 用填充密码解密

有什么想法吗?

【问题讨论】:

  • 如果您可以分享您在解密时收到的错误消息,那么它将加快解决过程。
  • 当然可以。抛出“javax.crypto.IllegalBlockSizeException:使用填充密码解密时输入长度必须是 16 的倍数”异常。
  • 您是先解码 Base64 编码的消息,然后再将其传递给解密函数吗?
  • 顺便说一句,PKCS#5 填充 == PKCS#7 填充

标签: java python encryption cryptography bouncycastle


【解决方案1】:

感谢您提出的好问题、答案和 cmets。 我对代码做了一些小的改动,现在它对我来说就像一个魅力。

import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import android.util.Base64;

public class AESTest {

    public enum Mode {
        CBC, ECB, CFB, OFB, PCBC
    };

    public enum Padding {
        NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
    }

    private static final String ALGORITHM = "AES";

    private static final byte[] keyValue ="myKey".getBytes();


    String decrypt(String valueToDec, Mode modeOption,
            Padding paddingOption) throws GeneralSecurityException {



        byte[] decodeBase64 = Base64.decode(valueToDec.getBytes(),0);

        Key key = new SecretKeySpec(keyValue, ALGORITHM); 
        Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); 
        c.init(Cipher.DECRYPT_MODE, key); 
        byte[] encValue = c.doFinal(decodeBase64); 
        return new String(encValue).trim();

    }

}

然后,我在我的 Android Activity 中使用了该类,如下所示:

AESTest aes=new AESTest();
String decrypted = aes.decrypt(myCipheredText,Mode.ECB,Padding.NoPadding);

注意python代码中的secret和java代码中的mykey是一样的。

【讨论】:

    【解决方案2】:

    您正在使用换页符 (\f) 进行填充。我不知道执行此操作的标准填充方案。因此,我建议您在 Java 端选择NoPadding,并准备从解密后得到的明文中去除\f 字符。

    由于您能够解密未填充的明文,因此它表明您在双方都有相同的密钥材料(这是一个常见的问题,我很高兴我们可以从列表中删除)。

    阅读the Python documentation,似乎默认选择了ECB模式。因此,请确保在 Java 端使用它。

    【讨论】:

    • 你是对的,NoPadding。我的测试代码的主要问题是 encodeBase64 的顺序和解密。当我尝试您的建议时,我才意识到这一点。正确的工作代码: byte[] decodeBase64 = Base64.decodeBase64(valueToDec.getBytes()); Key key = new SecretKeySpec(keyValue, ALGORITHM);密码 c = Cipher.getInstance("AES/ECB/NoPadding"); c.init(Cipher.DECRYPT_MODE, key); byte[] encValue = c.doFinal(decodeBase64); return new String(encValue).trim();
    猜你喜欢
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 2018-09-07
    • 2020-12-23
    • 2015-04-21
    相关资源
    最近更新 更多