java c和java之间的AES rijndael加密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10247896/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 00:13:15  来源:igfitidea点击:

AES rijndael encrypt between c and java

javaccryptographyaesmcrypt

提问by dendini

I'm getting crazy to encrypt/decrypt between c and Java but so far the encrypted string in Java and the one in c don't look the same. I've investigated base64 encoding/decoding but after getting crazy to find a library for java and c the respective base64 results looked different! I think it's a problem of converting between Java UTF16 string, saving Byte in java or perhaps AES options (128/256 key, PK5 padding or who knows what), or maybe the UTF8 conversion of the terminal or an absurd combination of the above. So far I get:

我越来越疯狂地在 c 和 Java 之间加密/解密,但到目前为止,Java 中的加密字符串和 c 中的加密字符串看起来不一样。我已经研究过 base64 编码/解码,但是在疯狂地找到 java 和 c 的库之后,各自的 base64 结果看起来不同!我认为这是在 Java UTF16 字符串之间转换、在 Java 中保存 Byte 或 AES 选项(128/256 密钥、PK5 填充或谁知道是什么),或者终端的 UTF8 转换或上述的荒谬组合的问题。到目前为止,我得到:

  user1@comp1:~/Desktop$ gcc AES.c /usr/lib/libmcrypt.a -lssl -lcrypto -lpthread
  user1@comp1:~/Desktop$ /usr/java/jdk1.6.0_25/bin/javac AES.java
  user1@comp1:~/Desktop$ ./a.out 
      Before encryption: test text 123
      After encryption: 49 -60 66 43 -8 66 -106 0 -14 -44 3 47 65 127 -110 117 
      After decryption: test text 123
  user1@comp1:~/Desktop$ java AES 
     Before encryption: test text 123
     After encryption: -110 21 23 59 47 120 70 -93 -54 -93 -12 -70 -91 83 -113 85 
     After decryption: test text 123

I think I really need help here from someone into low level coding, below is the code for Java and c respectively:

我想我真的需要有人帮助进行低级编码,下面分别是 Java 和 c 的代码:

import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
  public static void main(String [] args) {
      try {
  String text = "test text 123";
  /*fixed here now it is 128 bits = 16 Bytes*/
  String encryptionKey = "E072EDF9534053A0";

  System.out.println("Before encryption: " + text);

  byte[] cipher = encrypt(text, encryptionKey);

  System.out.print("After encryption: ");
  for (int i=0; i<cipher.length; i++)
        System.out.print(new Integer(cipher[i])+" ");
  System.out.println("");

  String decrypted = decrypt(cipher, encryptionKey);

  System.out.println("After decryption: " + decrypted);

      } catch (Exception e) {
  e.printStackTrace();
      } 
  }

  public static byte[] encrypt(String plainText, String encryptionKey) throws Exception {
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
      SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
      cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(new byte[cipher.getBlockSize()]));
      return cipher.doFinal(plainText.getBytes("UTF-8"));
  }

  public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
      SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
      cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(new byte[cipher.getBlockSize()]));
      return new String(cipher.doFinal(cipherText),"UTF-8");
  }
  }

