Генерация сертификата в Linux на c++
(1 чел.) (1) гость
  • Страница:
  • 1

ТЕМА: Генерация сертификата в Linux на c++

Генерация сертификата в Linux на c++ 1 год назад #4401

  • Kuyubaev
  • Новый участник
  • Постов: 18
  • Репутация: 0
Добрый день!

У нас возникла необходимость работы с ГОСТовыми сертификатами на Linux, а именно написание shared library (*.so) для генерации сертификата.

Для решения задачи сначала был выбран следующий подход, назову его №1:

Написание консольного приложения с++ с использованием статических библиотек libcrypto.a и libssl.a.
Приложение написать получилось и она генерирует сертификат.
Далее приступил к переделыванию консольного приложения в shared library.
Здесь возникла проблема - shared library требует, чтобы исходные объектные файлы компилировались c флагом -fPIC (Position Independent Code). Видимо объектные файлы из библиотек libcrypto.a и libssl.a компилировались без данного флага.
Как результат было решено, что этот подход тупиковый.


Далее было решено перейти к подходу №2:

Написание консольного приложения с++ с использованием динамических библиотек libcrypto.so, libssl.so, kncagost.so.
Проект вроде как компилируется, но выдает ошибку Unable to get "kncagost" engine.
Т.е. получается, что ENGINE_load_builtin_engines() не загружает ГОСТовый движок.
На всякий случай пробовал писать вместо этой функции ENGINE_load_gost(), но ее, при использовании динамических библиотек, компилятор вообще не распознает.


Нужна помощь:
1) Либо получить от вас статические библиотеки скомпилированные с флагом -fPIC
или
2) Объяснить, что у меня не так делается при использовании динамических библиотек



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

#include <iostream>

#include <openssl/engine.h>
#include <openssl/x509.h>
#include <openssl/pkcs12.h>
#include <openssl/pem.h>

/* Generates GOST key */
EVP_PKEY * generate_gost_key(ENGINE *engine) {

// Приватный ключ
EVP_PKEY *pkey = NULL;

// Контекст приватного ключа
EVP_PKEY_CTX *pkey_ctx = NULL;

// Создание контекста
pkey_ctx = EVP_PKEY_CTX_new_id(NID_id_Gost34310_2004, engine);

if (!pkey_ctx) {
std::cerr << "Unable to create 'NID_id_Gost34310_2004' context." << std::endl;
goto cleanup;
}

EVP_PKEY_paramgen_init(pkey_ctx);

EVP_PKEY_CTX_ctrl(pkey_ctx,
NID_id_Gost34310_2004,
EVP_PKEY_OP_PARAMGEN,

// Some kind of magic...
// Здесь должен быть макрос EVP_PKEY_CTRL_GOST_PARAMSET (EVP_PKEY_ALG_CTRL+1)
EVP_PKEY_ALG_CTRL + 1,

NID_id_Gost34310_2004_PKIGOVKZ_A_ParamSet,
NULL);


if (EVP_PKEY_keygen_init(pkey_ctx) <= 0) {
std::cerr << "Unable to init keygen." << std::endl;
goto cleanup;
}

/* Generate key */
if (EVP_PKEY_keygen(pkey_ctx, &pkey) <= 0) {
std::cerr << "Unable to generate keys." << std::endl;
goto cleanup;
}

cleanup:

if (pkey_ctx) {
EVP_PKEY_CTX_free(pkey_ctx);
}

return pkey;
}

/* Generates a self-signed x509 certificate. */
X509 * generate_x509(EVP_PKEY * pkey, const EVP_MD *md)
{
/* Allocate memory for the X509 structure. */
X509 * x509 = X509_new();

/* Set the serial number. */
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);

/* This certificate is valid from now until exactly one year from now. */
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);

/* Set the public key for our certificate. */
X509_set_pubkey(x509, pkey);

/* We want to copy the subject name to the issuer name. */
X509_NAME * name = X509_get_subject_name(x509);

/* Set the country code and common name. */
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"MyCompany", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0);

/* Now set the issuer name. */
X509_set_issuer_name(x509, name);

/* Actually sign the certificate with our key. */
if (!X509_sign(x509, pkey, md))
{
std::cerr << "Error signing certificate." << std::endl;
X509_free(x509);
return NULL;
}

return x509;
}

bool save_pem(EVP_PKEY * pkey, X509 * x509)
{
bool result = false;

FILE * pkey_file = fopen("key.pem", "wb");
FILE * x509_file = fopen("cert.cer", "wb");

if (!pkey_file) {
std::cerr << "Unable to open \"key.pem\" for writing." << std::endl;
goto cleanup;
}

if (!x509_file) {
std::cerr << "Unable to open \"cert.cer\" for writing." << std::endl;
goto cleanup;
}

if (!PEM_write_PrivateKey(pkey_file, pkey, NULL, NULL, 0, NULL, NULL)) {
std::cerr << "Unable to write private key to disk." << std::endl;
goto cleanup;
};

if (!PEM_write_X509(x509_file, x509)) {
std::cerr << "Unable to write x509 to disk." << std::endl;
goto cleanup;
}

result = true;

cleanup:

if (pkey_file) {
fclose(pkey_file);
}

if (x509_file) {
fclose(x509_file);
}

return result;
}

