我在c:
中使用以下代码加密了文件unsigned char ckey[] = "0123456789ABCDEF";
unsigned char iv[8] = {0};
AES_set_encrypt_key(ckey, 128, &key);
AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);
我必须使用java解密此文件,所以我使用下面的代码来执行此操作:
private static final byte[] encryptionKey = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
IvParameterSpec ips = new IvParameterSpec(iv);
Cipher aesCipher = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec aeskeySpec = new SecretKeySpec(encryptionKey, "AES");
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec, ips);
FileInputStream is = new FileInputStream(in);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);
copy(is, os);
os.close();
JAVA代码没有给我任何错误,但输出不正确。
我做错了什么?
我的主要疑问是,如果我使用正确的填充(也尝试了PKCS5Padding但没有成功),如果密钥和iv是正确的(不知道AES_set_encrypt_key真正做什么......)。
**编辑**
我想我对自己的问题有了答案,但我仍然有些疑惑。
CTR表示计数器模式。 AES_ctr128_encrypt函数接收实际计数器(ecount)和使用的块数(num)作为参数。
该文件以16字节为单位加密,如下所示:
for(int i = 0; i < length; i+=16)
{
// .. buffer processing here
init_ctr(&aesstate, iv); //Counter call
AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);
}
函数init_ctr执行此操作:
int init_ctr(struct ctr_state *state, const unsigned char iv[8])
{
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
return 0;
}
这意味着在每次加密/解密之前,C代码都会重置计数器和ivec。
我试图在java中解密整个文件。这可能意味着Java正确使用计数器,但C代码不是因为它在每个块重置计数器。
我的调查是否正确?
我完全没有控制调用openssl的C代码。有没有办法在JAVA中做同样的事情,即在每个16块重置计数器? (API仅请求密钥,算法,模式和IV)
我唯一的另一个选择是通过JNI使用openssl,但我试图避免它......
谢谢!
答案 0 :(得分:4)
我没有尝试过,但你应该能够有效地模拟在C端做的事情 - 分别解密每个16字节(= 128位)的块,并在两次调用之间重置密码。
请注意,使用CTR模式只有一个块,初始化矢量和计数器为零,无法实现CTR模式的目标 - 比ECB 更差。
如果我看到这一点,你可以尝试使用你的C函数(或等效的Java版本)加密一些零块 - 这些应该每次都作为相同的块出现。使用任何密文对此块进行异或,以恢复您的明文。
这相当于128位字母表上的凯撒密码(例如16字节的块),块密码在这里没有为简单的128位XOR密码增加安全性。猜测一个明文块(或者更一般地说,在正确的位置猜测128位,而不是在同一个块中都是必需的)允许获得有效密钥,这允许获得所有剩余的明文块。
答案 1 :(得分:2)
您的加密密钥不同。
C代码使用0
到F
的ASCII字符代码,而Javacode使用实际字节0
到16
。
答案 2 :(得分:0)
C代码存在许多严重问题:
init_ctr()
一次来修复。if (!RAND_bytes(iv, 8)) { /* handle error */ }
。PKCS5_PBKDF2_HMAC_SHA1()
在OpenSSL中实现)从密码生成密钥。