Программно создать сертификат X509 с помощью OpenSSL - PullRequest
64 голосов
/ 02 ноября 2008

У меня есть приложение на C / C ++, и мне нужно создать сертификат pem X509, содержащий как открытый, так и закрытый ключ. Сертификат может быть самоподписанным или неподписанным, не имеет значения.

Я хочу сделать это внутри приложения, а не из командной строки.

Какие функции OpenSSL сделают это для меня? Любой пример кода является бонусом!

Ответы [ 4 ]

168 голосов
/ 26 февраля 2013

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

Многое из того, что вы прочтете ниже, заимствовано из этой демонстрации и документации OpenSSL. Код ниже относится как к С, так и к С ++.


Прежде чем мы сможем создать сертификат, нам нужно создать закрытый ключ. 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);

Структура 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 ++), который вы можете просмотреть здесь .

44 голосов
/ 02 ноября 2008

Сначала вам необходимо ознакомиться с терминологией и механизмами.

Сертификат X.509 по определению не включает закрытый ключ. Вместо этого это версия открытого ключа, подписанная СА (вместе с любыми атрибутами, которые СА помещает в подпись). Формат PEM на самом деле поддерживает только раздельное хранение ключа и сертификата, хотя вы можете затем объединить их.

В любом случае вам потребуется задействовать более 20 различных функций API OpenSSL для создания ключа и самозаверяющего сертификата. Пример - в самом источнике OpenSSL, в demos / x509 / mkcert.c

Более подробный ответ см. Объяснение Натана Османа ниже.

2 голосов
/ 02 ноября 2008

Есть ли шанс сделать это через system звонок из вашего приложения? Несколько веских причин для этого:

  • Лицензирование: вызов исполняемого файла openssl, вероятно, отделяет его от вашего приложения и может обеспечить определенные преимущества. Отказ от ответственности: проконсультируйтесь с адвокатом по этому вопросу.

  • Документация: OpenSSL поставляется с феноменальной документацией командной строки, которая значительно упрощает потенциально сложный инструмент.

  • Тестируемость: вы можете использовать OpenSSL из командной строки, пока не поймете, как именно создавать свои сертификаты. Есть лот опций; ожидайте потратить около дня на это, пока не получите все детали правильно. После этого легко добавить команду в ваше приложение.

Если вы решите использовать API, проверьте список разработчиков openssl-dev на www.openssl.org.

Удачи!

0 голосов
/ 13 июля 2011

Очень простой учебник по созданию цифровых сертификатов http://publib.boulder.ibm.com/infocenter/rsthelp/v8r0m0/index.jsp?topic=/com.ibm.rational.test.lt.doc/topics/tcreatecertopenssl.html

В выполнении этих команд из вашего кода я не уверен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...