and

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int main()
{
MCRYPT td, td2;
const char * plaintext = "test text 123";
int i;
char *key; /* created using mcrypt_gen_key */
char *IV;
char * block_buffer;
int blocksize;
int keysize = 16; /* 128 bits == 16 bytes */
size_t* sizet;

key = calloc(1, keysize);

/*below dirty trick to be sure the entire key has been padded with 
$ ./a.out
==C==
plain:   test text 123
cipher:  16 -124 41 -83 -16 -123 61 -64 -15 -74 87 28 63 30 64 78 
decrypt: test text 123

$java AES
==JAVA==
plain:   test text 123
cipher:  16 -124 41 -83 -16 -123 61 -64 -15 -74 87 28 63 30 64 78 
decrypt: test text 123
's */ strcpy(key, "E072EDF9534053A0"); memset(key, '
... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD 03 03 03 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD 02 02 |
etc...
', sizeof(key)); strcpy(key, "E072EDF9534053A0"); /* MCRYPT mcrypt_module_open( char *algorithm, char* algorithm_directory, char* mode, char* mode_directory); * This function normally returns an encryption descriptor, or MCRYPT_FAILED on error. */ td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL); /*we need two encryption descriptors td and td2 for decryption*/ td2 = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL); blocksize = mcrypt_enc_get_block_size(td); block_buffer = calloc(1, blocksize); /*below to be sure the entire block_buffer has been padded with
... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD 03 03 03 |
... | DD DD DD DD DD DD DD DD | DD DD DD DD DD DD 02 02 |
etc...
's */ memset(block_buffer, '
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
  /*
   * Please realise that the following IV is terrible.
   * (As easy to crack as ROT13...)
   * Real situations should use a randomly generated IV.
   */
  static String IV = "AAAAAAAAAAAAAAAA";
  /* 
   * Note null padding on the end of the plaintext.
   */
  static String plaintext = "test text 123
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * MCrypt API available online:
 * http://linux.die.net/man/3/mcrypt
 */
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int encrypt(
    void* buffer,
    int buffer_len, /* Because the plaintext could include null bytes*/
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mcrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}


int decrypt(
    void* buffer,
    int buffer_len,
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mdecrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

void display(char* ciphertext, int len){
  int v;
  for (v=0; v<len; v++){
    printf("%d ", ciphertext[v]);
  }
  printf("\n");
}

int main()
{
  MCRYPT td, td2;
  char * plaintext = "test text 123";
  char* IV = "AAAAAAAAAAAAAAAA";
  char *key = "0123456789abcdef";
  int keysize = 16; /* 128 bits */
  char* buffer;
  int buffer_len = 16;

  buffer = calloc(1, buffer_len);
  /* 
   * Note that calloc() will null-initialise the memory. (Null padding)
   */

  strncpy(buffer, plaintext, buffer_len);

  printf("==C==\n");
  printf("plain:   %s\n", plaintext);
  encrypt(buffer, buffer_len, IV, key, keysize); 
  printf("cipher:  "); display(buffer , buffer_len);
  decrypt(buffer, buffer_len, IV, key, keysize);
  printf("decrypt: %s\n", buffer);

  return 0;
}
##代码####代码##"; static String encryptionKey = "0123456789abcdef"; public static void main(String [] args) { try { System.out.println("==JAVA=="); System.out.println("plain: " + plaintext); byte[] cipher = encrypt(plaintext, encryptionKey); System.out.print("cipher: "); for (int i=0; i<cipher.length; i++){ System.out.print(new Integer(cipher[i])+" "); } System.out.println(""); String decrypted = decrypt(cipher, encryptionKey); System.out.println("decrypt: " + decrypted); } catch (Exception e) { e.printStackTrace(); } } public static byte[] encrypt(String plainText, String encryptionKey) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE"); SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES"); cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8"))); return cipher.doFinal(plainText.getBytes("UTF-8")); } public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE"); SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES"); cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8"))); return new String(cipher.doFinal(cipherText),"UTF-8"); } }
', blocksize); IV = malloc(mcrypt_enc_get_iv_size(td)); if ((block_buffer == NULL) || (IV == NULL)) { fprintf(stderr, "Failed to allocate memory\n"); exit(EXIT_FAILURE); } for (i = 0; i < mcrypt_enc_get_iv_size(td); i++) { IV[i] = 0; } /*as we can see both td and td2 get same key and IV*/ mcrypt_generic_init(td, key, keysize, IV); mcrypt_generic_init(td2, key, keysize, IV); memset(block_buffer, '##代码##', sizeof(plaintext)); strcpy(block_buffer, plaintext); printf("Before encryption: %s\n", block_buffer); mcrypt_generic (td, block_buffer, blocksize); printf("After encryption: "); for (i=0; i < blocksize; i++) printf("%d ", block_buffer[i]); printf("\n"); mdecrypt_generic (td2, block_buffer, blocksize); printf("After decryption: %s\n", block_buffer); /* deinitialize the encryption thread */ mcrypt_generic_deinit (td); mcrypt_generic_deinit(td2); /* Unload the loaded module */ mcrypt_module_close(td); mcrypt_module_close(td2); return 0; }

回答by brice

Summary

概括

After resolving all the issues, I get:

解决所有问题后,我得到:

##代码##

See below for the code.

请参阅下面的代码。

Issues

问题

  1. Wrong Cipher:AES is Rijndael-128, which is what the Java encryption uses. However, your C code specifies Rijndael-256, which is not AES. From the Mcrypt docs:

    Rijndael [...] AES if used in 128-bit mode

    Remember that when referring to a cipher as CIPHER-XXX, XXX refers to the block size, not the key length. Indeed, Rijndael-128 will accept keys of 128, 192 and 256 bits. AES, however, refers strictly to 128 bit Rijndael. You will find more information at the usual place.

  2. Incorrect memory initialisation:You do not initialise your memory properly in C, which leaves the bytes between the end of your message and the block limit undefined.

  3. Incorrect Padding:You specify PKCS5Paddingin your java code, but you do not pad your plaintext appropriately in C. PKCS5 is actually trivially simple, and is described quite well on the wiki. To pad with PKCS#5, simply ensure that every padding byte is equal to the total length padded:

    ##代码##

    Where DD are data bytes. There are other padding methods, that are explained elsewhere.

  4. Incorrect key handling:You extract the key for your Java program from an hexadecimal encoded string, whereas in your C program, you take the hexadecimal string directly as your key. You must handle your keys consistently for both programs to give the same output.

  5. Different Initialization Vectors on either side:You will need the same Initialization Vector on both sides. Randomly generating your IV is correct. The IV should then be passed along with the cipher-text for decryption on the other side. IVs should not be reused.

  1. 错误的密码:AES 是Rijndael-128,这是 Java 加密使用的。但是,您的 C 代码指定Rijndael-256,它不是 AES。从Mcrypt 文档

    Rijndael [...] AES 如果在 128 位模式下使用

    请记住,当将密码称为 CIPHER-XXX 时,XXX 指的是块大小,而不是密钥长度。实际上,Rijndael-128 将接受 128、192 和 256 位的密钥。但是,AES 严格指的是 128 位 Rijndael。您会在通常的地方找到更多信息。

  2. 不正确的内存初始化:您没有在 C 中正确初始化您的内存,这会导致消息末尾和块限制之间的字节未定义。

  3. 不正确的填充:PKCS5Padding在 Java 代码中指定,但您没有在 C 中适当地填充纯文本。 PKCS5 实际上非常简单,并且在 wiki上有很好的描述。要填充 PKCS#5,只需确保每个填充字节等于填充的总长度:

    ##代码##

    其中 DD 是数据字节。还有其他填充方法,在别处解释

  4. 不正确的密钥处理:您从十六进制编码的字符串中提取 Java 程序的密钥,而在 C 程序中,您直接将十六进制字符串作为密钥。您必须一致地为两个程序处理您的密钥以提供相同的输出。

  5. 两边不同的初始化向量:两边都需要相同的初始化向量。随机生成您的 IV 是正确的。然后,IV 应与密文一起传递,以便在另一侧进行解密。IV 不应重复使用。

Example programs

示例程序

The following are available on github

以下内容可在github 上找到

##代码##

And the C file:

和 C 文件:

##代码##

Tested on Linux with the latest Libmcrypt and Java 1.7. Watch out, since I wrote the C in a rush, and it is full of memory leaks and overflow problems. (Exercise left to the reader to clean it up, as they say...)

在 Linux 上使用最新的 Libmcrypt 和 Java 1.7 进行了测试。小心,因为我匆忙写了C,而且它充满了内存泄漏和溢出问题。(练习留给读者清理它,正如他们所说......)

回答by Cameron Skinner

Further to @brice's flags, I don't see where you initialize the IV in your Java code. You create an `IvParameterSpec', but you pass in an all-zero byte array.

除了@brice 的标志之外,我没有看到您在 Java 代码中初始化 IV 的位置。您创建了一个“IvParameterSpec”,但您传入了一个全零字节数组。

Your C code generates a random IV so it should produce a different ciphertext each time it is run.

您的 C 代码会生成一个随机 IV,因此每次运行时它都应该生成不同的密文。

Try using a fixed IV for both implementations and see if that gives you consistent results. Of course you'll need to generate a random IV again when you want to do real encryption, but using a fixed IV may help you with debugging.

尝试对两种实现使用固定的 IV,看看是否会给您一致的结果。当然,当您想要进行真正的加密时,您需要再次生成随机 IV,但使用固定 IV 可能会帮助您进行调试。

I'd also make sure that both implementations are using the same padding (by explicitly setting the padding rather than letting mcrypt choose), and instead of writing the raw ciphertext to the console I strongly recommend writing the hex values, or just write each byte as a number - it will be significantly easier to debug when you don't have to worry about unprintable characters. This is only for debugging, so it doesn't matter if it's not efficient or pleasant to look at.

我还要确保两个实现都使用相同的填充(通过显式设置填充而不是让 mcrypt 选择),而不是将原始密文写入控制台,我强烈建议写入十六进制值,或者只写入每个字节作为数字 - 当您不必担心不可打印的字符时,调试会容易得多。这仅用于调试,因此它是否有效或不愉快都没有关系。