В данной статья я хочу рассказать, как работать с X509 сертификатом используя OpenSSL 3.0.0 в С++, начиная от генерации своего сертификата и заканчивая его валидацией. Так как информации в интернете по этой теме почти нет, то все, что я вам расскажу, я узнал исходя из своего печального опыта работы с этой библиотекой. Я очень надеюсь, что эта статья окажется вам полезной и сможет сохранить ваше время. В данной статье, я не буду рассказывать вам, что такое X509 сертификат, надеюсь, что это вы уже знаете, а если нет, то ссылка на статью вот тут. Начнем с простого, создания сертификата. Для начала нам необходимо подключить заголовочный файл <openssl/x509v3.h>. Тут все просто, для создания - вызываем X509_new(), а для очистки памяти - X509_free(). Если у нас уже имеется заполненная структура X509 сертификата, а нам нужно полностью ее скопировать в нашу переменную, то мы можем воспользоваться функцией X509_dup(), в которую нужно передать указатель на сертификат, который мы хотим скопировать. Давайте добавим нашему сертификату пропертей, начнем с номера, он же serial number. С помощью X509_get_serialNumber получаем указатель на проперть сертификата, а далее, с помощью RAND_bytes генерируем уникальный серийный номер и выставляем его используя ASN1_STRING_set(). ASN1_STRING_set() вернет 1 на удачный вызов, 0 на завалившийся. Это правило работает для всех функций при работе с сертификатом. Выставим версию нашему сертификату, используя функцию X509_set_version(). На текущий момент есть три версии сертификата, которым соответствуют следующие значения: 0x0 0x1 0x2 Используя функцию X509_get_subject_name мы можем получить указатель на subject name нашего сертификата, а вызвав X509_NAME_add_entry_by_txt(), можем его обновить. Теперь добавим сроки валидности нашего сертификата, с какой по какую дату он будет работать. Для этого воспользуемся функциями X509_getm_notAfter() и X509_getm_notBefore(). Дальше в примерах я пропускаю обработку ошибок, но вы должны держать ее в голове и использовать в настоящем проекте. Теперь добавим стартовую точку. Код получается точно таким же, как и для начальной, за исключением имени функции для получения проперти - X509_getm_notBefore() Допустим, нам понадобилось выставить Issuer для нашего сертификата, тот сертификат, которым подписан наш. Для этого нужно использовать X509_set_issuer_name(). Добавить что-то в поле Issuer нашему сертификату тоже очень просто, можно сделать следующим образом, используя уже знакомую нам X509_NAME_add_entry_by_txt(): Добавим пару стандартных расширений (standart extensions) для нашего сертификата. Для стандартных расширений в OpenSSL существуют собственные ID (nid). Например, для Basic Constraints  - это NID_basic_constraints, а для Key Usage - NID_key_usage. Эти айди необходимы, если мы хотим задать те или иные расширения для нашего сертификата. Это все работает до тех пор, пока нам не понадобится добавить кастомное расширение, поддержку которых добавили в третьей версии сертификата. Тут все немного сложнее, но тоже выполнимо. Для начала создадим объект в базе данных OpenSSL, в который позднее запишем наше расширение. Теперь можно заняться подготовкой данных, создаем строку, которая будет хранить ключ и значение нашего расширения. Добавляем расширение в сертификат.  Это довольно сложный и долгий путь, но, рабочий, как я выяснил на практике. Но если у вас есть более быстрый или эффективный вариант - прошу в комментарии. Ну и какой же сертификат без публичного ключа? Давайте его добавим. Тут все очень просто, вызываем X509_set_pubkey() и готово. Если вам интересен процесс генерации и работы с публичными и приватным ключами в OpenSSL, то пишите комментарии. Тут все просто, генерируем пару ключей - приватный и публичный, используя EVP_RSA_gen(). Подробнее о генерации ключей и работе с ними читайте в следующей статье. Выставляем ключ с помощью X509_set_pubkey() И наконец, подписываем наш сертификат. Есть несколько популярных алгоритмов для подписи, но основной - это SHA256, он и приведен в примере. По нему можно понять процесс, а по его имени найти нужный в исходном коде OpenSSL. Подробнее о работе с BIO будет написано в следующей статье, пока что не будем на этом останавливаться. Создаем био BIO_new(BIO_s_file(), с помощью функции BIO_write_filename создаем файл и используя PEM_write_bio_X509() сохраняем сертификат в файл в формате PEM.  Если вам понравилась данная статья и вы хотите увидеть продолжение, описывающее работу с ключами и стораджем сертификатов, а также научиться их валидировать - ставьте лайки и оставляйте комментарии.  Надеюсь, данная статья будет вам полезна и сохранит вам кучу времени, избавив от часов чтения скудной документации и редких мануалов. UPD: Добавил примеры кода на гитхаб, буду обновлять по мере написания статей  С++ разработчик встроенных систем