Программно создать сертификат X509 с помощью OpenSSL
У меня есть приложение C/C++, и мне нужно создать сертификат PEM X509, содержащий как открытый, так и закрытый ключ. Сертификат может быть самоподписанным, или нет, не важно.
Я хочу сделать это внутри приложения, а не из командной строки.
какие функции OpenSSL сделают это для меня? Любой пример кода-это бонус!
4 ответа:
сначала вам нужно ознакомиться с терминологией и механизмами.
Сертификат X. 509 сертификат по определению, не содержит закрытого ключа. Вместо этого это подписанная CA версия открытого ключа (вместе с любыми атрибутами, которые CA помещает в подпись). Формат PEM действительно поддерживает только отдельное хранение ключа и сертификата - хотя вы можете затем объединить их.
в любом случае, вам нужно будет вызвать 20+ различные функции API OpenSSL для создания ключа и самозаверяющего сертификата. Пример находится в самом источнике OpenSSL, в demos/x509 / mkcert.c
более подробный ответ см. объяснение Натана Османа ниже.
я понимаю, что это очень поздний (и длинный) ответ. Но учитывая, насколько хорошо этот вопрос, похоже, занимает место в результатах поиска, я подумал, что, возможно, стоит написать достойный ответ.
многое из того, что вы прочитаете ниже, взято из демо и документы OpenSSL. Приведенный ниже код применяется как к C, так и к c++.
прежде чем мы сможем создать сертификат, нам нужно создать закрытый ключ. OpenSSL предоставляет
EVP_PKEY
структура для хранения независимого от алгоритма закрытого ключа в памяти. Эта структура объявлена вopenssl/evp.h
, а какopenssl/x509.h
(что нам понадобится позже), поэтому вам не нужно явно включать заголовок.для того, чтобы выделить
EVP_PKEY
структуре, мы используемEVP_PKEY_new
:EVP_PKEY * pkey; pkey = EVP_PKEY_new();
существует также соответствующая функция для освобождения структуры -
EVP_PKEY_free
- который принимает один аргумент:EVP_PKEY
структура инициализирована выше.теперь нам нужно сгенерировать ключ. Для нашего примера мы создадим ключ RSA. Это делается с помощью
RSA_generate_key
функция, которая объявлена вopenssl/rsa.h
. Эта функция возвращает указатель наRSA
структура.простой вызов функции может выглядеть так:
RSA * rsa; rsa = RSA_generate_key( 2048, /* number of bits for the key - 2048 is a sensible value */ RSA_F4, /* exponent - RSA_F4 is defined as 0x10001L */ NULL, /* callback - can be NULL if we aren't displaying progress */ NULL /* callback argument - not needed in this case */ );
если возвращаемое значение
RSA_generate_key
иNULL
, потом что-то пошло не так. Если нет, то у нас теперь есть ключ RSA, и мы можете назначить его нашемуEVP_PKEY
структура из более ранних:EVP_PKEY_assign_RSA(pkey, rsa);
The
RSA
структура будет автоматически освобождается, когдаEVP_PKEY
структура освобождается.
теперь для самого сертификата.
OpenSSL использует
X509
структура для представления сертификата x509 в памяти. Определение этой структуры находится вopenssl/x509.h
. Первая функция, которая нам понадобится, этоX509_new
. Его использование относительно просто:X509 * x509; x509 = X509_new();
как и в случае с
EVP_PKEY
есть соответствующая функция для освобождения структуры -X509_free
.теперь нам нужно установить несколько свойств сертификата, используя некоторые
X509_*
функции:ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
это устанавливает серийный номер нашего сертификата к '1'. Некоторые HTTP-серверы с открытым исходным кодом отказываются принимать сертификат с серийным номером '0', который используется по умолчанию. Следующий шаг-указать промежуток времени во время которого сертификат действительно действителен. Мы делаем это с помощью следующих двух вызовов функций:
X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
первая строка устанавливает сертификат
notBefore
свойство к текущему времени. (ТегX509_gmtime_adj
функция добавляет указанное количество секунд текущего времени - в этом случае нет.) Вторая строка устанавливает сертификатnotAfter
свойство до 365 дней с этого момента (60 секунд * 60 минут * 24 часа * 365 дней).теперь нам нужно установить открытый ключ для нашего сертификат с использованием ключа, который мы создали ранее:
X509_set_pubkey(x509, pkey);
поскольку это самозаверяющий сертификат, мы устанавливаем имя эмитента на имя субъекта. Первым шагом в этом процессе является получение название темы:
X509_NAME * name; name = X509_get_subject_name(x509);
если вы когда-либо создавали самозаверяющий сертификат в командной строке раньше, вы, вероятно, помните, что вас попросили ввести код страны. Вот где мы предоставляем его вместе с организацией ('O') и общим названием ('CN'):
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 Inc.", -1, -1, 0); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0);
(я использую значение 'CA' здесь, потому что я канадец, и это наш код страны. Также обратите внимание, что параметр #4 должен быть явно приведен к
unsigned char *
.)теперь мы можем установить наименование эмитента:
X509_set_issuer_name(x509, name);
и, наконец,мы готовы выполнить процесс подписания. Мы зовем
X509_sign
С ключом, который мы создали ранее. Код для этого до боли прост:X509_sign(x509, pkey, EVP_sha1());
обратите внимание, что мы используем SHA-1 алгоритм хэширования для подписи ключа. Это отличается от
mkcert.c
демо я упомянул в начале этого ответа, который использует MD5.
теперь у нас есть самоподписанный сертификат! Но мы еще не закончили - нам нужно записать эти файлы на диск. К счастью OpenSSL имеет нас там тоже с
PEM_*
функции, которые объявлены вopenssl/pem.h
. Первый, который нам понадобится, этоPEM_write_PrivateKey
для сохранения нашего закрытого ключа.FILE * f; f = fopen("key.pem", "wb"); PEM_write_PrivateKey( f, /* write the key to the file we've opened */ pkey, /* our key from earlier */ EVP_des_ede3_cbc(), /* default cipher for encrypting the key on disk */ "replace_me", /* passphrase required for decrypting the key on disk */ 10, /* length of the passphrase string */ NULL, /* callback for requesting a password */ NULL /* data to pass to the callback */ );
если вы не хотите шифровать закрытый ключ, а затем просто пройти
NULL
для третьего и четвертого параметра выше. В любом случае, вы определенно захотите убедиться, что файл не читается в мире. (Для пользователей Unix, это означаетchmod 600 key.pem
.)уфф! Теперь мы дошли до одной функции - нам нужно записать сертификат на диск. Для этого нам нужна функция
PEM_write_X509
:FILE * f; f = fopen("cert.pem", "wb"); PEM_write_X509( f, /* write the certificate to the file we've opened */ x509 /* our certificate */ );
и мы сделали это! Надеюсь, информации в этом ответе достаточно, чтобы дайте вам приблизительное представление о том, как все работает, хотя мы едва поцарапали поверхность OpenSSL.
для тех, кто заинтересован в том, чтобы увидеть, как выглядит весь приведенный выше код в реальном приложении, я собрал Gist (написанный на C++), который вы можете просмотреть здесь.
любой шанс сделать это через
system
звонок из вашего приложения? Несколько веских причин для этого:
лицензирование: вызов
openssl
исполняемый файл, возможно, отделяет его от вашего приложения и может обеспечить определенные преимущества. отказ от ответственности: проконсультируйтесь с адвокатом по этому вопросу.документация: OpenSSL поставляется с феноменальную документация командной строки, которая значительно упрощает потенциально сложные инструменты.
тестируемость: вы можете использовать OpenSSL из командной строки, пока не поймете, как именно создавать свои сертификаты. Есть и много параметры; рассчитывать потратить около суток на это, пока вы не получите все детали. После этого тривиально включить команду в ваше приложение.
Если вы решили использовать API, проверьте
openssl-dev
список разработчиков на www.openssl.org.хорошее удачи!
очень простой учебник для создания цифровых сертификатов http://publib.boulder.ibm.com/infocenter/rsthelp/v8r0m0/index.jsp?topic=/com.ibm.rational.test.lt.doc/topics/tcreatecertopenssl.html
о выполнении этих команд из вашего кода я не уверен.