使用 OpenSSL AES 命令行工具和 OpenSSL AES API 时密文不同?

发布于 2025-01-03 09:11:13 字数 3418 浏览 3 评论 0原文

为什么我使用 openssl aes 命令工具和 openssl AES api 时得到不同的密文?

我使用了三种类型的加密:

  • a) openssl 命令行工具
  • b) javax.cryto 中的
  • 类 c) OpenSSL C api。

使用类型(a)和(b),我得到了相同的密文。但使用(c)时我得到了不同的密文。

我想在使用方法c和方法a/b时得到相同的密文。 我认为type c有问题,但我找不到它。请注意,我在上述三种方法中使用了相同的 KEY,IV 对。

键入 a

openssl enc -aes-128-cbc -e -a -in pt.txt -out ct.txt -K 01010101010101010101010101010101 -iv 01010101010101010101010101010101 -p 

键入 b
使用 javax.crypto 的 Java 代码。我不会粘贴代码,因为这样我得到了与 Type a 相同的密文。

类型 c: 使用 OpenSSL API 的 C 代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>

int main(int argc, char** argv) {

    AES_KEY aes;
    unsigned char key[AES_BLOCK_SIZE];        // AES_BLOCK_SIZE = 16
    unsigned char iv[AES_BLOCK_SIZE];        // init vector
    unsigned char* input_string;
    unsigned char* encrypt_string;
    unsigned char* decrypt_string;
    unsigned int len;        // encrypt length (in multiple of AES_BLOCK_SIZE)
    unsigned int i;

    // check usage
    if (argc != 2) {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }

    // set the encryption length
    len = 0;
    if ( strlen(argv[1])>=AES_BLOCK_SIZE || 
         (strlen(argv[1]) + 1) % AES_BLOCK_SIZE == 0) {
        len = strlen(argv[1]) + 1;
    } else {
        len = ((strlen(argv[1]) + 1) / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
    }

    // set the input string
    input_string = (unsigned char*)calloc(len, sizeof(unsigned char));
    if (input_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));

    // Generate AES 128-bit key
    memset(key, 0x01, AES_BLOCK_SIZE);

    // Set encryption key
    memset(iv, 0x01, AES_BLOCK_SIZE);
    if (AES_set_encrypt_key(key, 128, &aes) < 0) {
        fprintf(stderr, "Unable to set encryption key in AES\n");
        exit(-1);
    }

    // alloc encrypt_string
    encrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));    
    if (encrypt_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
        exit(-1);
    }

    // encrypt (iv will change)
    AES_cbc_encrypt(input_string, encrypt_string, len, &aes, iv, AES_ENCRYPT);

    /////////////////////////////////////

    // alloc decrypt_string
    decrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));
    if (decrypt_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
        exit(-1);
    }

    // Set decryption key
    memset(iv, 0x01, AES_BLOCK_SIZE);
    if (AES_set_decrypt_key(key, 128, &aes) < 0) {
        fprintf(stderr, "Unable to set decryption key in AES\n");
        exit(-1);
    }

    // decrypt
    AES_cbc_encrypt(encrypt_string, decrypt_string, len, &aes, iv, 
            AES_DECRYPT);

    // print
    printf("input_string =%s\n", input_string);
    printf("encrypted string =");
    for (i=0; i<len; ++i) {
        printf("%u ", encrypt_string[i]);    
    }
    printf("\n");
    printf("decrypted string =%s\n", decrypt_string);

    return 0;
}

不同输出的原因可能是什么?

Why do i got different ciphertexts when i used openssl aes command tools and openssl AES apis ?

I have used three types of encryption:

  • Type a) openssl command line tool
  • Type b) classes in javax.cryto
  • Type c) OpenSSL C api.

Using type (a) and (b), I got the same ciphertext. But I got different ciphertext when using (c).

I want to get the same ciphertexts when using method c and method a/b.
I think there's something wrong in type c, but I can't find it. Note that I used the same KEY,IV pair in the above three methods.

Type a:

openssl enc -aes-128-cbc -e -a -in pt.txt -out ct.txt -K 01010101010101010101010101010101 -iv 01010101010101010101010101010101 -p 

Type b:
Java code using javax.crypto. I won't paste the code, because this way I got the same ciphertext with Type a.

Type c:
C code using OpenSSL API:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>

int main(int argc, char** argv) {

    AES_KEY aes;
    unsigned char key[AES_BLOCK_SIZE];        // AES_BLOCK_SIZE = 16
    unsigned char iv[AES_BLOCK_SIZE];        // init vector
    unsigned char* input_string;
    unsigned char* encrypt_string;
    unsigned char* decrypt_string;
    unsigned int len;        // encrypt length (in multiple of AES_BLOCK_SIZE)
    unsigned int i;

    // check usage
    if (argc != 2) {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }

    // set the encryption length
    len = 0;
    if ( strlen(argv[1])>=AES_BLOCK_SIZE || 
         (strlen(argv[1]) + 1) % AES_BLOCK_SIZE == 0) {
        len = strlen(argv[1]) + 1;
    } else {
        len = ((strlen(argv[1]) + 1) / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
    }

    // set the input string
    input_string = (unsigned char*)calloc(len, sizeof(unsigned char));
    if (input_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));

    // Generate AES 128-bit key
    memset(key, 0x01, AES_BLOCK_SIZE);

    // Set encryption key
    memset(iv, 0x01, AES_BLOCK_SIZE);
    if (AES_set_encrypt_key(key, 128, &aes) < 0) {
        fprintf(stderr, "Unable to set encryption key in AES\n");
        exit(-1);
    }

    // alloc encrypt_string
    encrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));    
    if (encrypt_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
        exit(-1);
    }

    // encrypt (iv will change)
    AES_cbc_encrypt(input_string, encrypt_string, len, &aes, iv, AES_ENCRYPT);

    /////////////////////////////////////

    // alloc decrypt_string
    decrypt_string = (unsigned char*)calloc(len, sizeof(unsigned char));
    if (decrypt_string == NULL) {
        fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
        exit(-1);
    }

    // Set decryption key
    memset(iv, 0x01, AES_BLOCK_SIZE);
    if (AES_set_decrypt_key(key, 128, &aes) < 0) {
        fprintf(stderr, "Unable to set decryption key in AES\n");
        exit(-1);
    }

    // decrypt
    AES_cbc_encrypt(encrypt_string, decrypt_string, len, &aes, iv, 
            AES_DECRYPT);

    // print
    printf("input_string =%s\n", input_string);
    printf("encrypted string =");
    for (i=0; i<len; ++i) {
        printf("%u ", encrypt_string[i]);    
    }
    printf("\n");
    printf("decrypted string =%s\n", decrypt_string);

    return 0;
}

What could be the reason for different outputs?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷情妓 2025-01-10 09:11:13

在您的 C 代码中,您本质上是在使用零填充:您分配一个由零填充的内存区域(通过 calloc),然后将纯文本复制到此区域,保留末尾的零不变。

openssl enc 使用与 C 代码不同的填充。 openssl enc 的文档说(强调我):

所有分组密码通常使用PKCS#5填充也称为标准
块填充:这允许执行基本的完整性或密码检查。
然而,由于随机数据通过测试的机会优于 256 分之一
这不是一个很好的测试。

此外,openssl enc 命令默认使用盐,这会随机化密文。盐的用途与每消息初始化向量 (IV) 类似。但是您使用的是显式 IV,因此盐不会随机化密文。

javax.crypto.Cipher 的文档 (我想你用过)说:

转换的形式如下:

  • “算法/模式/填充”
  • “算法”

(在后一种情况下,模式和填充的特定于提供商的默认值
使用方案)。例如,以下是有效的转换:

Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");

因此,如果您只是使用 AESARS/CBC 而不指示填充模式,它将使用它认为合适的任何内容,其中您的情况恰好与 OpenSSL 使用的情况相同(即 PKCS#5 填充)。

要更改您的 C 程序,您必须自己进行相同的填充(本质上,它是用 x 字节填充块,所有字节的值都与该数字相同,同时附加填充了 16 个字节的整个块当最后一个块已经满时) - 或使用更高级别的 EVP 函数,这应该为您提供一种指定密码的填充模式的方法。

In your C code, you are essentially using zero-padding: You allocate a memory area filled by zeros (by calloc), and then copy the plain text into this area, leaving the zeros at the end intact.

The openssl enc uses different padding than your C code. The documentation for openssl enc says (emphasis by me):

All the block ciphers normally use PKCS#5 padding also known as standard
block padding: this allows a rudimentary integrity or password check to be performed.
However since the chance of random data passing the test is better than 1 in 256 it
isn't a very good test.

In addition, the openssl enc command uses a salt by default, which randomizes the ciphertext. The salt serves a similar purpose as a per-message Initialization Vector (IV). But you are using an explicit IV, so the salt is not randomizing the ciphertext.

The documentation for javax.crypto.Cipher (which I suppose you used) says:

A transformation is of the form:

  • "algorithm/mode/padding" or
  • "algorithm"

(in the latter case, provider-specific default values for the mode and padding
scheme
are used). For example, the following is a valid transformation:

Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");

So if you simply are using AES or ARS/CBC without indicating the padding mode, it uses whatever it finds fitting, which in your case happened to be the same as what OpenSSL used (i.e. PKCS#5 padding).

To change your C program, you'll have to do the same padding yourself (essentially, it is filling the block with a number x of bytes, all of which have the same value as this number, while appending a whole block filled with 16 when the last block is already full) - or use the higher level EVP-functions, which should provide you with a way to specify the padding mode to the cipher.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文