как импортировать внешний открытый ключ RSA в OpenSSL - PullRequest
0 голосов
/ 08 июня 2018

Я хотел бы выполнить такие операции, как проверка подписи RSA через файл открытого ключа RSA, полученный от внешнего сервера.Значения ключей задаются в формате der или pem следующим образом.

der:"30820122300d06092a864886f70d01010105000382010f003082010a02820101008ab8c549138cc59f605b50265f69ec3b8ce3b3e9af5e174d26cfe242650104bea05101189676c7a292cc6b5ae719e119e3ac29e9d3ad9dadcda496f2f7185f9c0c4872a2db124f01992b238e83fd582d8a290f2973f9cf744f1e53f8aa53a225c12299b1cc3658fb607cb5aba579832d6c2687f71300a4df3df1c1407e17d22a5c19c830ed0c5824072309a612bb3e4fb339f25bbbcfe2999f4342110649abac5f4bfea2a59cd38c173979a679afdc8baacb4e87eb50acf806cfb7407504bdac110cfbcb99c04227031e146b9f3b8377c87035690309fae9872e2c7b93e8375fccdebc4f98be1d574269c513a43594f6b8861f7464832ae99ebaceae0fcc3ac50203010001"

pem_base64:"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2nsO4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhfnAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmDLWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmrrF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuDd8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6xQIDAQAB"

Я хочу импортировать вышеуказанные данные в "struct rsa_st * rsa" , но это не работает.

Например, чтобы импортировать открытый ключ rsa в формате der, я сделал следующее:

==========================================================
char data[] = "30820122300d06092a864886f70d01010105000382010f003082010a02820101008ab8c549138cc59f605b50265f69ec3b8ce3b3e9af5e174d26cfe242650104bea05101189676c7a292cc6b5ae719e119e3ac29e9d3ad9dadcda496f2f7185f9c0c4872a2db124f01992b238e83fd582d8a290f2973f9cf744f1e53f8aa53a225c12299b1cc3658fb607cb5aba579832d6c2687f71300a4df3df1c1407e17d22a5c19c830ed0c5824072309a612bb3e4fb339f25bbbcfe2999f4342110649abac5f4bfea2a59cd38c173979a679afdc8baacb4e87eb50acf806cfb7407504bdac110cfbcb99c04227031e146b9f3b8377c87035690309fae9872e2c7b93e8375fccdebc4f98be1d574269c513a43594f6b8861f7464832ae99ebaceae0fcc3ac50203010001";

unsigned char * pArr = (unsigned char *)malloc(buf_len);
RSA *pub_rsa = NULL;

fnStr2Hex(pArr, data); // Converts a data array composed of strings to a hex array (pArr).
pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)buf_len);
==========================================================

В этом случае функция In d2i_RSAPublicKey возвращает NULL Pointer.Я не знаю, что пошло не так.

И я не знаю, как изменить строковые данные, полученные pem_base64, на "struct rsa_st * rsa" .В примере кода используется функция с именем «ReadPublicKey» , которая, кажется, загружает файл сертификата X.509.

Я не читаю файл, но мне нужно получить данные изсервер, как указано выше.

Пожалуйста, ответьте человеку, который знает об этом.

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Цель, которую я делаю, состоит в том, чтобы найти модуль и показатель степени в открытом ключе RSA.Мой пример кода и текущие результаты показаны ниже.

==============================================================

void fnStr2Hex(char* out, char* in) { 
        int data_len = strlen(in); 
        char * pStr = in; 
        int i; 

        for(i=0; i<data_len/2; i++) { 
                char buf[3] = {0,}; 
                memcpy(buf, pStr, sizeof(buf)); 

                out[i] = (unsigned char)strtol(buf, NULL, 16); 

                // need to check strol 2nd arguments... for error checking.. 
                printf("i:%d, pArr[i]:%02X \n", i, out[i]); 

                pStr+=2; 
        } 
} 

int main() { 
        char raw_data[] = 
"30819F300D06092A864886F70D010101050003818D0030818902818100AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E19670203010001"; 

        int data_len = strlen(raw_data);  
        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

// raw_data is a string. Not in hex state. So I changed the contents of 
raw_data [] to hex in pArr. 
// The implementation of this function is above main function. 
        fnStr2Hex(pArr, raw_data); 

        STDout=BIO_new_fp(stdout,BIO_NOCLOSE); 

        pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)data_len); 

        if(pub_rsa == NULL) { 
                printf(&quot;error : failed d2i_RSAPublicKey \n&quot;); 
                return -1; 
        } 

        BN_print(STDout,pub_rsa->n);   // print modulus bignum 
        BN_print(STDout,pub_rsa->e);  //  print exponent bignum 

        return 0; 
} 

результат: ошибка: сбой d2i_RSAPublicKey Я не знаю, почему это сбой.

И вы сказали, что вам нужен префикс иPostfix, чтобы сделать формат PEM.Правильно ли настроить, как показано ниже?

