Можно ли помочь мне получить голову вокруг openssl шифрования с открытым ключом с rsa.h в C++?

Я пытаюсь получить голову вокруг шифрования с открытым ключом с помощью openssl реализации rsa в C++. Можно ли помочь? До сих пор это мои мысли (исправьте при необходимости),

  1. Alice подключена к Bob по сети
  2. Alice и Bob хотят безопасную связь
  3. Alice генерирует общественность / пара с закрытым ключом и отправляет открытый ключ Bob
  4. Bob получает открытый ключ и шифрует случайным образом сгенерированный симметричный ключ шифра (например, шифр) с открытым ключом и отправляет результат Alice
  5. Alice дешифрует шифрованный текст с первоначально сгенерированным закрытым ключом и получает симметричный ключ шифра
  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.

Править: 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(){}
10
задан Ben J 21 January 2010 в 14:28
поделиться

2 ответа

Вы должны использовать функции более высокого уровня «конверт-шифрование» из 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.

Как только вы получите это, чтобы решить проблему аутентификации, я упомянул в моем комментарии, вам нужно создать доверенный орган («Трент»). Открытый ключ Трента известен всем пользователям (распространяется с приложением или похожим - просто загрузите его из файла PEM). Вместо того, чтобы обмениваться голыми параметрами RSA, ALICE и BOB Exchange X509 сертификатов, которые содержат свои открытые ключи RSA вместе с их именем и подписаны Трент. Алиса и Боб тогда каждый проверил сертификат, который они получили от другого (используя открытый ключ TRENT, который они уже знают), включая проверку того, что связанное имя является правильным, прежде чем продолжить протокол. OpenSSL включает в себя функции для загрузки и проверки сертификатов в заголовке x509.h .


Вот пример того, как использовать EVP_SEAL * () для шифрования файла, учитывающего открытый ключ получателя. Он принимает файл открытого ключа PEM RSA (т. Е. Как генерируется OpenSSL RSA -Pubout ) в качестве аргумента командной строки, считывает исходные данные из stdin и записывает зашифрованные данные на STDOUT. Чтобы расшифровать, использовать EVP_OPEN * () вместо этого и PEM_READ_RSAPRIVATEKEY () , чтобы прочитать закрытый ключ, а не открытый ключ.

Это не так уж вредно - и, безусловно, меньше ошибок, кроме того, чтобы возиться за создание прокладки, IVS и т. Д. В себе (функция уплотнения делает как части 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> /* 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 () решительно криптографически сильный генератор случайных чисел! Создание вашего симметричного ключа с использованием RAND () достаточно, чтобы вся система полностью небезопасна. (Функции EVP _ * () создают необходимые случайные числа самими, используя криптографически сильные RNG, посеянные из соответствующего источника энтропии).

  • Вы устанавливаете IV для режима CFB в фиксированное значение (ноль). Это отрицает любое преимущество использования режима CFB в первую очередь (позволяя злоумышленникам тривиально выполнять атаки для замены блоков и хуже). (Функции EVP _ * () приводятся соответствующие IV для вас, при необходимости).

  • RSA_PKCS1_OAEEP_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;
}
27
ответ дан 3 December 2019 в 15:52
поделиться

На самом деле, нет проблем, я только что прочитал, что в основном, объект RSA - это структура, которая содержит как публичные, так и частные поля. Можно извлечь открытые данные полей и отправить их только Бобу.

Т.е. в основном, чтобы извлечь открытые поля из rsa и сохранить каждое из них в двух различных буферах (которые представляют собой char-массивы и затем могут быть отправлены Бобу), вы делаете:

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

А затем Боб, на принимающей стороне, восстанавливает следующим образом:

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

Боб может затем использовать rsa* для публичного шифрования симметричного ключа шифрования, который затем может быть отправлен Элис. Затем Элис может расшифровать с помощью закрытого ключа

Ben.

0
ответ дан 3 December 2019 в 15:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: