Как выполнить шифрование данных RSA с открытым ключом на C с использованием OpenSSL 3

Я относительно новичок в C и пытаюсь зашифровать строковые данные в C.

Мне предоставили ПУБЛИЧНЫЙ КЛЮЧ, который чем-то похож на

  const char *pub_key_str = "-----BEGIN PUBLIC KEY-----"
                              "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1WRkqlsZbmaNWbOZr/M4"
                              "ddwXKjmHUJ8zn9hy0WE15A8T4xjNuzJ/ATKbz20/2wVgFEGTWPNCUjkUvFzr3mhN"
                              "v/YJXo5iXQDP3M6rVv/w3SXaiR9C8yhDxWUz7WX/clmj7vDvk5Iydgq07gVScx+"
                              "5DBVbGAx10jHSDSa7f/NXMn9yNHad4hN0i1l+tsTB39CFsNeVFr349y4R8HA5Zma"
                              "EKseLc8iKzkwEQcuyMn4znaFpnOL0CmSrYB5K1E9zmmtDhMvDs540ZotcH/xpJiV"
                              "XEdQOWGFN6rryeAKP8y+sMRfQLxoiTg37YfsxTscf0gzKBizoamTF3TjRUNM55aE"
                              "BwIDAQAB"
                              "-----END PUBLIC KEY-----";

И примерное представление того, чего я пытаюсь достичь в Typescript

rsaEncrypt(text: string, publicKey: string): Promise<string> {
    const buffer = Buffer.from(JSON.stringify(text));
    const encrypted = publicEncrypt(
      {
        key: publicKey,
        padding: constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: 'sha1',
      },
      buffer,
    );
    return encrypted.toString('base64');
  }

Однако это моя реализация, мой rsa всегда равен NULL после PEM_read_bio_RSA_PUBKEY.


int rsa_encrypt(const unsigned char *msg, size_t msg_len, unsigned char **enc_msg, size_t *enc_msg_len) {
    RSA *rsa = NULL;
    BIO *bio = NULL;
    int ret = -1;

    tappa_print("%s", "In rsa_enc");
    tappa_print("%s", PUBLIC_KEY);
    bio = BIO_new_mem_buf(PUBLIC_KEY, -1);
    tappa_printf("Bio %d", BIO_get_init(bio));
    if (bio == NULL) {
        fprintf(stderr, "Error creating bio\n");
        return -1;
    }

    rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
    tappa_printf("RSA %d", rsa==NULL);
    if (rsa == NULL) {
        ERR_print_errors_fp(stderr);
        tappa_print("%s", strerror(errno));
        fprintf(stderr, "Error reading public key\n");
        //BIO_free(bio);
        return -1;
    }
    tappa_printf("RSA %d", RSA_size(rsa));
    *enc_msg = (unsigned char *)malloc(RSA_size(rsa));
    tappa_printf("Enc msg %d", sizeof *enc_msg);
    if (*enc_msg == NULL) {
        fprintf(stderr, "Memory allocation error\n");
        RSA_free(rsa);
        BIO_free(bio);
        return -1;
    }

    *enc_msg_len = RSA_public_encrypt(msg_len, msg, *enc_msg, rsa, RSA_PKCS1_PADDING);
    tappa_printf("Enc msg len%d", sizeof *enc_msg_len);
    if (*enc_msg_len == -1) {
        fprintf(stderr, "Encryption error\n");
        free(*enc_msg);
        RSA_free(rsa);
        BIO_free(bio);
        return -1;
    }

    ret = 0;

    tappa_printf("ret msg %d", ret);
    RSA_free(rsa);
    BIO_free(bio);

    return ret;
}

Я попробовал другой подход, который видел в Интернете, прочитав ключ из файла, однако после добавления файла в мой проект я столкнулся с ошибкой «Нет такого файла/каталога».

  FILE* fp =fopen("public.txt","r");

    if (fp==NULL){
        perror("file error");
        return NULL;
    }

ОБНОВЛЕНИЕ 1:: После того, как я обновился до более поздних функций для генерации ключей, шифрования и дешифрования

У меня нет проблем при создании ключа, используемого с этим кодом.

