2008-11-02 6 views
57

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

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

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

ответ

36

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

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

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

Для получения более подробного ответа см. Ниже Nathan Osman's explanation.

0

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

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

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

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

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

Удачи вам!

+0

OpenSSL не находится под лицензией GPL http://www.openssl.org/source/license.html – Zoredache 2008-11-02 02:55:38

+5

OpenSSL является лицензией по лицензии типа apache, ее можно использовать в коммерческих приложениях, как и любая другая лицензия, отличная от копилефта ,Люди все еще могут обратиться к адвокату, чтобы убедиться, что все, что они делают, в порядке, но у него нет связанных с GPL вопросов. – 2008-11-02 02:58:38

+0

Отмечено и обновлено - спасибо. Разделение открытого кода из кода с закрытым кодом, как правило, является хорошей идеей, и если эффективность не имеет решающего значения, другие причины делают хороший пример для использования автономной утилиты openssl. – 2008-11-02 03:05:31

139

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

Многое из того, что вы будете читать ниже, заимствовано у this demo и документов 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); 

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.)

Whew! Теперь мы ограничимся одной функцией - нам нужно записать сертификат на диск. Функция нам нужно для этого 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++), который вы можете просмотреть here.

Смежные вопросы