使用 EVP 和 OpenSSL,用 C 编码

Working with EVP and OpenSSL, coding in C

我看到很多关于 OpenSSL 和 EVP 的问题,但没有很多明确的答案,但我想我仍然 post 我的问题在这里,希望得到更好的反馈。

给我的材料是一个签名文件"symmetrickey.bin"、一个RSA密钥集"privatekey_A.pem"、"publickey_A.pem"和另一个用户的public密钥"publickey_B.pem".

我需要做的是:

  1. 取消签名 symmetrickey.bin 并将其存储到文本文件中。
  2. 使用 symmetrickey.txt 和 AES 等算法加密 message.txt
  3. 使用 privatekey_A.pem 对加密消息进行签名并写入文件 cipher.bin.
  4. 之后我需要在 cipher.bin 上取消签名并验证签名。
  5. 然后用我们的对称密钥解密消息,然后写入另一个文件。

我遇到的问题是了解如何实施 OpenSSL EVP 库。 API 页面不是很清楚每个函数的值来自哪里。例如,EVP_OpenInit() 我从哪里得到 ekek "ekl" 的长度? "prvi" 是私钥吗?我怎么知道类型?这些是我没有得到的。

我看过很多实现,但大多数都没有回答我的问题,或者它们提供了疯狂的代码,几乎没有解释发生了什么或值来自何处。我post来这里是不得已的办法...

对于sign/unsign关键部分我需要进一步的信息,这个签名是如何完成的?例如,这个签名是否在文件末尾有 X 字节长度,然后可以轻松删除?

对于列表中的第 2-5 项,以下代码肯定会有所帮助,它基于 openssl 文档中的示例,并根据您的需要进行了更多注释和改编。如果您有任何未评论的问题,请随时提出!

crpytor.c

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <openssl/evp.h>

#define APPNAME "C"

#define CHUNK_SIZE 512
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[CHUNK_SIZE];
    unsigned char outbuf[CHUNK_SIZE + EVP_MAX_BLOCK_LENGTH];
    int inlen;
    int outlen;
    EVP_CIPHER_CTX ctx;
    /* Bogus key and IV: we'd normally set these from
     * another source.
     */
    unsigned char key[] = { 0x13, 0xa3, 0xb4, 0xc1, 0x24, 0x19, 0xf5, 0x23, 0x18, 0xef, 0xca, 0x12, 0x4c, 0x9f, 0x14, 0xfe };
    unsigned char iv[] = { 0x92, 0x1c, 0x23, 0x3f, 0x5e, 0x10, 0x3d, 0x9a };
    /* Don't set key or IV because we will modify the parameters */
    EVP_CIPHER_CTX_init(&ctx);
    /* Using Blowfish encryption with cbc algorithm, you can use whichever is supported in openssl if you wish */
    EVP_CipherInit_ex(&ctx, EVP_bf_cbc(), NULL, NULL, NULL, do_encrypt);
    EVP_CIPHER_CTX_set_key_length(&ctx, 16);
    /* We finished modifying parameters so now we can set key and IV */
    EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
    for(;;)
    {
        inlen = fread(inbuf, 1, CHUNK_SIZE, in);
        if(inlen <= 0) break;
        if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
        {
            /* Error */
            EVP_CIPHER_CTX_cleanup(&ctx);
            return -1;
        }
        fwrite(outbuf, 1, outlen, out);
    }
    if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
    {
        /* Error */
        EVP_CIPHER_CTX_cleanup(&ctx);
        return -1;
    }
    fwrite(outbuf, 1, outlen, out);
    EVP_CIPHER_CTX_cleanup(&ctx);
    rewind(in);
    rewind(out);
    return 0;
}

/* This is the standalone encryptor entry point */
int main(int argc, char** argv)
{
    FILE *encode_file;
    FILE *decode_file;
    int enc_or_dec;
    if (argc < 4)
    {
        printf("Usage: %s [plain file] [encrypted file] [0/1 deccrypt/encrypt]\n", argv[0]);
        return -1;
    }
    encode_file = fopen(argv[1], "r");
    decode_file = fopen(argv[2], "w+");
    /* Stupid decimal translation */
    enc_or_dec = *argv[3]-48;

    do_crypt(encode_file, decode_file, enc_or_dec);
    return 0;
}

和生成文件:

all:
    gcc cryptor.c -o cryptor -g -lcrypto -I ../openssl-1.0.1f-host/include
clean:
    rm cryptor

此代码不使用EVP_OpenInit(),因为它仅用于解密,而我的方法(和您的需求)需要加密或解密。虽然您可以使用 EVP_OpenInit() 来初始化解密上下文,但我将仅适用于解密的单个调用替换为适用于加密和解密的两个调用。

来自手册页:

EVP_OpenInit() initializes a cipher context ctx for decryption with cipher type. It decrypts the encrypted symmetric key of length ekl bytes passed in the ek parameter using the private key priv. The IV is supplied in the iv parameter. EVP_OpenUpdate() and EVP_OpenFinal() have exactly the same properties as the EVP_DecryptUpdate() and EVP_DecryptFinal() routines, as documented on the EVP_EncryptInit(3) manual page.

EVP_OpenInit() 关键文件

如果您引用的签名文件是 RSA/DSA 或类似格式的 public 密钥文件,您可以使用 this Whosebug question 作为比我的更好的方法,因为它确实自动从文件中提取密钥(并根据需要使用 EVP_OpenInit()