int rsaEvpKeyGen(EVP_PKEY **outPK){
EVP_PKEY_CTX * ctx = NULL;
EVP_PKEY * pkey = NULL;
EVP_PKEY * rkey = NULL;
size_t outlen = 0;
size_t doutlen = 0;

unsigned char * encrypted = NULL;
unsigned char * decrypted = NULL;

// Создаем контекст

ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (!ctx) {
    cleanup(ctx, pkey, encrypted, decrypted);
    return 1;
}
// Init key generator
if (EVP_PKEY_keygen_init(ctx) <= 0) {
    cleanup(ctx, pkey, encrypted, decrypted);
    return 2;
}
int KEY_BITS = 4096;
// Configure key generation
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KEY_BITS) <= 0) {
    cleanup(ctx, pkey, encrypted, decrypted);
    return 3;
}

// Create keys
if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
    cleanup(ctx, pkey, encrypted, decrypted);
    return 4;
}

*outPK =pkey;
return 0;

}

Однако, когда я пытаюсь создать EVP_PKEY из моей строки ключа, это не удается, поскольку мой EVP_PKEY_new_raw_public_key возвращает NULL

int rsaEvpEncrypt(const char * data){
EVP_PKEY_CTX * ctx = NULL;

const char *pub_key_str = "-----BEGIN PUBLIC KEY-----\n"
                          "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1WRkqlsZbmaNWbOZr/M4\n"
                          "ddwXKjmHUJ8zn9hy0WE15A8T4xjNuzJ/ATKbz20/2wVgFEGTWPNCUjkUvFzr3mhN\n"
                          "v/YJXo5iXQDP3M6rVv/w3SXaiR9C8yhDxWUz7WX/clmj7vDvk5Iydgq07gVScx+\n"
                          "5DBVbGAx10jHSDSa7f/NXMn9yNHad4hN0i1l+tsTB39CFsNeVFr349y4R8HA5Zma\n"
                          "EKseLc8iKzkwEQcuyMn4znaFpnOL0CmSrYB5K1E9zmmtDhMvDs540ZotcH/xpJiV\n"
                          "XEdQOWGFN6rryeAKP8y+sMRfQLxoiTg37YfsxTscf0gzKBizoamTF3TjRUNM55aE\n"
                          "BwIDAQAB\n"
                          "-----END PUBLIC KEY-----";
EVP_PKEY * pkey = NULL;
pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_RSA,NULL, (const unsigned char *) pub_key_str,
                                   strlen(pub_key_str) );


unsigned char * encrypted = NULL;
size_t outlen = 0;
const size_t inlen = strlen(data);

ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx) {
    cleanup(ctx, NULL, NULL, NULL);
    return 1000;
}

// Init encryption context.
if (EVP_PKEY_encrypt_init(ctx) <= 0) { // <-SEGFAULT LINE
    cleanup(ctx, pkey, encrypted, NULL);
    return 5;
}

// Defines padding
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
    cleanup(ctx, pkey, encrypted, NULL);
    return 6;
}

// Evaluating size for encrypted buffer and encrypting
if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char *) data, inlen) <= 0) {
    cleanup(ctx, pkey, encrypted, NULL);
    return 7;
}

encrypted = (unsigned char *)OPENSSL_malloc(outlen);
if (!encrypted) {
    cleanup(ctx, pkey, encrypted, NULL);
    return 8;
}

if (EVP_PKEY_encrypt(ctx, encrypted, &outlen, (const unsigned char *)data, inlen) <= 0) {
    cleanup(ctx, pkey, encrypted, NULL);
    return 9;
}

tappa_print("\"ENCRYPTED: %s", (char *)encrypted);

}

Я относительно новичок в C. Не отмечайте c++ для c вопросов.

PaulMcKenzie 02.05.2024 03:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
147
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, низкоуровневые (т. е. специфичные для алгоритма) API, такие как RSA_public_encrypt, устарели в OpenSSL 3 (и в основном так было, по крайней мере, с версии 1.1.0). Хотя их еще не удалили.

