Как стать автором
Обновить

Комментарии 27

А я-то думал в BouncyCastle заморочно. OpenSSL вообще очень low level API имеет, в очередной раз убеждаюсь.

Все так) и документация оставляет желать лучшего :) Поэтому я и решил написать статью, чтобы помочь остальным в этом нелегком пути познания опенссл

В BouncyCastle тоже low level API есть, и к слову, если просто смотреть в спецификации, то даже без документации все достаточно понятно — там просто прямолинейное отображение ASN.1 структур. Вот их high level API действительно не понять, как использовать и зачем он вообще нужен. Мой issue полностью игнорируют.

Вы его в Java мире используете... В C# мире документации нет вообще =) Официальный ответ "смотрите исходники или документацию на Java версию".

API в принципе не такое плохое, но местами конечно особенное.

Это довольно сложный и долгий путь, но, рабочий, как я выяснил на практике. Но если у вас есть более быстрый или эффективный вариант - прошу в комментарии.

`X509_EXTENSION_create_by_NID`, по сути все из этого сниппета в одной функции.

А вообще код сложный, потому что вся эта предметная область (сертификаты X509 и т.п.) сложная. Спецификации на десятки и сотни страниц, ничего не поделаешь.

А у openssl на самом деле вполне себе единообразный API. Если к нему привыкнуть, то потом проблем не будет, а вместо документации лучше читать исходники. Причем не самого openssl, а boringssl - там все очень сильно упростили внутри.

Надо попробовать сделать так:)

Спасибо за совет, обновил пример. Работает как часы :)

как же так? Это не С++, а простой старый С. Где владение ресурсами? Где гарантия освобождения ресурсов при исключениях? Где умные указатели?

Все есть, но как я и написал, чтобы не загромождать пример, умные указатели я оставил на конец. Где и написал, как их можно использовать: std::unique_ptr<X509_EXTENSION, decltype(&::X509_EXTENSION_free)> ex(X509_EXTENSION_new(), ::X509_EXTENSION_free); Моей главной задачей было показать последовательность вызовов, которая поможет решить человеку проблему. Красоту он сможет навести позже, когда у него наконец-то все заработает :)

ну это был сарказм, я не против и С, сам использую с 80х. Но С++ требует другого подхода, спасибо что проапдйтили.
Я использую openssl довольно давно, хотя переход на 3.х только в планах на это лето. Что касается документации - увы, она хромает. Но, если вы находите публикации или примеры как использовать сам openssl бинарник, то несложно раскрутить что он внутри делает, благо вся коммуникация с остальной частью идет по тому же API. И вообще примеры в openssl вполне вполне полезные.

по поводу uniqur_ptr есть лайфхак, который я использую для заворачивания openssl объектов.
Тип деструктора должен быть compile time и размером 0, тогда uniqur_ptr будет 8 байт и без анонимных вызовов.
Достигается это:

using MyPtr = std::unique_ptr<Type, decltype([](Type *t) {Type_free(t);}) >

// либо через структуру если decltype не работает (вроде на gcc8 и ниже) 
trmplate<auto Func>
struct MyFree
{
 void operator () (Type *t) {(*Func)(t) ;} 
} 
using MyPtr2 = std::unique_ptr<Type, MyFree<Type_free>>

В вашем же случае uniqur_ptr будет 16байт и при удалении делать анонимный вызов

Обновил примеры :)

а как насчет длинных серийников? Они там байт 20, а вовсе не int32_t. Нужно бы через BN.

Спасибо за замечание, в моем проекте просто таких длинных не встречается. Обновлю пример, чтобы можно было для всех использовать:)

как насчет использования C++17 std::filesystem для сохранения в файл?

На работе просто C++11, поэтому я еще не работал с 17. А чем плохи вызовы самой библиотеки? BIO_write_filename() и PEM_write_bio_X509()?

перед подписью, openssl, во всяком случае в версии 1.1, еще ставит NID_subject_key_identifier в "hash" и NID_authority_key_identifier в "keyid:always" и для CA ключей делает NID_key_usage в "keyCertSign,cRLSign", а для остальных в "digitalSignature,keyEncipherment"

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

Я предполагаю данные сниппеты выдернуты из продакшн кода, а сертификаты в таком коде часто именуют переменной X (от X509, особенно в OpenSSL мире), и автор просто забыл поменять на certificate.

все именно так, поправил :)

Ещё могу посоветовать https://lapo.it/asn1js/ для просмотра контента сгенеренного сертификата (или даже ключа, или чего-то ещё в asn1/pem кодировке) если у вас с ним что-то не так. Сильно помогает иногда. Меня спасло от сумасшедствия когда я не мог понять почему сгенеренный мной сертификат Фаерфокс упрямо не желает импортировать, хотя такой же сгенеренный командной строкой openssl работал безо всяких проблем...

Только зачем версию сертификату самому ставить? Она вычисляется по правилам спецификации, чтобы парсер знал, что дальше парсить. Уж хотя бы это поле библиотека должна самостоятельно генерировать. BouncyCastle так и делает в своем low-level API.

Может быть я чего-то не знаю, но когда я добавлял расширения, которые есть только в v3 у меня все равно выставлялась версия 1

Версия должна быть == 2. Там вообще все просто делается и примеров куча в самой OpenSSL. В версиях 1.0.x был пример ~/demos/x509/mkcert.c , потом примеры убрали, но там ничего особо не поменялось в вызовах. Ну и код в ~/apps/x509.c как был так и остался, там больше всего, но тоже все просто. Весь код для генерации сертификата с ключами и подписью можно было уложить в 100 строк или меньше, у вас куча кусков кода повставлено и результат по итогу практически нулевой.

Немножко критики.

Для начала я бы не ставил уровень сложности как сложный. Так как тут нет никаких сложностей, все прямо указано в документации.

Далее, статья опять же в разделе c++. Тогда почему бы во-первых не сделать псевдоним типа для указателя cert. А также все методы, которые относятся к cert не объединить в класс. Получился бы своеобразный wrapper. Почему, да потому, что для использования это будет гораздо удобнее потом, все методы доступны через класс, внутри класса будет инкапсулирован указатель. Из этого выведется масса вариантов использования для c++. Именно тот случай, когда ООП выгоднее структурного... Да и будет в разы короче в использовании потом.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории