需要有关 AES CTR 密码 python 与 Java 的建议
Need advice about AES CTR cipher python vs. Java
我正在处理一些任意数据使用 Python simple-crypt (source here) 加密的项目,然后在 java 应用程序中使用相同的加密数据。
我想了解 JSSE 和 Pycrypto 之间的概念差异。
这是 python 进行加密的部分 (source):
counter = Counter.new(HALF_BLOCK, prefix=salt[:HALF_BLOCK//8])
cipher = AES.new(cipher_key, AES.MODE_CTR, counter=counter)
这是我尝试 java 重新实现相同操作的尝试:
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 密码的初始化抛出异常:
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。
所以问题是,python 的 AES 实现如何与 HALF_BLOCK/8 密钥大小一起工作而 java 的 AES 实现如何不工作?
谢谢!
随机数(IV 的 "left side";"right" 是顺序计数器)应作为 IV 与密文一起传输。没有必要对随机数保密。它只是 不能 重新用于由同一密钥加密的另一条消息。
看来 Python 代码正在生成一个新的 Counter
,它有 64
位长并设置 prefix
(我假设 nonce
值)成为 salt
变量的前 8
个字节。它可能(这里是巨大的假设,因为我无法访问 Python 代码)在 0x00
* 8 处开始实际的 counter 值,所以你的初始 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
初始化为与上面相同一样简单(即用 [= 右填充 salt 的前 8 个字节21=] 到 16 个字节)。
// 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 方的数据进行验证。
@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);
}
我正在处理一些任意数据使用 Python simple-crypt (source here) 加密的项目,然后在 java 应用程序中使用相同的加密数据。
我想了解 JSSE 和 Pycrypto 之间的概念差异。
这是 python 进行加密的部分 (source):
counter = Counter.new(HALF_BLOCK, prefix=salt[:HALF_BLOCK//8])
cipher = AES.new(cipher_key, AES.MODE_CTR, counter=counter)
这是我尝试 java 重新实现相同操作的尝试:
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 密码的初始化抛出异常:
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。
所以问题是,python 的 AES 实现如何与 HALF_BLOCK/8 密钥大小一起工作而 java 的 AES 实现如何不工作?
谢谢!
随机数(IV 的 "left side";"right" 是顺序计数器)应作为 IV 与密文一起传输。没有必要对随机数保密。它只是 不能 重新用于由同一密钥加密的另一条消息。
看来 Python 代码正在生成一个新的 Counter
,它有 64
位长并设置 prefix
(我假设 nonce
值)成为 salt
变量的前 8
个字节。它可能(这里是巨大的假设,因为我无法访问 Python 代码)在 0x00
* 8 处开始实际的 counter 值,所以你的初始 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
初始化为与上面相同一样简单(即用 [= 右填充 salt 的前 8 个字节21=] 到 16 个字节)。
// 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 方的数据进行验证。
@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);
}