Java Can';t解密使用openssl AES加密的文件\u ctr128\u加密

Java Can';t解密使用openssl AES加密的文件\u ctr128\u加密,java,openssl,aes,Java,Openssl,Aes,我有一个使用以下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解密此文件,

我有一个使用以下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代码没有给我任何错误,但输出不正确

我做错了什么

我的主要疑问是我是否使用了正确的填充(也尝试了PKCS5P添加,但没有成功),以及密钥和iv是否正确(不知道函数AES\u set\u encrypt\u 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);
}
这意味着在每次加密/解密之前,C代码都会重置计数器和ivec

我试图用java对整个文件进行解密。这可能意味着Java正在正确地使用计数器,但C代码不是这样,因为它正在每个块重置计数器

我的调查正确吗

我完全无法控制调用openssl的C代码。在JAVA中是否有同样的方法,即在16的每个块上重置计数器?(API仅请求密钥、算法、模式和IV)

我唯一的其他选择是通过JNI使用openssl,但我试图避免它


谢谢大家!

您的加密密钥不同


C代码对
0
F
使用ASCII字符代码,而Javacode使用实际字节
0
16
您的加密密钥不同


C代码使用ASCII字符代码表示
0
F
,而Javacode使用实际字节
0
16
,我没有尝试过,但您应该能够有效地模拟C端的操作-分别解密每个16字节(=128位)块,并在两次调用之间重置密码


请注意,仅对一个块使用CTR模式,且初始化向量和计数器为零,这违背了CTR模式的目标-它比更糟糕

如果我看得对,您可以尝试用C函数(或等效的Java版本)加密一些零块——它们每次都应该显示为相同的块。用任何密文对该块进行异或,以恢复明文


这相当于128位字母表(例如16字节块)上的凯撒密码,块密码在这里不为简单的128位异或密码增加安全性。猜测一个纯文本块(或者更一般地说,在正确的位置猜测128位,不需要在同一块中全部猜测)可以获得有效密钥,这可以获得所有剩余的纯文本块。

我没有尝试过,但您应该能够有效地模拟C端的操作-解密每个16字节(=128位)分别分组,并在两次调用之间重置密码


请注意,仅对一个块使用CTR模式,且初始化向量和计数器为零,这违背了CTR模式的目标-它比更糟糕

如果我看得对,你可以尝试用你的C函数(或等效的Java版本)加密一些零块——每次都应该是相同的块。用任何密文对这个块进行异或,以获得明文


这相当于128位字母表(例如16字节块)上的凯撒密码,块密码在这里不会给简单的128位异或密码增加安全性。猜测一块纯文本(或者更一般地说,在正确的位置猜测128位,不必全部在同一块中)允许获取有效密钥,从而获取所有剩余的明文块。

该C代码存在许多严重问题:

  • 如前所述,它正在重新初始化每个块上的计数器。这使得加密完全不安全。在加密第一个块之前,只需调用
    init\u ctr()
    一次即可解决此问题
  • 它将IV静态设置为零。应随机生成新的IV,例如
    if(!RAND_bytes(IV,8)){/*handle error*/}
  • 代码似乎直接使用密码字符串作为密钥。相反,应该使用密钥派生函数(如PBKDF2)从密码生成密钥(在OpenSSL中通过
    PKCS5_PBKDF2_HMAC_SHA1()实现)

该C代码存在许多严重问题:

  • 如前所述,它正在重新初始化每个块上的计数器。这使得加密完全不安全。在加密第一个块之前,只需调用
    init\u ctr()
    一次即可解决此问题
  • 它将IV静态设置为零。应随机生成新的IV,例如
    if(!RAND_bytes(IV,8)){/*handle error*/}
  • 代码似乎直接使用密码字符串作为密钥。相反,应该使用密钥派生函数(如PBKDF2)从密码生成密钥(在OpenSSL中通过
    PKCS5_PBKDF2_HMAC_SHA1()实现)

谢谢,这是我的疑问之一,但移植到java的未签名字符ckey[]=“0123456789ABCDEF”;不应该是新字节[]{0x00、0x01、0x02、0x03、0x04、0x05、0x06、0x07、0x08、0x09、0x0A、0x0B、0x0C、0x0D、0x0E、0x0F}什么应该是正确的移植?字节不是字符。您需要将字符转换为ASCII值。例如,
'0'
0x30
。谢谢,我会尝试一下,看看它是如何进行更改的,键应该是{48、49、50、51、52、53、54、55、56、57、65、66、67、68、69、70}在JAVA中,但文件仍然没有正确解密:(任何其他
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;
}