CTR密码python与Java的建议

CTR密码python与Java的建议

本文介绍了需要有关AES CTR密码python与Java的建议的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用Python加密一些任意数据时,我正在研究项目(源)然后在java应用程序中使用相同的加密数据。

I'm working on project when some arbitrary data are encrypted using Python simple-crypt (source here) and same encrypted data are then used in java application.

我想了解JSSE和Pycrypto之间的概念差异。

I would like to understand conceptual difference between JSSE and Pycrypto.

这是进行加密的python部分():

This is python part doing encryption (source):

counter = Counter.new(HALF_BLOCK, prefix=salt[:HALF_BLOCK//8])
cipher = AES.new(cipher_key, AES.MODE_CTR, counter=counter)

这是我尝试java重新实现相同的操作:

This is my attempt for java re-implementation of same operation:

SecretKeySpec key = new SecretKeySpec(cipher_key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(salt, 0, HALF_BLOCK / 8);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

这里的问题是java Cipher抛出异常的初始化:

Problem here is that initialization of java Cipher throw exception:

java.security.InvalidAlgorithmParameterException: IV must be 16 bytes long.
    at org.bouncycastle.jce.provider.JCEBlockCipher.engineInit(Unknown Source)
    at javax.crypto.Cipher.init(Cipher.java:1394)
    at javax.crypto.Cipher.init(Cipher.java:1327)

HALF_BLOCK的值为64。

Value of HALF_BLOCK is 64.

所以问题是,python的AES实现如何与HALF_BLOCK / 8密钥大小和java不兼容?

So question is, how does python's AES implementation works with HALF_BLOCK/8 key size and java's not?

谢谢!

推荐答案

nonce(IV的左侧;right是顺序计数器)应与密文一起传输,作为IV。没有必要保持nonce秘密。只有不得重新使用由同一密钥加密的其他邮件。

The nonce (the "left side" of the IV; the "right" being the sequential counter) should be transported alongside the cipher text as the IV. There is no need to keep the nonce secret. It just must not be re-used for another message encrypted by the same key.

看来Python代码正在生成一个新的计数器,这是 64 位长并设置前缀(我假设 nonce 值)成为第一个 8 salt 变量的字节。它很可能(这里因为我无法访问Python代码而有很大的假设)在 0x00 * 8处启动实际的计数器值,所以你的初始IV将是:

It appears the Python code is generating a new Counter which is 64 bits long and sets the prefix (I'm assuming the nonce value) to be the first 8 bytes of the salt variable. It likely (huge assumption here because I don't have access to the Python code) starts the actual counter value at 0x00 * 8, so your initial IV would be:

salt = '#Eg����' # => UTF-8 encoding of 0x0123456789ABCDEF (not familiar enough with Python for the actual expression)
# Really may be misunderstanding, but as the AES IV must be 16 bytes, I imagine the terminology here is prefix = 8 bytes, sequence = 8 bytes
counter = Counter.new(HALF_BLOCK, prefix=salt[:HALF_BLOCK]) # => '0x01234567 89ABCDEF 00000000 00000000'
# Perform one encryption
counter # => '0x01234567 89ABCDEF 00000000 00000001'
# etc.

要在Java中执行相同的操作,它应该像将上面的 IvParameterSpec 初始化一样简单(即用 0 到16个字节)。

To perform the same operation in Java, it should be as simple as initializing your IvParameterSpec to the same as above (i.e. right-pad the first 8 bytes of the salt with 0 to 16 bytes).

// Intentionally verbose for demonstration; this can obviously be compacted
byte[] salt = org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEF");
byte[] nonceAndCounter = new byte[16];
System.arraycopy(salt, 0, nonceAndCounter, 0, ((int) (HALF_BLOCK / 8)));
IvParameterSpec iv = new IvParameterSpec(nonceAndCounter);

这是一个完整的测试用例,断言加密和解密是内部兼容的;您也可以使用Python端的数据运行此验证。

Here's a full test case which asserts that the encrypt and decrypt are internally compatible; you could also run this with data from the Python side to verify.

    @Test
    public void testPythonCompatibility() {
        // Arrange
        byte[] cipher_key = org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210");
        final int HALF_BLOCK = 64;
        byte[] salt = org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEF");
        byte[] nonceAndCounter = new byte[16];
        System.arraycopy(salt, 0, nonceAndCounter, 0, ((int) (HALF_BLOCK / 8)));
        IvParameterSpec iv = new IvParameterSpec(nonceAndCounter);
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
        SecretKeySpec key = new SecretKeySpec(cipher_key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);

        final String plaintext = "This is a plaintext message.";

        // Act
        byte[] cipherBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));

        // Assert
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] recoveredBytes = cipher.doFinal(cipherBytes);
        String recovered = new String(recoveredBytes, StandardCharsets.UTF_8);
        assert recovered.equals(plaintext);
    }

这篇关于需要有关AES CTR密码python与Java的建议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 20:48