raw_data[] = { 
"-----BEGIN PUBLIC KEY-----"\ 
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns"\ 
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf"\ 
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD"\ 
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr"\ 
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD"\ 
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6"\ 
"xQIDAQAB"\ 
"-----END PUBLIC KEY----- " };

После этого я кодировал следующим образом.

    int data_len = strlen(raw_data); 
        BIO *bufio = NULL; 
        RSA *pub_rsa = NULL; 

        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

        fnStr2Hex(pArr, raw_data);  // for converting hex 

        bufio = BIO_new_mem_buf((void*)pArr, data_len); 

        if(bufio == NULL) { 
                printf("Error (1) \n"); 
                return -1; 
        } 

        PEM_read_bio_RSAPublicKey(bufio, &pub_rsa, 0, NULL); 

        if(pub_rsa == NULL) { 
                printf("Error (2) \n"); 
                return -1; 
        } 
} // end of main 

Когда я выполняю вышеуказанный код, выводится ошибка (2).Я хочу помочь с двумя вышеупомянутыми (DER, PEM) ситуациями.Опять же, я хочу найти модуль и открытый показатель в открытом ключе RSA.(Я хочу реализовать это в коде C, а не в команде оболочки.)

BR,

0 голосов
/ 08 июня 2018

Ваши данные не являются (просто) публичным ключом RSA, как определено PKCS1, переизданным как RFC8017 , но как X.509 SubjectPublicKeyInfo, как определено X.509, переизданным как RFC5280 содержащий открытый ключ RSA, как определено RFC3279 .OpenSSL называет эту структуру X.509 PUBKEY. Используйте d2i_RSA_PUBKEY .

Также:

  • ваш опубликованный код не показывает, откуда взято buf_len.Убедитесь, что это правильно.

  • ваш опубликованный код устанавливает pArr, чтобы указывать на пространство, выделенное malloc, а затем передает &pArr как ppin в d2i подпрограмму.d2i подпрограммы изменяют указатель, на который указывает ppin, поэтому он больше не указывает на пространство, выделенное malloc.Если вы сейчас попытаетесь free(pArr), то это будет неопределенное поведение в соответствии со стандартом C, и на практике это может привести к сбоям или повреждению вашей кучи, что приведет к ошибкам или сбоям других, казалось бы, не связанных вещей;если вы не free, это утечка памяти.

  • Формат PEM - это не просто base64 из DER.Это base64 для DER, разбитый на строки, оканчивающиеся разделителями строк, с добавлением заголовка и концевых строк (тире, BEGIN / END, тип, тире);они являются частью формата и не являются обязательными.При желании может быть блок заголовка в стиле 822, но это здесь не применимо.Base64 из DER может быть полезен, но это НЕ PEM.


Meta: пожалуйста, добавьте дополнительную информацию о вашем вопросе к вашему вопросу.Stackexchange предназначен для того, чтобы иметь вопрос в вопросе и ответ (ы) в ответе (ах), среди прочих причин, потому что ответы не хранятся во временной последовательности.Я думал, что на этом есть страница справочного центра, но если это так, я не могу сейчас ее найти.

В любом случае, вы говорите, что ваш «текущий» код:

void fnStr2Hex(char* out, char* in) { 
        int data_len = strlen(in); 
        char * pStr = in; 
        int i; 

        for(i=0; i<data_len/2; i++) { 
                char buf[3] = {0,}; 
                memcpy(buf, pStr, sizeof(buf)); 

                out[i] = (unsigned char)strtol(buf, NULL, 16); 

                // need to check strol 2nd arguments... for error checking.. 
                printf("i:%d, pArr[i]:%02X \n", i, out[i]); 

                pStr+=2; 
        } 
} 

int main() { 
        char raw_data[] = 
"30819F300D06092A864886F70D010101050003818D0030818902818100AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E19670203010001"; 

        int data_len = strlen(raw_data);  
        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

// raw_data is a string. Not in hex state. So I changed the contents of 
raw_data [] to hex in pArr. 
// The implementation of this function is above main function. 
        fnStr2Hex(pArr, raw_data); 

        STDout=BIO_new_fp(stdout,BIO_NOCLOSE); 

        pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)data_len); 

        if(pub_rsa == NULL) { 
                printf(&quot;error : failed d2i_RSAPublicKey \n&quot;); 
                return -1; 
        } 

        BN_print(STDout,pub_rsa->n);   // print modulus bignum 
        BN_print(STDout,pub_rsa->e);  //  print exponent bignum 

        return 0; 
} 

