Я понимаю, что это очень поздний (и длинный) ответ. Но, учитывая, насколько хорошо этот вопрос ранжируется в результатах поисковых систем, я подумал, что, возможно, стоит написать достойный ответ.
Многое из того, что вы прочтете ниже, заимствовано из эту демонстрацию и документы 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
.)
Ух! Теперь у нас есть одна функция - нам нужно записать сертификат на диск. Для этого нам нужна функция 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 ++), который вы можете просмотреть здесь.
person
Nathan Osman
schedule
26.02.2013