Я относительно новичок в 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);
}
Во-первых, низкоуровневые (т. е. специфичные для алгоритма) 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: Как я и предполагал, заявленный вами формат PEM не содержит необходимых разрывов строк в данных; добавьте их. Что касается других вопросов, когда я перепроверил, я обнаружил ошибку, поэтому см. «Редактирование» для исправления и дополнения.
Можете ли вы поделиться примером переменной char? Я добавил \n в конце каждой строки, но особого успеха не добился. Также я портировал и теперь использую обновленные функции, см. обновление 1.
@Lilvinco: Возможно, я раньше не присматривался достаточно внимательно, но 4-я строка в вашем «PEM» составляет 63 символа (исключая символ новой строки, если он присутствует), что неверно; Данные base64 должны поступать частями по 4 символа. Если я добавлю произвольный символ base64, он успешно декодируется — как PUBKEY! -- но почти наверняка это не правильное значение ключа. Найдите, какой символ отсутствует и где, и исправьте это. Использование _raw_public_key — совершенно неправильный подход: он работает только для алгоритмов, определяющих «необработанный» формат ключа, которым является только X25519 X448 Ed25519 Ed448, а НЕ RSA.
Действительно, ключ был неполным и недействительным, это очень помогло. Если вы здесь и все еще застряли, я использовал более новую функцию OSSL, дополнительную информацию можно найти здесь stackoverflow.com/questions/78427036/…
Я относительно новичок в C. Не отмечайте
c++
дляc
вопросов.