d2i_RSA_PUBKEY, d2i_RSAPrivateKey и d2i_RSAPublicKey возвращает NULL

Я создал закрытый ключ RSA, используя следующую команду:

openssl genrsa -out keypair.pem 2048 

Я должен использовать DER-закодированные ключи (PKCS # 1) для этого проекта, поэтому я сгенерировал два файла DER из этого файла с закрытым ключом PEM – один с закрытым ключом, а другой с открытым ключом.

 openssl rsa -inform PEM -in keypair.pem -outform DER -pubout -out public.der openssl rsa -inform PEM -in keypair.pem -outform DER -out private.der 

В моем коде я загрузил содержимое обоих этих файлов в переменные char *.

Ни одно из следующих действий не ожидается:

 d2i_RSA_PUBKEY(NULL, &public_key_bytes, public_key_length); d2i_RSAPublicKey(NULL, &public_key_bytes, public_key_length); d2i_RSAPrivateKey(NULL, &private_key_bytes, private_key_length); 

Я знаю, что все они возвращают null . Я также пробовал следующее:

 RSA * rsa = RSA_new(); d2i_RSA_PUBKEY(&rsa, &public_key_bytes, public_key_length); RSA * rsa = RSA_new(); d2i_RSAPublicKey(&rsa, &public_key_bytes, public_key_length); RSA * rsa = RSA_new(); d2i_RSAPrivateKey(&rsa, &private_key_bytes, private_key_length); 

Все возвращают null .

Мой полный тестовый код выглядит следующим образом:

 #include  #include  #include  #include  #include  typedef struct { int len; char * bytes; } FileData; static FileData readFileBytes(const char * name, int zero_ended) { FILE * fl = fopen(name, "r"); if (fl == NULL) return (FileData) { .len = 0, .bytes = NULL }; fseek(fl, 0, SEEK_END); long len = ftell(fl); char * ret = malloc(len + (zero_ended ? 1 : 0)); fseek(fl, 0, SEEK_SET); fread(ret, 1, len, fl); if (zero_ended) ret[len] = 0; fclose(fl); return (FileData) { .len = len, .bytes = ret }; } int main() { FileData private_key = readFileBytes("../private.der", 0); FileData public_key = readFileBytes("../public.der", 0); char* public_key_bytes = public_key.bytes; int public_key_length = public_key.len; char* private_key_bytes = private_key.bytes; int private_key_length = private_key.len; RSA * rsa; public_key_bytes = public_key.bytes; public_key_length = public_key.len; rsa = d2i_RSA_PUBKEY(NULL, &public_key_bytes, public_key_length); printf("d2i_RSA_PUBKEY(NULL, &public_key_bytes, public_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); public_key_bytes = public_key.bytes; public_key_length = public_key.len; rsa = d2i_RSAPublicKey(NULL, &public_key_bytes, public_key_length); printf("d2i_RSAPublicKey(NULL, &public_key_bytes, public_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); private_key_bytes = private_key.bytes; private_key_length = private_key.len; rsa = d2i_RSAPrivateKey(NULL, &private_key_bytes, private_key_length); printf("d2i_RSAPrivateKey(NULL, &private_key_bytes, private_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); public_key_bytes = public_key.bytes; public_key_length = public_key.len; rsa = RSA_new(); rsa = d2i_RSA_PUBKEY(&rsa, &public_key_bytes, public_key_length); printf("d2i_RSA_PUBKEY(&rsa, &public_key_bytes, public_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); public_key_bytes = public_key.bytes; public_key_length = public_key.len; rsa = RSA_new(); rsa = d2i_RSAPublicKey(&rsa, &public_key_bytes, public_key_length); printf("d2i_RSAPublicKey(&rsa, &public_key_bytes, public_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); private_key_bytes = private_key.bytes; private_key_length = private_key.len; rsa = RSA_new(); rsa = d2i_RSAPrivateKey(&rsa, &private_key_bytes, private_key_length); printf("d2i_RSAPrivateKey(&rsa, &private_key_bytes, private_key_length) != NULL -> %s\n", (rsa != NULL) ? "true" : "false"); getchar(); return 0; } 

Что я делаю неправильно?

TLDR : d2i_RSA_PUBKEY и d2i_RSAPrivateKey должны работать и делать для меня в Unix.

Вы говорите, что хотите «DER-закодированные ключи (PKCS # 1)».

Для publickeys OpenSSL обычно использует формат, определенный в X.509 как SubjectPublicKeyInfo который содержит AlgorithmIdentifier plus (отредактированный) BIT STRING содержащий значение publickey в структуре, которая изменяется в зависимости от алгоритма. Для RSA альгид содержит идентификатор OID, идентифицирующий RSA, и никаких параметров; зависимая от алгоритма структура – это PKCS # 1.

Напротив, OpenSSL поддерживает два типа формата privatekey: существует формат «legacy» для каждого алгоритма (кроме DH), который для RSA является PKCS # 1; и общий формат, определенный PKCS # 8, который, как и SPKI, состоит из AlgorithmIdentifier плюс значение, зависящее от алгоритма, на этот раз в OCTET STRING . PKCS # 8 также имеет возможность шифровать ключ, который SPKI не имеет или нуждается.

Старые части OpenSSL, включая genrsa командной строки genrsa и rsa , используют устаревший формат закрытых ключей, но формат публикации SPKI, который OpenSSL называет PUBKEY. Таким образом, ваши команды rsa создали файл публикации, читаемый d2i_RSA_PUBKEY но не d2i_RSAPublicKey (который был бы только частью PKCS # 1), а файл закрытого ключа, читаемый d2i_RSAPrivateKey .

Если вам действительно нужно публиковать в формате «голый» PKCS # 1, утилита rsa имеет параметры -RSAPublicKey_in и -RSAPublicKey_out чтобы читать и записывать этот формат с 1.0.0, хотя документально только недавно и все еще не в справочном сообщении. Этот файл будет d2i_RSAPublicKey чтения d2i_RSAPublicKey но не d2i_RSA_PUBKEY .

Одна возможность : вы не упоминаете операционную систему. Файлы DER являются двоичными, а в C для правильной обработки двоичных файлов в Windows вы должны fopen с помощью модификатора b , здесь вы хотите "rb" для чтения двоичного кода. Если я запускаю ваш код в Unix, он работает, но для получения правильных результатов в Windows я должен добавить b .

Также небольшая точка: вы говорите о «загрузке содержимого … в char * variables». Фактически вы загружаете содержимое файла в память и используете переменную char * чтобы указать на них. Строго говоря, подпрограммы OpenSSL d2i хотят адрес const unsigned char * variable – и ваш компилятор должен предупредить вас об этом несоответствии, по крайней мере, если вы запустите его в стандартном режиме. Но C требует указателей на все char ( signed unsigned и «простые») с квалификацией или без нее, чтобы иметь те же требования к представлению и выравниванию, даже если они несовместимы, как определено в стандарте, поэтому передайте char ** где const unsigned char ** ожидается работа.