你能帮助我使用rsa.h在c ++中使用openssl公钥加密吗? [英] Can you help me get my head around openssl public key encryption with rsa.h in c++?

查看:143
本文介绍了你能帮助我使用rsa.h在c ++中使用openssl公钥加密吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过在C ++中使用openssa实现rsa来了解公钥加密。你能帮我吗?


  1. Alice通过网络连接到Bob

  2. Alice和Bob想要安全通讯

  3. Alice会产生公开/私人金钥对,并传送公开金钥给Bob

  4. 并用公钥对随机生成的对称密钥(例如blowfish)进行加密并将结果发送给Alice。

  5. Alice使用原始生成的私钥对密文进行解密, / li>
  6. Alice和Bob现在都掌握了对称的河豚鱼的知识,并且可以建立安全的沟通渠道。

现在,我看了openssl / rsa.h rsa实现(因为我已经有openssl / blowfish.h的实践经验),我看到这两个函数:

  int RSA_public_encrypt(int flen,unsigned char * from,
unsigned char * to,RSA * rsa,int padding);
int RSA_private_decrypt(int flen,unsigned char * from,
unsigned char * to,RSA * rsa,int padding);

如果Alice要生成* rsa,那么如何生成rsa密钥对?有什么像rsa_public和rsa_private是从rsa衍生的? * rsa是否包含公钥和私钥,上述函数根据是否需要公钥或私钥自动删除必要的密钥?应该生成两个唯一的* rsa指针,这样我们有以下的结果:

  int RSA_public_encrypt(int flen,unsigned char * from,
unsigned char * to,RSA * rsa_public,int padding);
int RSA_private_decrypt(int flen,unsigned char * from,
unsigned char * to,RSA * rsa_private,int padding);

其次,* rsa公钥应以什么格式发送给Bob?是否必须重新解释到一个字符数组,然后发送标准的方式?我听说过有关证书的事情 - 是否与它有什么关系?



对于所有的问题,对不起,
最好的祝福,
Ben 。



$ b

编辑:Coe我目前正在招聘:

  / * 
* theEncryptor.cpp
*
*
*由14/01/2010创建。
*版权所有2010 __MyCompanyName__。版权所有。
*
* /

#includetheEncryptor.h
#include< iostream>
#include< sys / socket.h>
#include< sstream>

theEncryptor :: theEncryptor()
{

}

void
theEncryptor :: blowfish(unsigned char * data ,int data_len,unsigned char * key,int enc)
{

//首先hash密钥!
unsigned char obuf [20];
bzero(obuf,20);
SHA1((const unsigned char *)key,64,obuf);

BF_KEY bfkey;
int keySize = 16; // strlen((char *)key);
BF_set_key(& bfkey,keySize,obuf);

unsigned char ivec [16];
memset(ivec,0,16);

unsigned char * out =(unsigned char *)malloc(data_len);
bzero(out,data_len);
int num = 0;
BF_cfb64_encrypt(data,out,data_len,& bfkey,ivec,& num,enc);

// for(int i = 0; i
memcpy(data,out,data_len);
free(out);

}

void
theEncryptor :: generateRSAKeyPair(int bits)
{
rsa = RSA_generate_key(bits,65537,NULL,NULL );
}


int
theEncryptor :: publicEncrypt(unsigned char * data,unsigned char * dataEncrypted,int dataLen)
{
return RSA_public_encrypt(dataLen,data,dataEncrypted,rsa,RSA_PKCS1_OAEP_PADDING);
}

int
theEncryptor :: privateDecrypt(unsigned char * dataEncrypted,
unsigned char * dataDecrypted)
{
return RSA_private_decrypt(RSA_size (rsa),dataEncrypted,
dataDecrypted,rsa,RSA_PKCS1_OAEP_PADDING);
}

