代码之家  ›  专栏  ›  技术社区  ›  Ben J

你能帮我在OpenSSL中用RSA .H在C++中进行公钥加密吗?

  •  10
  • Ben J  · 技术社区  · 16 年前

    我正试图通过C++中的RSA实现OpenSSL实现公钥加密。你能帮忙吗?到目前为止,这些是我的想法(如有必要,请纠正)

    1. 爱丽丝通过网络与鲍勃相连
    2. 爱丽丝和鲍勃想要安全的通讯
    3. Bob接收公钥并使用公钥加密随机生成的对称密码密钥(例如,blowfish),然后将结果发送给Alice
    4. Alice使用最初生成的私钥解密密文,并获得对称blowfish密钥
    5. Alice和Bob现在都知道对称blowfish密钥,并且可以建立安全的通信通道

    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?必须将其重新解释为字符数组,然后以标准方式发送吗?我听说了一些关于证书的事情——它们与此有关吗?

    抱歉问了这么多问题, 本。

    编辑:我目前雇用的Coe:

    /*
     *  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(){}
    
    4 回复  |  直到 16 年前
        1
  •  27
  •   caf    13 年前

    实际上,您应该使用 openssl/evp.h ,而不是直接使用低级RSA函数。这些为你做了大部分工作,意味着你不必重新发明轮子。

    在这种情况下,您将使用 EVP_SealInit() , EVP_SealUpdate() 和 EVP_SealFinal() EVP_OpenInit() , EVP_OpenUpdate() 和 EVP_OpenFinal() . 我建议使用 EVP_aes_128_cbc() 作为密码类型参数的值。

    RSA * 把手,你用 EVP_PKEY_assign_RSA() 把它放到一个 EVP_PKEY * EVP函数的句柄。

    x509.h 标题。


    EVP_Seal*() 在给定收件人公钥的情况下加密文件。它采用PEM RSA公钥文件(即由 openssl rsa -pubout )作为命令行参数,从stdin读取源数据并将加密数据写入stdout。要解密,请使用 EVP_Open*() 相反,以及 PEM_read_RSAPrivateKey() 读取私钥而不是公钥。

    #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;
    }
    

    您发布的代码很好地说明了为什么您应该使用更高级别的函数-您已经陷入了几个陷阱:

    • rand() 强调 不 一个加密强随机数生成器!使用生成对称密钥 兰德() EVP_*() 函数使用加密强RNG(从适当的熵源中播种)自己生成必要的随机数。

    • 您正在将循环流化床模式的IV设置为固定值(零)。这首先否定了使用CFB模式的任何优势(允许攻击者轻松地执行块替换攻击和更糟糕的攻击)(这个 执行副总裁*()

    • 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> /* 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;
    }
    
        2
  •  0
  •   Ben J    16 年前

    实际上,没问题,我刚刚读到,基本上,RSA对象是一个包含公共和私有字段的结构。可以提取公共字段数据,并仅将其发送给Bob。

    也就是说,基本上,要从rsa提取公共字段并将每个字段存储在两个不同的缓冲区中(这两个缓冲区是字符数组,然后可以发送给Bob),您需要执行以下操作:

    sprintf(keyBufferA,"%s\r\n",BN_bn2hex(rsa->n));
    sprintf(keyBufferB,"%s\r\n",BN_bn2hex(rsa->e));
    

    然后,接收端的Bob进行如下重构:

    rsa = RSA_new();
    BN_hex2bn(&rsa->n, keybufA);
    BN_hex2bn(&rsa->e, keybufB);
    

    Bob可以使用rsa*公开加密对称密码密钥,然后将其发送给Alice。然后Alice可以用私钥解密

    本。

        3
  •  0
  •   Fatih Arslan    14 年前

    我写了两封信 关于CAF的代码。它们经过大量修改,并使用OpenSSL的 BIO 用于更多抽象的容器。

    RSA 和 DES ,但是您可以很容易地从代码中更改它。编译指令在代码中。我需要一个工作的例子,我希望有人觉得这有用。我还对代码进行了注释。您可以从这里获得:

    拿 文件 作为输入: https://github.com/farslan/snippets/blob/master/hybrid_file.c

    字符串缓冲区 https://github.com/farslan/snippets/blob/master/hybrid_data.c

        4
  •  0
  •   Satish    13 年前

    谢谢@Caf。你的帖子有帮助。然而我得到了

      PEM_read_RSA_PUBKEY(rsa_pkey_file, &rsa_pkey, NULL, NULL)
    

    我改成

    BIO *bio;
    X509 *certificate;
    
    bio = BIO_new(BIO_s_mem());
    BIO_puts(bio, (const char*)data);
    certificate = PEM_read_bio_X509(bio, NULL, NULL, NULL);
    EVP_PKEY *pubkey = X509_get_pubkey (certificate);
    rsa_pkey = EVP_PKEY_get1_RSA(pubkey);