bool save_pkcs12(EVP_PKEY * pkey, X509 * x509) {

bool result = false;

PKCS12 * p12 = NULL;
FILE * p12_file = NULL;

char pass[128] = "123456";

p12 = PKCS12_create(pass, NULL, pkey, x509, NULL, 0, 0, PKCS12_DEFAULT_ITER, 1, NID_key_usage);

if (!p12) {
std::cerr << "Error creating pkcs12." << std::endl;
goto cleanup;
}

p12_file = fopen("cert.p12", "wb");

if (i2d_PKCS12_fp(p12_file, p12) != 1) {
std::cerr << "Error saving cert.p12." << std::endl;
// OpenSSL Error. Use `ERR_peek_last_error_line` to find out more.
goto cleanup;
}

result = true;

cleanup:

if (p12) {
PKCS12_free(p12);
}

if (p12_file) {
fclose(p12_file);
}

return result;
}

int main()
{

ENGINE *engine = NULL;
EVP_PKEY *pkey = NULL;
X509 * x509 = NULL;
const EVP_MD *md;

//ENGINE_load_openssl(); //эту строку и добавлял и удалял. Со статическими библиотеками
//приложение работает и без этой строки

//ENGINE_load_gost(); // эту строку использую в случае со статическими библиотеками.
ENGINE_load_builtin_engines(); //эта строка для случая динамических библиотек

OpenSSL_add_all_algorithms(); // Без этого не создастся pkcs12

//ENGINE_register_all_pkey_asn1_meths(); //эту строку так же и удалял и добавлял. Приложение
//со статическими библиотеками работает и без этой строки

engine = ENGINE_by_id("kncagost"); // Ищем нуцовский гост

if (!engine) {
std::cerr << "Unable to get 'kncagost' engine." << std::endl;
goto cleanup;
}

pkey = generate_gost_key(engine);

if (!pkey) {
std::cout << "Unable to generate key";
goto cleanup;
}

md = EVP_get_digestbyname(SN_id_Gost34311_95);
if (!md) {
std::cerr << "Unable to get 'md_gost95' md." << std::endl;
goto cleanup;
}

x509 = generate_x509(pkey, md);

if (!x509) {
std::cout << "Unable to generate x509";
goto cleanup;
}

save_pem(pkey, x509);
save_pkcs12(pkey, x509);


cleanup:

if (engine) {
ENGINE_free(engine);
}

if (x509) {
X509_free(x509);
}

if (pkey) {
EVP_PKEY_free(pkey);
}

return 0;
}

Компилирую командой:
g++ -c -fPIC CertDLL CertDLL.cpp -I/opt/certgen/Linux/dynamic/x64/include

Линкую командой:
g++ -o CertDLL СertDLL.o -l:libcrypto.so.1.0.0 -l:libssl.so.1.0.0 -l:libknca-pkcs11-x64.so -l:libkncagost.so -L/opt/certgen/Linux/dynamic/x64 -L/opt/certgen/Linux/dynamic/x64/engines -Wl,-rpath=/opt/certgen/Linux/dynamic/x64:/opt/certgen/Linux/dynamic/x64/engines

добавлял эти пути также в LD_LIBRARY_PATH

при проверке командой ldd CertDLL выдает следующее:
linux-vdso.so.1 => (0x00007ffea2752000)
libcrypto.so.1.0.0 => /opt/certgen/Linux/dynamic/x64/libcrypto.so.1.0.0 (0x00007fc62d95c000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc62d5da000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc62d210000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc62d00c000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc62cdf2000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc62cae9000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc62dd47000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc62c8d3000)

Как будто остальные библиотеки вообще не линкуются, так обычно происходит когда они нигде по коду не используются.

Короче говоря я запутался очень конкретно.

Re: Генерация сертификата в Linux на c++ 1 год назад #4403

  • Kuyubaev
  • Новый участник
  • Постов: 18
  • Репутация: 0
Попробовал собрать пример из SDK - KalkanGOST_test.cpp под Linux.

Проблема та же.

Со статическими библиотеками отрабатывает отлично, но с динамическими выдает ошибку:

GOST example start...
140547520657080:error:0609E09C:lib(6):func(158):reason(156):p_lib.c:239:
140547520657080:error:0606F076:lib(6):func(111):reason(118):evp_pkey.c:84:TYPE=GOST Old 34.310-2004
140547520657080:error:23076072:lib(35):func(118):reason(114):p12_kiss.c:129:
>>Subject info write to file subject_info.txt
Segmentation fault (core dumped)


Собираю пример так:

g++ -o test KalkanGOST_test.cpp -I/opt/kalkantest/Linux/static/x64/include -lkncagost -l:libcrypto.so.1.0.0 -l:libssl.so.1.0.0 -ldl -lz -lpcsclite -L/opt/kalkantest/Linux/dynamic/x64 --L/opt/kalkantest/Linux/dynamic/x64/engines -Wl,-rpath=/opt/kalkantest/Linux/dynamic/x64:/opt/kalkant
est/Linux/dynamic/x64/engines


В случае с динамическими библиотеками заменяю только строку

ENGINE_load_gost() на ENGINE_load_builtin_engines()

Re: Генерация сертификата в Linux на c++ 11 мес., 3 нед. назад #4441

  • Bozghurt
  • Новый участник
  • Постов: 17
  • Репутация: 0
А как у вас со статическими все получилось?

Я столкнулся с этой проблемой pki.gov.kz/index.php/ru/forum/7-dlya-raz...to-engine-load-gost.

То есть та же проблема с fPIC: gist.github.com/rauanmayemir/7ced6dfaeed774ff3a6678cd4367ba7a

Re: Генерация сертификата в Linux на c++ 11 мес., 1 нед. назад #4500

  • Kuyubaev
  • Новый участник
  • Постов: 18
  • Репутация: 0
Смотря, что вы разрабатываете? Если динамическую библиотеку под линукс, т.е. файл с расширением *.so, то со статическими никак. Дело в том, что при их создании нужно указывать флаг -fPIC, но так как доработаны они специалистами НУЦ, то мы это сделать никак не можем. Исходников они не предоставляют, чтобы мы сами могли их перекомпилировать.

Если вы разрабатываете приложение, а не динамическую библиотеку, то проблем с fPIC у вас не должно возникать, ну насколько я смог разобраться (с++ не мой основной профиль). У меня был тестовый пример для генерации гостового ключа с использованием статических библиотек. Я сначала компилировал его, а затем линковал с библиотеками вот так

компиляция: g++ -c CertApp.cpp -o CertApp.o -I/opt/certgen/static/x64/include
линк: g++ -o CertApp CertApp.o -L/opt/certgen/static/x64 -lcrypto -lssl -lz -ldl -lpcsclite

Т.е. у меня проект лежал в директории /opt/certgen. В директории /opt/certgen/static/x64 лежали статические библиотеки от НУЦ РК libcrypto.a, libssl.a. Плюс, в моем случае, были нужны библиотеки -lz, -ldl, -lpcsclite. Это уже не НУЦовские библиотеки. Чтобы программа нашла их я опытным путем и с помощью гугла установил дополнительные пакеты под Линукс.

Вот кусок кода для подгрузки ГОСТ движка. Могу скинуть весь код если дадите ссылку.
...
//nuzhno pri ispolzovanii staticheskih bibliotek
ENGINE_load_gost();

//nuzhno pri ispolzovanii dynamicheskih bibliotek
//ENGINE_load_builtin_engines();

OpenSSL_add_all_algorithms(); // Без этого не создастся pkcs12
ENGINE_register_all_pkey_asn1_meths();

engine = ENGINE_by_id("kncagost"); // Ищем нуцовский гост

//nuzhno pri ispolzovanii dynamicheskih bibliotek
//ENGINE_init(engine);


Для использования динамических библиотек надо соответственно раскомментить строки связанные с динамикой и закомментить строку связанную со статикой. И обязательно нужно чтобы библиотека libkncagost.so лежала в директории
/home/ai/sdk/linux64dynamic/lib/engines. Видимо программисты НУЦ задали статический путь к данной библиотеке. Я создал такую директорию и закинул ее туда, а путь к ней при компиляции не указывал, только для двух других библиотек.

g++ -fPIC -shared -o CertDLL.so CertDLL.cpp -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux -I/opt/certgen/dynamic/x64/include -l:libssl.so.1.0.0 -l:libcrypto.so.1.0.0 -L/opt/certgen/dynamic/x64 -Wl,-rpath=/opt/certgen/dynamic/x64


Инклуды от Джава это для моего случая.

Re: Генерация сертификата в Linux на c++ 11 мес., 1 нед. назад #4501

  • Bozghurt
  • Новый участник
  • Постов: 17
  • Репутация: 0
Ясно, спасибо. Вроде то же самое пытался сделать, но возможно у меня сперва компилится в .a, а затем уже линкуется в object file. Попробую с динамической тщательно.

Re: Генерация сертификата в Linux на c++ 11 мес., 1 нед. назад #4503

  • Kuyubaev
  • Новый участник
  • Постов: 18
  • Репутация: 0
Точно не уверен, но по ссылке которую вы мне скинули gist.github.com/rauanmayemir/7ced6dfaeed774ff3a6678cd4367ba7a ощущение, что у вас не хватает каких-то дополнительных пакетов под линукс. Типа вот такого github.com/SoftEtherVPN/SoftEtherVPN/issues/525
  • Страница:
  • 1
FaLang translation system by Faboba