Это не таккомпилируется;у него неправильный комментарий, он не объявляет STDout и pub_rsa и не имеет необходимых #include.Исправление этих проблем оставляет следующие проблемы:

  1. (большой) неправильное преобразование в гекс .Он копирует по 3 символа за раз из шестнадцатеричной строки, так что, тогда как преобразованные байты должны быть шестнадцатеричными 30 81 9F и т. Д., Он фактически вычисляет в моей системе (и, вероятно, большинство) 08 19 F3 и т. Д., Что совершенно неправильно.Так как он получает доступ по крайней мере к одному байту вне любого допустимого объекта, теоретически это может быть даже хуже.Ваша «проверка» printf показывает вам это, если вы посмотрите на это.

  2. (маленький) ваше распределение неверно.Во-первых, в C никогда не нужно приводить возвращаемое значение malloc (или realloc или calloc), а иногда (но не здесь) вредно; см. FAQ по comp.lang.c .Во-вторых, хотя строка hex состоит из 324 символов (плюс завершающий ноль), вам нужно только 162 байта, чтобы содержать декодированный результат (также показанный вашей проверкой printf).Однако, поскольку d2i игнорирует лишние конечные данные, это не причиняет вреда.Кроме того, у вас все еще есть проблема освобождения, как я описал ранее.

  3. (большой) Как я уже говорил, d2i_RSAPublicKey - неправильная процедура;он ожидает определенный формат данных (PKCS1), которого у вас нет. Правильная процедура для этих данных: d2i_RSA_PUBKEY.

  4. (маленький) Ваша обработка ошибок плохая.Когда подпрограмма OpenSSL возвращает индикацию ошибки, почти во всех случаях она также сохраняет подробную информацию об ошибках в «очереди ошибок».Самый простой способ отобразить эту информацию с помощью ERR_print_errors_fp, хотя есть более сложные способы, если вы предпочитаете.Для достижения наилучших результатов вам необходимо предварительно загрузить строки ошибок, как правило, на ERR_load_crypto_strings или в версии 1.1.0+ OPENSSL_init_crypto или варианты того, которые также охватывают SSL (который вы в настоящее время используете).не используют и не нужны).Хотя в этом случае, перед исправлением d2i_RSAPublicKey, очередь ошибок дает вам внутреннее описание для процедур декодирования ASN.1, которое вы, вероятно, в любом случае не поняли бы - хотя, возможно, оно по крайней мере указало бы вам в направленииглядя на формат ASN.1.

Ваш код с этими исправлениями правильно печатает модуль и экспоненту в шестнадцатеричном виде:

AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E196710001

Поскольку вы не установили какой-либо разделитель, вы должны знать длину каждой части, чтобы правильно ее прочитать.

Однако это не будет работать в OpenSSL версии 1.1.0 или новее.API был изменен, так что вы теперь не можете напрямую обращаться к таким полям, как n и e в RSA (это typedef ror struct rsa_st).

Затем вы предлагаете следующее для формата PEM:

raw_data[] = { 
"-----BEGIN PUBLIC KEY-----"\ 
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns"\ 
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf"\ 
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD"\ 
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr"\ 
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD"\ 
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6"\ 
"xQIDAQAB"\ 
"-----END PUBLIC KEY----- " };

Это также неправильно.В дополнение к отсутствующему типу данных (формально описатель объявления) char синтаксис переноса строки препроцессора

"ABC"\
"DEF"\
"GHI"

в точности эквивалентен

"ABC" "DEF" "GHI" 

, который, поскольку смежные строковые литералы объединяютсяв точности соответствует

"ABCDEFGHI"

и не содержит каких-либо ограничителей строки, как требуется.Вам нужно поместить как минимум символы LF, а в идеале символы CR и LF, в строковые значения, что в C может быть выполнено переносимо только с использованием escape-последовательностей \n и \r.(Хотя спецификация PEM вызывает CR LF, OpenSSL будет работать только с LF.) Таким образом, вам необходимо:

char raw_data[] = { "-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns\n"
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf\n"
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD\n"
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr\n"
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD\n"
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6\n"
"xQIDAQAB\n"
"-----END PUBLIC KEY-----\n" };

(или то же самое без { }, потому что инициализация массива символов с одним строковым литераломобрабатывается C как особый случай).

Затем ваш код пытается преобразовать эти данные из шестнадцатеричного кода, так же, как и в предыдущем случае - но PEM имеет значение , а не шестнадцатеричный, так что это совершенно неверно даже после исправления шестнадцатеричной процедуры каквыше.Эти данные уже представлены в формате PEM, поэтому вы должны просто использовать BIO_new_mem_buf(raw_data, strlen(raw_data)) - или в качестве оптимизации BIO_new_mem_buf(raw_data,-1), которая делает для вас strlen.

Наконец, у вас та же проблема, что и выше: выпозвоните PEM_read_bio_RSAPublicKey, но ваши данные не в формате PKCS1;вместо этого позвоните PEM_read_bio_RSA_PUBKEY.

Опять ваш код с этими изменениями работает.

Поскольку это ТАК, я не спрашиваю, почему вы не хотите использовать для этого функции командной строки - даже если бы это занялотолько секунда или две вместо дней.

...