void
theEncryptor :: receivePublicKeyAndSetRSA(int sock,int bits)
{
int max_hex_size =(bits / 4)
char keybufA [max_hex_size];
bzero(keybufA,max_hex_size);
char keybufB [max_hex_size];
bzero(keybufB,max_hex_size);
int n = recv(sock,keybufA,max_hex_size,0);
n = send(sock,OK,2,0);
n = recv(sock,keybufB,max_hex_size,0);
n = send(sock,Ok,2,0);
rsa = RSA_new();
BN_hex2bn(& rsa-> n,keybufA);
BN_hex2bn(& rsa-> e,keybufB);
}

void
theEncryptor :: transmitPublicKey(int sock,int bits)
{
const int max_hex_size =(bits / 4)+ 1;
long size = max_hex_size;
char keyBufferA [size];
char keyBufferB [size];
bzero(keyBufferA,size);
bzero(keyBufferB,size);
sprintf(keyBufferA,%s\r\\\
,BN_bn2hex(rsa-> n));
sprintf(keyBufferB,%s\r\\\
,BN_bn2hex(rsa-> e));
int n = send(sock,keyBufferA,size,0);
char recBuf [2];
n = recv(sock,recBuf,2,0);
n = send(sock,keyBufferB,size,0);
n = recv(sock,recBuf,2,0);
}

void
theEncryptor :: generateRandomBlowfishKey(unsigned char * key,int bytes)
{
/ *
srand )time(NULL));
std :: ostringstream stm;
for(int i = 0; i int randomValue = 65 + rand()%26;
stm<< (char)((int)randomValue);
}
std :: string str(stm.str());
const char * strs = str.c_str();
for(int i = 0; bytes; i ++)key [i] = strs [i];
* /

int n = RAND_bytes(key,bytes);

if(n == 0)std :: cout<<警告密钥生成时使用的熵不好,你不应该考虑通信是安全的<< std :: endl;

}

theEncryptor ::〜theEncryptor(){}


openssl / evp.h 中更高级别的Envelope Encryption函数,而不是比低层RSA功能直接。



在这种情况下,你可以使用 EVP_SealInit() EVP_SealUpdate() EVP_SealFinal()相应的解密函数为 EVP_OpenInit() EVP_OpenUpdate() EVP_OpenFinal code>。我建议使用 EVP_aes_128_cbc()作为密码类型参数的值。



公钥加载到 RSA * 句柄中,则使用 EVP_PKEY_assign_RSA()将其放入 EVP_PKEY * 用于EVP功能的句柄。



一旦你有了这个,去解决我在评论中提到的身份验证问题,您需要建立一个可信的权威机构(Trent)。 Trent的公钥是所有用户都知道的(与应用程序或类似的分布 - 只是从PEM文件加载它)。不是交换裸RSA参数,Alice和Bob交换包含其RSA公钥及其名称的x509证书,并由Trent签名。然后,Alice和Bob在继续协议之前,分别验证他们从另一个接收的证书(使用他们已经知道的Trent的公钥),包括检查关联的名称是否正确。 OpenSSL包括在 x509.h 头中加载和验证证书的功能。






这里是一个如何使用 EVP_Seal *()加密给定接收者公钥的文件的示例。它需要PEM RSA公钥文件(即由 openssl rsa -pubout 生成)作为命令行参数,从stdin读取源数据并将加密数据写入stdout 。要解密,请使用 EVP_Open *()而不是 PEM_read_RSAPrivateKey()来读取私钥而不是公钥。



这不是真的那么困难 - 当然不会产生错误,而不是自己生成填充,IV等等(Seal函数同时执行RSA和AES部分交易)。无论如何,代码:

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

#include< openssl / evp.h>
#include< openssl / pem.h>
#include< openssl / rsa.h>
#include< openssl / err.h>

#include< arpa / inet.h> / *对于htonl()* /

int do_evp_seal(FILE * rsa_pkey_file,FILE * in_file,FILE * out_file)
{
int retval = 0;
RSA * rsa_pkey = NULL;
EVP_PKEY * pkey = EVP_PKEY_new();
EVP_CIPHER_CTX ctx;
unsigned char buffer [4096];
unsigned char buffer_out [4096 + EVP_MAX_IV_LENGTH];
size_t len;
int len_out;
unsigned char * ek;
int eklen;
uint32_t eklen_n
unsigned char iv [EVP_MAX_IV_LENGTH];

if(!PEM_read_RSA_PUBKEY(rsa_pkey_file,& rsa_pkey,NULL,NULL))
{
fprintf(stderr,加载RSA公钥文件时出错\ n ;
ERR_print_errors_fp(stderr);
retval = 2;
goto out;
}

if(!EVP_PKEY_assign_RSA(pkey,rsa_pkey))
{
fprintf(stderr,EVP_PKEY_assign_RSA:failed.\\\
);
retval = 3;
goto out;
}

EVP_CIPHER_CTX_init(& ctx);
ek = malloc(EVP_PKEY_size(pkey));

if(!EVP_SealInit(& ctx,EVP_aes_128_cbc(),& ek,& eklen,iv,& pkey,1))
{
fprintf(stderr ,EVP_SealInit:failed.\\\
);
retval = 3;
goto out_free;
}

/ *首先我们写出加密的密钥长度,然后加密密钥,
*然后是iv(IV长度由我们选择的密码固定) 。
* /

eklen_n = htonl(eklen);
if(fwrite(& eklen_n,sizeof eklen_n,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}
if(fwrite(ek,eklen,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}
if(fwrite(iv,EVP_CIPHER_iv_length(EVP_aes_128_cbc()),1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}

/ *现在我们处理输入文件并将加密数据写入
*输出文件。 * /

while((len = fread(buffer,1,sizeof buffer,in_file))> 0)
{
if(!EVP_SealUpdate(& ctx,buffer_out ,& len_out,buffer,len))
{
fprintf(stderr,EVP_SealUpdate:failed.\\\
);
retval = 3;
goto out_free;
}

if(fwrite(buffer_out,len_out,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}
}

if(ferror(in_file))
{
perror(input file);
retval = 4;
goto out_free;
}

if(!EVP_SealFinal(& ctx,buffer_out,& len_out))
{
fprintf(stderr,EVP_SealFinal:failed.\\\
);
retval = 3;
goto out_free;
}

if(fwrite(buffer_out,len_out,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}

out_free:
EVP_PKEY_free(pkey);
free(ek);

out:
return retval;
}

int main(int argc,char * argv [])
{
FILE * rsa_pkey_file;
int rv;

if(argc <2)
{
fprintf(stderr,Usage:%s< PEM RSA Public Key File> \\\
,argv [0] );
exit(1);
}

rsa_pkey_file = fopen(argv [1],rb);
if(!rsa_pkey_file)
{
perror(argv [1]);
fprintf(stderr,加载PEM RSA公钥文件错误。\\\
);
exit(2);
}

rv = do_evp_seal(rsa_pkey_file,stdin,stdout);

fclose(rsa_pkey_file);
return rv;
}






发布了很好的为什么你应该使用更高级的函数 - 你陷入了几个陷阱:




  • rand()是强调的一个加密强的随机数生成器!使用 rand()生成对称密钥足以使整个系统完全不安全。 ( EVP _ *()函数自己生成必要的随机数,使用加密的RNG,从合适的熵源播种)。


  • 您正在将CFB模式的IV设置为固定值(零)。这消除了首先使用CFB模式的任何优点(允许攻击者轻易地执行块替换攻击和更糟)。 ( EVP _ *()函数在需要时为您生成适当的IV。)


  • 如果您要定义新协议,而不是与现有协议互操作,则应使用code> RSA_PKCS1_OAEP_PADDING







相应的解密代码,用于后代:

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

#include< openssl / evp.h>
#include< openssl / pem.h>
#include< openssl / rsa.h>
#include< openssl / err.h>

#include< arpa / inet.h> / *对于htonl()* /

int do_evp_unseal(FILE * rsa_pkey_file,FILE * in_file,FILE * out_file)
{
int retval = 0;
RSA * rsa_pkey = NULL;
EVP_PKEY * pkey = EVP_PKEY_new();
EVP_CIPHER_CTX ctx;
unsigned char buffer [4096];
unsigned char buffer_out [4096 + EVP_MAX_IV_LENGTH];
size_t len;
int len_out;
unsigned char * ek;
unsigned int eklen;
uint32_t eklen_n;
unsigned char iv [EVP_MAX_IV_LENGTH];

if(!PEM_read_RSAPrivateKey(rsa_pkey_file,& rsa_pkey,NULL,NULL))
{
fprintf(stderr,加载RSA私钥文件时出错\ n ;
ERR_print_errors_fp(stderr);
retval = 2;
goto out;
}

if(!EVP_PKEY_assign_RSA(pkey,rsa_pkey))
{
fprintf(stderr,EVP_PKEY_assign_RSA:failed.\\\
);
retval = 3;
goto out;
}

EVP_CIPHER_CTX_init(& ctx);
ek = malloc(EVP_PKEY_size(pkey));

/ *首先需要获取加密的密钥长度,加密密钥和IV * /

if(fread(& eklen_n,sizeof eklen_n,1,in_file)!= 1)
{
perror(input file);
retval = 4;
goto out_free;
}
eklen = ntohl(eklen_n);
if(eklen> EVP_PKEY_size(pkey))
{
fprintf(stderr,bad encrypted key length(%u>%d)\\\
,eklen,
EVP_PKEY_size(pkey));
retval = 4;
goto out_free;
}
if(fread(ek,eklen,1,in_file)!= 1)
{
perror(input file);
retval = 4;
goto out_free;
}
if(fread(iv,EVP_CIPHER_iv_length(EVP_aes_128_cbc()),1,in_file)!= 1)
{
perror(input file);
retval = 4;
goto out_free;
}

if(!EVP_OpenInit(& ctx,EVP_aes_128_cbc(),ek,eklen,iv,pkey))
{
fprintf(stderr,EVP_OpenInit :failed.\\\
);
retval = 3;
goto out_free;
}

while((len = fread(buffer,1,sizeof buffer,in_file))> 0)
{
if(!EVP_OpenUpdate(& ctx,buffer_out,& len_out,buffer,len))
{
fprintf(stderr,EVP_OpenUpdate:failed.\\\
);
retval = 3;
goto out_free;
}

if(fwrite(buffer_out,len_out,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}
}

if(ferror(in_file))
{
perror(input file);
retval = 4;
goto out_free;
}

if(!EVP_OpenFinal(& ctx,buffer_out,& len_out))
{
fprintf(stderr,EVP_OpenFinal:failed.\\\
);
retval = 3;
goto out_free;
}

if(fwrite(buffer_out,len_out,1,out_file)!= 1)
{
perror(output file);
retval = 5;
goto out_free;
}

out_free:
EVP_PKEY_free(pkey);
free(ek);

out:
return retval;
}

int main(int argc,char * argv [])
{
FILE * rsa_pkey_file;
int rv;

if(argc <2)
{
fprintf(stderr,Usage:%s< PEM RSA Private Key File> \\\
,argv [0] );
exit(1);
}

rsa_pkey_file = fopen(argv [1],rb);
if(!rsa_pkey_file)
{
perror(argv [1]);
fprintf(stderr,加载PEM RSA私钥文件时出错。\\\
);
exit(2);
}

rv = do_evp_unseal(rsa_pkey_file,stdin,stdout);

fclose(rsa_pkey_file);
return rv;
}


I am trying to get my head around public key encryption using the openssl implementation of rsa in C++. Can you help? So far these are my thoughts (please do correct if necessary)

  1. Alice is connected to Bob over a network
  2. Alice and Bob want secure communications
  3. Alice generates a public / private key pair and sends public key to Bob
  4. Bob receives public key and encrypts a randomly generated symmetric cypher key (e.g. blowfish) with the public key and sends the result to Alice
  5. Alice decrypts the ciphertext with the originally generated private key and obtains the symmetric blowfish key
  6. Alice and Bob now both have knowledge of symmetric blowfish key and can establish a secure communication channel

Now, I have looked at the openssl/rsa.h rsa implementation (since I already have practical experience with openssl/blowfish.h), and I see these two functions:

int RSA_public_encrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, unsigned char *from,
 unsigned char *to, RSA *rsa, int padding);

If Alice is to generate *rsa, how does this yield the rsa key pair? Is there something like rsa_public and rsa_private which are derived from rsa? Does *rsa contain both public and private key and the above function automatically strips out the necessary key depending on whether it requires the public or private part? Should two unique *rsa pointers be generated so that actually, we have the following:

int RSA_public_encrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa_public, int padding);
int RSA_private_decrypt(int flen, unsigned char *from,
 unsigned char *to, RSA *rsa_private, int padding);

Secondly, in what format should the *rsa public key be sent to Bob? Must it be reinterpreted in to a character array and then sent the standard way? I've heard something about certificates -- are they anything to do with it?

Sorry for all the questions, Best Wishes, Ben.

EDIT: Coe I am currently employing:

/*
 *  theEncryptor.cpp
 *  
 *
 *  Created by ben on 14/01/2010.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include "theEncryptor.h"
#include <iostream>
#include <sys/socket.h>
#include <sstream>

theEncryptor::theEncryptor()
{

}

void
theEncryptor::blowfish(unsigned char *data, int data_len, unsigned char* key, int enc)
{

    //  hash the key first! 
    unsigned char obuf[20];
    bzero(obuf,20);
    SHA1((const unsigned char*)key, 64, obuf);

    BF_KEY bfkey;
    int keySize = 16;//strlen((char*)key);
    BF_set_key(&bfkey, keySize, obuf);

    unsigned char ivec[16];
    memset(ivec, 0, 16);

    unsigned char* out=(unsigned char*) malloc(data_len);
    bzero(out,data_len);
    int num = 0;
    BF_cfb64_encrypt(data, out, data_len, &bfkey, ivec, &num, enc);

    //for(int i = 0;i<data_len;i++)data[i]=out[i];

    memcpy(data, out, data_len);
    free(out);  

}

void
theEncryptor::generateRSAKeyPair(int bits)
{
    rsa = RSA_generate_key(bits, 65537, NULL, NULL);
}


int
theEncryptor::publicEncrypt(unsigned char* data, unsigned char* dataEncrypted,int dataLen)
{   
    return RSA_public_encrypt(dataLen, data, dataEncrypted, rsa, RSA_PKCS1_OAEP_PADDING);   
}

int
theEncryptor::privateDecrypt(unsigned char* dataEncrypted,
                             unsigned char* dataDecrypted)
{
    return RSA_private_decrypt(RSA_size(rsa), dataEncrypted, 
                                   dataDecrypted, rsa, RSA_PKCS1_OAEP_PADDING);
}

void 
theEncryptor::receivePublicKeyAndSetRSA(int sock, int bits)
{
    int max_hex_size = (bits / 4) + 1;
    char keybufA[max_hex_size];
    bzero(keybufA,max_hex_size);
    char keybufB[max_hex_size];
    bzero(keybufB,max_hex_size);
    int n = recv(sock,keybufA,max_hex_size,0); 
    n = send(sock,"OK",2,0);
    n = recv(sock,keybufB,max_hex_size,0); 
    n = send(sock,"OK",2,0); 
    rsa = RSA_new();
    BN_hex2bn(&rsa->n, keybufA);
    BN_hex2bn(&rsa->e, keybufB);
}

void 
theEncryptor::transmitPublicKey(int sock, int bits)
{
    const int max_hex_size = (bits / 4) + 1;
    long size = max_hex_size;
    char keyBufferA[size];
    char keyBufferB[size];
    bzero(keyBufferA,size);
    bzero(keyBufferB,size);
    sprintf(keyBufferA,"%s\r\n",BN_bn2hex(rsa->n));
    sprintf(keyBufferB,"%s\r\n",BN_bn2hex(rsa->e));
    int n = send(sock,keyBufferA,size,0);
    char recBuf[2];
    n = recv(sock,recBuf,2,0);
    n = send(sock,keyBufferB,size,0);
    n = recv(sock,recBuf,2,0);
}

void
theEncryptor::generateRandomBlowfishKey(unsigned char* key, int bytes)
{
            /*
    srand( (unsigned)time( NULL ) );
    std::ostringstream stm;
    for(int i = 0;i<bytes;i++){
        int randomValue = 65 + rand()% 26;
        stm << (char)((int)randomValue);
    }
    std::string str(stm.str());
    const char* strs = str.c_str();
    for(int i = 0;bytes;i++)key[i]=strs[i];
            */

    int n = RAND_bytes(key, bytes);

    if(n==0)std::cout<<"Warning key was generated with bad entropy. You should not consider communication to be secure"<<std::endl;

}

theEncryptor::~theEncryptor(){}

解决方案

You should actually be using the higher-level "Envelope Encryption" functions from openssl/evp.h, rather than the low-level RSA functions directly. These do most of the work for you and mean you don't have to reinvent the wheel.

In this case, you'd use the EVP_SealInit(), EVP_SealUpdate() and EVP_SealFinal() functions. The corresponding decryption functions are EVP_OpenInit(), EVP_OpenUpdate() and EVP_OpenFinal(). I would suggest using EVP_aes_128_cbc() as the value of the cipher type parameter.

Once you've got the public key loaded into an RSA * handle, you use EVP_PKEY_assign_RSA() to put it into an EVP_PKEY * handle for the EVP functions.

Once you've got this going, to solve the authentication problem I mentioned in my comment, you'll need to established a trusted authority ("Trent"). Trent's public key is known to all users (distributed with the application or similar - just load it from a PEM file). Instead of exchanging bare RSA parameters, Alice and Bob exchange x509 certificates that contain their RSA public keys together with their name, and are signed by Trent. Alice and Bob then each verify the certificate they recieved from the other (using Trent's public key, which they already know), including checking that the associated name is the right one, before continuing the protocol. OpenSSL includes functions for loading and verifying certificates in the x509.h header.


Here's an example of how to use EVP_Seal*() to encrypt a file given the recipient's public key. It takes the PEM RSA Public Key file (ie as generated by openssl rsa -pubout) as a command line argument, reads the source data from stdin and writes the encrypted data to stdout. To decrypt, use EVP_Open*() instead, and PEM_read_RSAPrivateKey() to read a private key rather than public key.

It's not really that hard - and certainly less error prone than messing about generating padding, IVs and so on yourself (the Seal function does both the RSA and AES parts of the deal). Anyway, the code:

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

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>

#include <arpa/inet.h> /* For htonl() */

int do_evp_seal(FILE *rsa_pkey_file, FILE *in_file, FILE *out_file)
{
    int retval = 0;
    RSA *rsa_pkey = NULL;
    EVP_PKEY *pkey = EVP_PKEY_new();
    EVP_CIPHER_CTX ctx;
    unsigned char buffer[4096];
    unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH];
    size_t len;
    int len_out;
    unsigned char *ek;
    int eklen;
    uint32_t eklen_n;
    unsigned char iv[EVP_MAX_IV_LENGTH];

    if (!PEM_read_RSA_PUBKEY(rsa_pkey_file, &rsa_pkey, NULL, NULL))
    {
        fprintf(stderr, "Error loading RSA Public Key File.\n");
        ERR_print_errors_fp(stderr);
        retval = 2;
        goto out;
    }

    if (!EVP_PKEY_assign_RSA(pkey, rsa_pkey))
    {
        fprintf(stderr, "EVP_PKEY_assign_RSA: failed.\n");
        retval = 3;
        goto out;
    }

    EVP_CIPHER_CTX_init(&ctx);
    ek = malloc(EVP_PKEY_size(pkey));

    if (!EVP_SealInit(&ctx, EVP_aes_128_cbc(), &ek, &eklen, iv, &pkey, 1))
    {
        fprintf(stderr, "EVP_SealInit: failed.\n");
        retval = 3;
        goto out_free;
    }

    /* First we write out the encrypted key length, then the encrypted key,
     * then the iv (the IV length is fixed by the cipher we have chosen).
     */

    eklen_n = htonl(eklen);
    if (fwrite(&eklen_n, sizeof eklen_n, 1, out_file) != 1)
    {
        perror("output file");
        retval = 5;
        goto out_free;
    }
    if (fwrite(ek, eklen, 1, out_file) != 1)
    {
        perror("output file");
        retval = 5;
        goto out_free;
    }
    if (fwrite(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, out_file) != 1)
    {
        perror("output file");
        retval = 5;
        goto out_free;
    }

    /* Now we process the input file and write the encrypted data to the
     * output file. */

    while ((len = fread(buffer, 1, sizeof buffer, in_file)) > 0)
    {
        if (!EVP_SealUpdate(&ctx, buffer_out, &len_out, buffer, len))
        {
            fprintf(stderr, "EVP_SealUpdate: failed.\n");
            retval = 3;
            goto out_free;
        }

        if (fwrite(buffer_out, len_out, 1, out_file) != 1)
        {
            perror("output file");
            retval = 5;
            goto out_free;
        }
    }

    if (ferror(in_file))
    {
        perror("input file");
        retval = 4;
        goto out_free;
    }

    if (!EVP_SealFinal(&ctx, buffer_out, &len_out))
    {
        fprintf(stderr, "EVP_SealFinal: failed.\n");
        retval = 3;
        goto out_free;
    }

    if (fwrite(buffer_out, len_out, 1, out_file) != 1)
    {
        perror("output file");
        retval = 5;
        goto out_free;
    }

    out_free:
    EVP_PKEY_free(pkey);
    free(ek);

    out:
    return retval;
}

int main(int argc, char *argv[])
{
    FILE *rsa_pkey_file;
    int rv;

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s <PEM RSA Public Key File>\n", argv[0]);
        exit(1);
    }

    rsa_pkey_file = fopen(argv[1], "rb");
    if (!rsa_pkey_file)
    {
        perror(argv[1]);
        fprintf(stderr, "Error loading PEM RSA Public Key File.\n");
        exit(2);
    }

    rv = do_evp_seal(rsa_pkey_file, stdin, stdout);

    fclose(rsa_pkey_file);
    return rv;
}


The code you've posted illustrates nicely why you should use the higher-level functions - you've fallen into a couple of pitfalls:

  • rand() is emphatically not a cryptographically strong random number generator! Generating your symmetric key using rand() is enough to make the entire system completely insecure. (The EVP_*() functions generate the necessary random numbers themselves, using a cryptographically strong RNG, seeded from an appropriate entropy source).

  • You are setting the IV for CFB mode to a fixed value (zero). This negates any advantage of using CFB mode in the first place (allowing attackers to trivially perform block-replacement attacks and worse). (The EVP_*() functions generate an appropriate IV for you, when required).

  • RSA_PKCS1_OAEP_PADDING should be used if you're defining a new protocol, rather than interoperating with an existing protocol.


The corresponding decryption code, for posterity:

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

#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>

#include <arpa/inet.h> /* For htonl() */

int do_evp_unseal(FILE *rsa_pkey_file, FILE *in_file, FILE *out_file)
{
    int retval = 0;
    RSA *rsa_pkey = NULL;
    EVP_PKEY *pkey = EVP_PKEY_new();
    EVP_CIPHER_CTX ctx;
    unsigned char buffer[4096];
    unsigned char buffer_out[4096 + EVP_MAX_IV_LENGTH];
    size_t len;
    int len_out;
    unsigned char *ek;
    unsigned int eklen;
    uint32_t eklen_n;
    unsigned char iv[EVP_MAX_IV_LENGTH];

    if (!PEM_read_RSAPrivateKey(rsa_pkey_file, &rsa_pkey, NULL, NULL))
    {
        fprintf(stderr, "Error loading RSA Private Key File.\n");
        ERR_print_errors_fp(stderr);
        retval = 2;
        goto out;
    }

    if (!EVP_PKEY_assign_RSA(pkey, rsa_pkey))
    {
        fprintf(stderr, "EVP_PKEY_assign_RSA: failed.\n");
        retval = 3;
        goto out;
    }

    EVP_CIPHER_CTX_init(&ctx);
    ek = malloc(EVP_PKEY_size(pkey));

    /* First need to fetch the encrypted key length, encrypted key and IV */

    if (fread(&eklen_n, sizeof eklen_n, 1, in_file) != 1)
    {
        perror("input file");
        retval = 4;
        goto out_free;
    }
    eklen = ntohl(eklen_n);
    if (eklen > EVP_PKEY_size(pkey))
    {
        fprintf(stderr, "Bad encrypted key length (%u > %d)\n", eklen,
            EVP_PKEY_size(pkey));
        retval = 4;
        goto out_free;
    }
    if (fread(ek, eklen, 1, in_file) != 1)
    {
        perror("input file");
        retval = 4;
        goto out_free;
    }
    if (fread(iv, EVP_CIPHER_iv_length(EVP_aes_128_cbc()), 1, in_file) != 1)
    {
        perror("input file");
        retval = 4;
        goto out_free;
    }

    if (!EVP_OpenInit(&ctx, EVP_aes_128_cbc(), ek, eklen, iv, pkey))
    {
        fprintf(stderr, "EVP_OpenInit: failed.\n");
        retval = 3;
        goto out_free;
    }

    while ((len = fread(buffer, 1, sizeof buffer, in_file)) > 0)
    {
        if (!EVP_OpenUpdate(&ctx, buffer_out, &len_out, buffer, len))
        {
            fprintf(stderr, "EVP_OpenUpdate: failed.\n");
            retval = 3;
            goto out_free;
        }

        if (fwrite(buffer_out, len_out, 1, out_file) != 1)
        {
            perror("output file");
            retval = 5;
            goto out_free;
        }
    }

    if (ferror(in_file))
    {
        perror("input file");
        retval = 4;
        goto out_free;
    }

    if (!EVP_OpenFinal(&ctx, buffer_out, &len_out))
    {
        fprintf(stderr, "EVP_OpenFinal: failed.\n");
        retval = 3;
        goto out_free;
    }

    if (fwrite(buffer_out, len_out, 1, out_file) != 1)
    {
        perror("output file");
        retval = 5;
        goto out_free;
    }

    out_free:
    EVP_PKEY_free(pkey);
    free(ek);

    out:
    return retval;
}

int main(int argc, char *argv[])
{
    FILE *rsa_pkey_file;
    int rv;

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s <PEM RSA Private Key File>\n", argv[0]);
        exit(1);
    }

    rsa_pkey_file = fopen(argv[1], "rb");
    if (!rsa_pkey_file)
    {
        perror(argv[1]);
        fprintf(stderr, "Error loading PEM RSA Private Key File.\n");
        exit(2);
    }

    rv = do_evp_unseal(rsa_pkey_file, stdin, stdout);

    fclose(rsa_pkey_file);
    return rv;
}

这篇关于你能帮助我使用rsa.h在c ++中使用openssl公钥加密吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