С++ openssl - сгенерировать пару ключей RSA и прочитать

Я пытаюсь сгенерировать пару ключей RSA, используя библиотеку openssl, а затем прочитать те же ключи позже. Однако это не удается. Иногда это дает мне эту ошибку:

ошибка: 0906D06C: подпрограммы PEM: PEM_read_bio: нет стартовой строки

И иногда это дает мне эту ошибку:

ошибка: 0906D06C: библиотека (9): функция (109): причина (108)

Каков правильный способ сгенерировать пару ключей, а затем прочитать ее? Вот мой код. Если вы запустите его, вы обнаружите, что он правильно генерирует пару ключей RSA, но не может их прочитать позже.

    #include <stdio.h>
    #include <iostream>
    #include <openssl/rsa.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <exception>

    bool generate_key() {
        size_t pri_len;            // Length of private key
        size_t pub_len;            // Length of public key
        char *pri_key;           // Private key in PEM
        char *pub_key;           // Public key in PEM

        int ret = 0;
        RSA *r = NULL;
        BIGNUM *bne = NULL;
        BIO *bp_public = NULL, *bp_private = NULL;
        int bits = 2048;
        unsigned long e = RSA_F4;

        EVP_PKEY *evp_pbkey = NULL;
        EVP_PKEY *evp_pkey = NULL;

        BIO *pbkeybio = NULL;
        BIO *pkeybio = NULL;

        // 1. generate rsa key
        bne = BN_new();
        ret = BN_set_word(bne, e);
        if (ret != 1) {
            goto free_all;
        }

        r = RSA_new();
        ret = RSA_generate_key_ex(r, bits, bne, NULL);
        if (ret != 1) {
            goto free_all;
        }

        // 2. save public key
        //bp_public = BIO_new_file("public.pem", "w+");
        bp_public = BIO_new(BIO_s_mem());
        ret = PEM_write_bio_RSAPublicKey(bp_public, r);
        if (ret != 1) {
            goto free_all;
        }

        // 3. save private key
        //bp_private = BIO_new_file("private.pem", "w+");
        bp_private = BIO_new(BIO_s_mem());
        ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);

        //4. Get the keys are PEM formatted strings
        pri_len = BIO_pending(bp_private);
        pub_len = BIO_pending(bp_public);

        pri_key = (char*) malloc(pri_len + 1);
        pub_key = (char*) malloc(pub_len + 1);

        BIO_read(bp_private, pri_key, pri_len);
        BIO_read(bp_public, pub_key, pub_len);

        pri_key[pri_len] = '\0';
        pub_key[pub_len] = '\0';

        printf("\n%s\n%s\n", pri_key, pub_key);

        //verify if you are able to re-construct the keys
        pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
        if (pbkeybio == NULL) {
            return -1;
        }
        evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
        if (evp_pbkey == NULL) {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            printf("Error reading public key:%s\n", buffer);
        }

        pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
        if (pkeybio == NULL) {
            return -1;
        }
        evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
        if (evp_pbkey == NULL) {
            char buffer[120];
            ERR_error_string(ERR_get_error(), buffer);
            printf("Error reading private key:%s\n", buffer);
        }

        BIO_free(pbkeybio);
        BIO_free(pkeybio);

        // 4. free
        free_all:

        BIO_free_all(bp_public);
        BIO_free_all(bp_private);
        RSA_free(r);
        BN_free(bne);

        return (ret == 1);
    }

    int main(int argc, char* argv[]) {
        generate_key();
        return 0;
    }

person sburnwal    schedule 16.05.2018    source источник
comment
Иногда? Это предполагает неинициализированные данные, которые меня не удивят, глядя на код. Используйте valgrind или аналогичный отладчик памяти. Кроме того, сократите свой код, чтобы приблизиться к минимально воспроизводимому примеру.   -  person Ulrich Eckhardt    schedule 16.05.2018
comment
Ошибка по сути одна и та же, просто иногда кажется, что вы загрузили строки ошибок, а иногда нет, см.: openssl.org/docs/man1.1.0/crypto/ERR_load_crypto_strings.html   -  person hauron    schedule 16.05.2018
comment
Под «иногда» я имел в виду — в Linux я получаю первое, в Mac — второе.   -  person sburnwal    schedule 16.05.2018


Ответы (1)


Выглядит хорошо для меня. За исключением перезагрузки; Я бы использовал PEM_read_bio_RSAPublicKey вместо PEM_read_bio_PUBKEY. Я не уверен, что это лучший способ сделать это, хотя.

--- /tmp/stack_openssl.cpp.back 2018-05-25 12:53:12.366488025 +0000
+++ /tmp/stack_openssl.cpp  2018-05-25 13:57:20.614066828 +0000
@@ -18,6 +18,8 @@
   int bits = 2048;
   unsigned long e = RSA_F4;

+  RSA *pb_rsa = NULL;
+  RSA *p_rsa = NULL;
   EVP_PKEY *evp_pbkey = NULL;
   EVP_PKEY *evp_pkey = NULL;

@@ -66,27 +68,32 @@
   printf("\n%s\n%s\n", pri_key, pub_key);

   //verify if you are able to re-construct the keys
-  pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
+  pbkeybio = BIO_new_mem_buf((void*) pub_key, pub_len);
   if (pbkeybio == NULL) {
     return -1;
   }
-  evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
-  if (evp_pbkey == NULL) {
+  pb_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &pb_rsa, NULL, NULL);
+  if (pb_rsa == NULL) {
     char buffer[120];
     ERR_error_string(ERR_get_error(), buffer);
     printf("Error reading public key:%s\n", buffer);
   }
+  evp_pbkey = EVP_PKEY_new();
+  EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);

-  pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
+  pkeybio = BIO_new_mem_buf((void*) pri_key, pri_len);
   if (pkeybio == NULL) {
     return -1;
   }
-  evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
-  if (evp_pbkey == NULL) {
+  p_rsa = PEM_read_bio_RSAPrivateKey(pkeybio, &p_rsa, NULL, NULL);
+  if (p_rsa == NULL) {
     char buffer[120];
     ERR_error_string(ERR_get_error(), buffer);
     printf("Error reading private key:%s\n", buffer);
   }
+  evp_pkey = EVP_PKEY_new(); 
+  EVP_PKEY_assign_RSA(evp_pkey, p_rsa);

   BIO_free(pbkeybio);
   BIO_free(pkeybio);
person Chris    schedule 25.05.2018
comment
Это действительно было так - поскольку я использовал функцию PEM_write_bio_RSAPrivateKey() для сохранения ключа, для чтения следует использовать эквивалентный метод PEM_read_bio_RSAPublicKey() вместо PEM_read_bio_PUBKEY(). - person sburnwal; 04.06.2018