“看起来填充有问题,”——实际上问题是你只做了一半的工作。
EVP_Seal/Open
做
混合加密
,也称为信封(或信封)加密。这是
described almost correctly in wikipedia
但我会重新措辞,以匹配OpenSSL的实现和我的偏好。
加密包括三个步骤和
两种不同的
加密:
-
为对称算法生成一个随机数密钥(称为DEK)
-
使用RSA和收件人的公钥加密DEK(您必须已经有了公钥,所以这可能被认为是步骤-1,但不需要对每条消息重复)
-
使用DEK使用对称密码(在您的示例中为AES-256-CBC)加密数据,并根据模式有时随机IV/随机数(CBC使用IV);也取决于模式,这可能会额外产生身份验证标签(CBC没有)
加密步骤1和2可以按任意顺序执行,但我相信这种顺序更常见,而且它肯定是OpenSSL中使用的顺序(
SealInit
做0和1,
SealUpdate
和
SealFinal
一起做2)。
要解密,您需要所有2-4个数据项
由步骤1和2生成:RSA加密的DEK、IV(如果适用)、加密的数据和标签(如果适用的话)。在实际系统中,有一些数据结构将这些数据项打包,再加上额外的元数据:维基百科提到PKCS7(现在被CMS及其变体SMIME取代)和PGP;还有JOSE/JWE、XMLenc等等。解密同样需要
两个步骤
:
-
使用RSA和本地已知私钥解密传输的加密DEK
-
使用相同的对称算法解密传输的加密数据,解密的DEK,如果适用,传输的IV/随机数,以及如果适用,发送的标签
(这次你必须按这个顺序做)。
但是,要解密
使用OpenSSL命令行
您需要将这些项目放在单独的文件中(除了用于身份验证模式的标记,该命令行
enc
根本无法处理)。因此,我修改了您的程序,将加密的DEK、IV和密文写入3个文件,并进行了轻微的重构以减少混乱,更改文件名以避免冲突,并添加了最小的错误处理:
// SO76696165
#include <stdio.h>
#include <stdlib.h>
//#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define BUFFER_SIZE 1024
static int evp_aes_encrypt(FILE *fin, FILE *fek, FILE *fiv, FILE *fenc, EVP_PKEY *pkey)
{
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) return -1;
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char* ek = malloc(EVP_PKEY_get_size(pkey));
int ekl, ivl;
if (EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &ekl, iv, &pkey, 1) != 1)
{ EVP_CIPHER_CTX_free(ctx); free(ek); return -2; }
ivl = 16; // or EVP_CIPHER_CTX_get_iv_length(EVP_aes_256_cbc());
fwrite (ek,ekl,1,fek); fwrite (iv,ivl,1,fiv);
unsigned char in_buffer[BUFFER_SIZE];
unsigned char out_buffer[BUFFER_SIZE + EVP_MAX_IV_LENGTH];
// actually this only needs + cipher_blocksize, but bytes are cheap now
int bytes_read, bytes_written;
while ((bytes_read = fread(in_buffer, 1, BUFFER_SIZE, fin)) > 0)
{
if (EVP_SealUpdate(ctx, out_buffer, &bytes_written, in_buffer, bytes_read) != 1) {
EVP_CIPHER_CTX_free(ctx); free(ek); return -3; }
fwrite(out_buffer, 1, bytes_written, fenc);
}
if (EVP_SealFinal(ctx, out_buffer, &bytes_written) != 1) {
EVP_CIPHER_CTX_free(ctx); free(ek); return -4; }
fwrite(out_buffer, 1, bytes_written, fenc);
EVP_CIPHER_CTX_free(ctx); free(ek);
return 0;
}
int main(void)
{
FILE *pub = fopen("76696165.pub", "rb");
EVP_PKEY *pkey = PEM_read_PUBKEY(pub, NULL, NULL, NULL);
FILE *fin = fopen("76696165.in", "rb");
FILE *fek = fopen("76696165.ek", "wb");
FILE *fiv = fopen("76696165.iv", "wb");
FILE *fenc = fopen("76696165.enc", "wb");
int err = evp_aes_encrypt(fin, fek, fiv, fenc, pkey);
if( err ){ printf ("error in step %d\n", err);
ERR_print_errors_fp(stdout); return 1; }
// fclose not needed when exiting normally
return 0;
}
运行后,我已经:
$ ls -rt1 76696165*
76696165.pub
76696165.prv
76696165.c
76696165.exe
76696165.in
76696165.iv
76696165.enc
76696165.ek
$ # STEP 1
$ openssl rsautl -decrypt -inkey 76696165.prv <76696165.ek >76696165.dek
$ xxd 76696165.dek; xxd 76696165.iv
0000000: a46e 5084 e28b 8f9c 7fe3 a465 fbeb 4ed7 .nP........e..N.
0000010: 7c4b 38f5 3174 f905 3034 52df d992 da3d |K8.1t..04R....=
0000000: 959b a611 3fbb 5486 b6c6 2056 f9ee 6f03 ....?.T... V..o.
$ # STEP 2
$ keyhex=$(xxd -p -c32 <76696165.dek) ivhex=$(xxd -p <76696165.iv)
$ openssl enc -aes-256-cbc -d -K $keyhex -iv $ivhex <76696165.enc
Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten loreâ
While I nodded, nearly napping, suddenly there came a tapping,
As of some one gently rapping, rapping at my chamber door.
"'Tis some visiter," I muttered, "tapping at my chamber doorâ
Only this and nothing more."
-- Edgar Allan Poe