Во-вторых, ваш «машинописный» код (я предполагаю, что на самом деле nodejs) шифруется с использованием RSA-OAEP (исправление), чего нет в вашем коде, хотя я никогда раньше не замечал, что старый API может выполнять OAEP с использованием ненастраиваемых параметров по умолчанию, но вы все равно не должен. (добавлено) Как сообщает справочная страница RSA_public_encrypt,RSA_private_decrypt, предпочтительным методом (начиная с версии 1.0.0 в 2010 году) является высокоуровневый интерфейс EVP_PKEY_encrypt_init[_ex] и EVP_PKEY_encrypt, который может выполнять OAEP, включая (необязательные) параметры, и фактически справочную страницу. для этих функций даже есть пример для RSA-OAEP (без параметров).

В-третьих, значение errno полезно только для (некоторых) вызовов C-stdlib (в основном тех, которые включают вызов ОС); после неудачной операции с mem_BIO это неактуально и бесполезно.

В-четвертых, sizeof *enc_msg где enc_msg - unsigned char ** - это размер указателя, фиксированное значение, обычно равное сегодня 8, а не размер распределенных данных, на которые он указывает, что, вероятно, и есть то, что вам здесь нужно. Точно так же sizeof *enc_msg_len на size_t * сегодня является фиксированным значением, обычно равным 8, а не размеру зашифрованного текста RSA. Кроме того, если ваш tappa_printf использует те же спецификаторы, что и stdio {,v}{,f,s}printf, %d на самом деле не подходит для size_t, хотя на многих платформах он будет «случайно» работать. %zu гарантировано.

Тем не менее, ваш код работает для меня, если у меня есть открытый ключ, который на самом деле находится в формате PEM, и действительное (то есть небольшое) сообщение. Вы публикуете только «что-то вроде» своего открытого ключа, поэтому я не могу точно сказать, что с ним не так, но он определенно должен иметь разрывы строк (в C, символы новой строки) между строкой BEGIN и телом, телом и строкой END. , возможно, потребуется иметь разрывы строк внутри тела (фактический PEM требовал их каждые 64, OpenSSL ниже 1.1.0 требовал каждые 76 или меньше, но кратен 4; с тех пор предел увеличился, но он все еще не бесконечен, и не задокументировано, что я я нашел), и встроенные пространства не гарантируются, хотя некоторые из них могут иногда работать.

«RSA_public_encrypt устарел в OpenSSL 3», какая функция его заменяет. Или он просто больше не поддерживается? «шифрование с использованием RSA-OAEP, чего OpenSSL RSA_public_encrypt не делает — и не может», — это значит, что невозможно добиться того же результата в C. «Вы публикуете только «что-то вроде» своего открытого ключа», — я отредактировал сообщение и предоставил реальный ключ, используя его, мой rsa from rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); все еще НУЛЬ

Lilvinco 02.05.2024 13:43

@Lilvinco: Как я и предполагал, заявленный вами формат PEM не содержит необходимых разрывов строк в данных; добавьте их. Что касается других вопросов, когда я перепроверил, я обнаружил ошибку, поэтому см. «Редактирование» для исправления и дополнения.

dave_thompson_085 02.05.2024 15:06

Можете ли вы поделиться примером переменной char? Я добавил \n в конце каждой строки, но особого успеха не добился. Также я портировал и теперь использую обновленные функции, см. обновление 1.

Lilvinco 03.05.2024 17:05

@Lilvinco: Возможно, я раньше не присматривался достаточно внимательно, но 4-я строка в вашем «PEM» составляет 63 символа (исключая символ новой строки, если он присутствует), что неверно; Данные base64 должны поступать частями по 4 символа. Если я добавлю произвольный символ base64, он успешно декодируется — как PUBKEY! -- но почти наверняка это не правильное значение ключа. Найдите, какой символ отсутствует и где, и исправьте это. Использование _raw_public_key — совершенно неправильный подход: он работает только для алгоритмов, определяющих «необработанный» формат ключа, которым является только X25519 X448 Ed25519 Ed448, а НЕ RSA.

dave_thompson_085 05.05.2024 05:41

Действительно, ключ был неполным и недействительным, это очень помогло. Если вы здесь и все еще застряли, я использовал более новую функцию OSSL, дополнительную информацию можно найти здесь stackoverflow.com/questions/78427036/…

Lilvinco 05.05.2024 17:52

Другие вопросы по теме