Я розумію, що це дуже пізня (і довга) відповідь. Але, враховуючи, наскільки добре це питання, здається, займає місце в результатах пошукової системи, я зрозумів, що, можливо, варто написати гідну відповідь.
Багато з того, що ви прочитаєте нижче, запозичено з цієї демонстрації та документів 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,
RSA_F4,
NULL,
NULL
);
Якщо повернене значення 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,
pkey,
EVP_des_ede3_cbc(),
"replace_me",
10,
NULL,
NULL
);
Якщо ви не хочете шифрувати закритий ключ, просто перейдіть NULL
до третього та четвертого параметрів вище. У будь-якому випадку, ви точно захочете переконатись, що файл неможливо прочитати у всьому світі. (Для користувачів Unix це означає chmod 600 key.pem
.)
Ух! Тепер ми перейшли до однієї функції - нам потрібно записати сертифікат на диск. Для цього нам потрібна така функція PEM_write_X509
:
FILE * f;
f = fopen("cert.pem", "wb");
PEM_write_X509(
f,
x509
);
І ми закінчили! Сподіваємось, інформації в цій відповіді достатньо, щоб дати вам приблизне уявлення про те, як все працює, хоча ми ледве подряпали поверхню OpenSSL.
Для тих, хто зацікавлений побачити, як виглядає весь наведений вище код у реальному додатку, я зібрав Gist (написаний на C ++), який ви можете переглянути тут .