需要关于AES CTR密码python与Java的建议吗
我正在进行一个项目,使用Python(源代码)对一些任意数据进行加密,然后在java应用程序中使用相同的加密数据 我想了解JSSE和Pycrypto在概念上的区别 这是python的加密部分(): 这是我对java重新实现相同操作的尝试:需要关于AES CTR密码python与Java的建议吗,java,aes,pycrypto,python-simple-crypt,Java,Aes,Pycrypto,Python Simple Crypt,我正在进行一个项目,使用Python(源代码)对一些任意数据进行加密,然后在java应用程序中使用相同的加密数据 我想了解JSSE和Pycrypto在概念上的区别 这是python的加密部分(): 这是我对java重新实现相同操作的尝试: SecretKeySpec key = new SecretKeySpec(cipher_key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(salt, 0, HALF_BLOCK / 8); C
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的初始化引发异常:
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)
半块的值为64
所以问题是,python的AES实现如何使用半块/8键大小而java不使用
谢谢 nonce(IV的“左侧”,“右侧”是顺序计数器)应与密码文本一起作为IV传输。无需对nonce保密。它只是不能被用同一密钥加密的另一封邮件重复使用
Python代码似乎正在生成一个新的计数器
,其长度为64位,并将前缀
(我假设nonce
值)设置为salt
变量的第一个8
字节。很可能(这里的假设很大,因为我无法访问Python代码)会在0x00
*8处启动实际的计数器值,因此您的初始IV为:
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
右键填充salt的前8个字节到16个字节)
这里有一个完整的测试用例,它断言加密和解密是内部兼容的;您还可以使用来自Python端的数据运行此程序以进行验证
@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);
}
通常最好不要使用CTR模式,因为它很容易出错。问题是,同一个密钥和计数器决不能重复使用。通常,CBC模式与随机IV一起使用,PKCS#7填充用于容纳不是块大小倍数的输入。@zaph:没错。不幸的是,这是所用python库给出的要求。注意:PyCrypto的计数器的默认初始值。new
为1,您需要根据java中使用的值对其进行设置。Cipher.getInstance
中的“BC”
是什么,表明它正在用作安全提供程序。您还可以使用本机Sun JCE等。有关更多信息,请参阅文档。
// 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);
@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);
}