Программный пример создания заявки на сертификат в формате PKCS#10 , реализованный на OpenSSL. Генерация ключа ГОСТ Р 34.10-2001 и подпись заявки происходят "на борту" Рутокен ЭЦП
Для сборки примера под платформу Win32 с помощью cl.exe требуются заголовочные файлы OpenSSL и статическая библиотека libeay32.lib. Полный комплект OpenSSL для win32 с нужными либами и заголовочными файлами можно скачать по ссылке http://www.slproweb.com/download/Win32O … 1_0_0e.exe.
Приложение следует собирать с опцией /MT.
Для работы собранного приложения в environment следует добавить переменную окружения OPENSSL_ENGINES, в которой указать путь к папке, в которой располагаются библиотеки pkcs11_gost.dll и gost.dll. Кроме того, библиотеки libeay32.dll, libp11.dll и libltdl3.dll должны находиться либо в текущей директории приложения, либо в директории, прописанной в переменной окружения PATH.
Приложение имеет параметры командной строки SLOT токена:ID ключа и PIN-код.
Пример запуска приложения:
request_pkcs10 100 12345678
Получить тестовый сертификат по созданной заявке можно, например, здесь http://www.cryptopro.ru/certsrv/certrqxt.asp. Для этого заявку следует сохранить в файл командой
request_pkcs10 101 12345678 > req.csr
а затем содержимое файла вставить в форму на сайте. ВНИМАНИЕ, ниже на форуме представлен пример записи сертификата на токен. Для его работы требуется сертификат в формате PEM. Поэтому рекомендуется при получении сертификата выбрать формат хранения Base64.
#include <windows.h>
#include <openssl/lhash.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/crypto.h> /* for CRYPTO_* and SSLeay_version */
#include <openssl/rand.h>
#include <openssl/md4.h>
#include <openssl/des.h>
#include <openssl/engine.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#define CMD_MODULE_PATH (ENGINE_CMD_BASE+1)
#define CMD_PIN (ENGINE_CMD_BASE+2)
#define CMD_LOAD_CERT_CTRL (ENGINE_CMD_BASE+5)
#define CMD_SAVE_CERT_CTRL (ENGINE_CMD_BASE+6)
#define GOST2001_KEY_TYPE 811
ENGINE* LoadEngine(const char* engine_name, const char* pkcs11_provider, const char* pin)
{
ENGINE* engine_pkcs11=NULL;
engine_pkcs11=ENGINE_by_id(
engine_name);
if(!engine_pkcs11)
return NULL;
if(!ENGINE_ctrl(engine_pkcs11, CMD_MODULE_PATH, 0,
(void*)pkcs11_provider, NULL)) {
ENGINE_free(engine_pkcs11);
return NULL;
}
if(pin) {
if(!ENGINE_ctrl(engine_pkcs11, CMD_PIN, 0,
(void*)pin, NULL)) {
ENGINE_free(engine_pkcs11);
return NULL;
}
}
if(!ENGINE_init(engine_pkcs11)) {
ENGINE_free(engine_pkcs11);
return NULL;
}
if(!ENGINE_set_default(engine_pkcs11, ENGINE_METHOD_ALL)) {
ENGINE_free(engine_pkcs11);
return NULL;
}
return engine_pkcs11;
}
void UnloadEngine(ENGINE* engine_pkcs11)
{
ENGINE_finish(engine_pkcs11);
ENGINE_free(engine_pkcs11);
}
/* создает объект расширения сертификата */
X509_EXTENSION*
create_X509_extension
(
char* name,
char *value
)
{
X509_EXTENSION *ex;
ex = X509V3_EXT_conf(NULL, NULL, name, value);
if (!ex)
return NULL;
return ex;
}
X509_REQ* create_request(
EVP_PKEY* pKey,
const char* common_name, /* понятное имя субъекта */
const char* org, /* организация */
const char* org_unit, /* подразделение организации */
const char* city, /* город */
const char* region, /* регион */
const char* country, /* страна */
const char* email, /* почтовый адрес */
const char* keyUsages, /* способы использования ключа, через , */
const char* extendedKeyUsages /* расширенные способы использования ключа, через ; */
)
{
X509_REQ* req = NULL;
X509_NAME* subject = NULL;
BOOL bGoodEmail = TRUE;
subject = X509_NAME_new();
/* добавляем CN */
if(common_name && strlen(common_name)>0) {
if(!X509_NAME_add_entry_by_NID(
subject, NID_commonName,
MBSTRING_UTF8, (unsigned char*)common_name,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
}
/* добавляем название организации */
if(org && strlen(org)>0)
if(!X509_NAME_add_entry_by_NID(
subject, NID_organizationName,
MBSTRING_UTF8, (unsigned char*)org,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
/* добавляем название подразделения */
if(org_unit && strlen(org_unit)>0)
if(!X509_NAME_add_entry_by_NID(
subject, NID_organizationalUnitName,
MBSTRING_UTF8, (unsigned char*)org_unit,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
/* добавляем locality */
if(city && strlen(city)>0)
if(!X509_NAME_add_entry_by_NID(
subject, NID_localityName,
MBSTRING_UTF8, (unsigned char*)city,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
if(region && strlen(region)>0)
if(!X509_NAME_add_entry_by_NID(
subject, NID_stateOrProvinceName,
MBSTRING_UTF8, (unsigned char*)region,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
if(country && strlen(country)>0)
if(!X509_NAME_add_entry_by_NID(
subject, NID_countryName,
MBSTRING_UTF8, (unsigned char*)country,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
if(email && strlen(email)>0) {
for (int i=0; i<strlen(email); i++)
if (email[i]&0x80) {
bGoodEmail=FALSE;
break;
}
if(bGoodEmail) {
if(!X509_NAME_add_entry_by_NID(
subject, NID_pkcs9_emailAddress,
MBSTRING_UTF8, (unsigned char*)email,
-1, -1, 0)) {
X509_NAME_free(subject);
return NULL;
}
}
}
req=X509_REQ_new();
if(!req) {
X509_NAME_free(subject);
return NULL;
}
/* установка версии */
if(!X509_REQ_set_version(req, 0)) {
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
/* установка subject */
if(!X509_REQ_set_subject_name(req, subject)) {
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
/* установка открытого ключа */
if(!X509_REQ_set_pubkey(req, pKey)) {
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
/* установка keyUsage */
X509_EXTENSION* keyUsageExt =
create_X509_extension("keyUsage", (char*)keyUsages);
if(!keyUsageExt) {
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
/* установка extendedkeyUsage */
X509_EXTENSION* extendedKeyUsageExt =
create_X509_extension("extendedKeyUsage", (char*)extendedKeyUsages);
if(!extendedKeyUsageExt) {
X509_EXTENSION_free(keyUsageExt);
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
STACK_OF(X509_EXTENSION)* extension_stack =
sk_X509_EXTENSION_new_null();
if(!extension_stack) {
X509_EXTENSION_free(extendedKeyUsageExt);
X509_EXTENSION_free(keyUsageExt);
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
sk_X509_EXTENSION_push(extension_stack, keyUsageExt);
sk_X509_EXTENSION_push(extension_stack, extendedKeyUsageExt);
if(!X509_REQ_add_extensions(req, extension_stack)) {
X509_EXTENSION_free(extendedKeyUsageExt);
X509_EXTENSION_free(keyUsageExt);
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
/* подпись заявки закрытым ключом */
if(!X509_REQ_sign(req, pKey, EVP_get_digestbyname("md_gost94"))) {
X509_EXTENSION_free(extendedKeyUsageExt);
X509_EXTENSION_free(keyUsageExt);
X509_REQ_free(req);
X509_NAME_free(subject);
return NULL;
}
sk_X509_EXTENSION_pop_free
(extension_stack,X509_EXTENSION_free);
X509_NAME_free(subject);
return req;
}
int main(int argc, char* argv[])
{
BIO* bio_req=NULL;
BIO* bio_key=NULL;
X509_REQ* pRequest=NULL;
BUF_MEM* pbmReq;
char* cRequest=NULL;
UI_METHOD* uim=NULL;
int reason=0;
EVP_PKEY* key1=NULL;
EVP_PKEY* newkey=NULL;
EVP_PKEY_CTX* ctx=NULL;
ENGINE* engine_pkcs11=NULL;
if(!argv[1] || !argv[2]) {
fprintf(stderr, "Bad parameters!");
return 1;
}
/* инициализируем OpenSSL */
ENGINE_load_builtin_engines();
OPENSSL_add_all_algorithms_noconf();
/* загружаем engine, реализующую работу с
Рутокен ЭЦП через библиотеку PKCS#11 */
engine_pkcs11=LoadEngine(
"pkcs11_gost",
"rtPKCS11ECP.dll",
NULL);
if(!engine_pkcs11)
return 1;
key1=EVP_PKEY_new();
if(!key1) {
UnloadEngine(engine_pkcs11);
return 1;
}
/* тип ключа ГОСТ Р 34.10-2001 */
if(!EVP_PKEY_set_type(key1, GOST2001_KEY_TYPE)) {
EVP_PKEY_free(key1);
UnloadEngine(engine_pkcs11);
return 1;
}
ctx=EVP_PKEY_CTX_new(key1, NULL);
if(!ctx) {
EVP_PKEY_free(key1);
UnloadEngine(engine_pkcs11);
return 1;
}
if(!EVP_PKEY_keygen_init(ctx)) {
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
/* параметры ключа */
if(!EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A")) {
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
/* ID ключа */
if(!EVP_PKEY_CTX_ctrl_str(ctx, "slot_key_id", argv[1])) {
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
/* PIN-код токена */
if(!EVP_PKEY_CTX_ctrl_str(ctx, "pin", argv[2])) {
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
/* генерация ключа */
if(!EVP_PKEY_keygen(ctx,&newkey)) {
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
/* создание запроса на сертификат */
pRequest=create_request(
newkey,
"tester",
"rutoken",
"development",
"Moscow",
"Moscow",
"ru",
"tester@rutoken.ru",
"digitalSignature,keyEncipherment",
"clientAuth,emailProtection");
if(!pRequest) {
EVP_PKEY_free(newkey);
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
bio_req=BIO_new(BIO_s_mem());
if(!bio_req) {
X509_REQ_free(pRequest);
EVP_PKEY_free(newkey);
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
if(!PEM_write_bio_X509_REQ(bio_req, pRequest)) {
BIO_free(bio_req);
X509_REQ_free(pRequest);
EVP_PKEY_free(newkey);
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
BIO_get_mem_ptr(bio_req, &pbmReq);
if(!pbmReq) {
BIO_free(bio_req);
X509_REQ_free(pRequest);
EVP_PKEY_free(newkey);
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 1;
}
pbmReq->data[pbmReq->length]='\0';
fprintf(stdout, "%s", pbmReq->data);
BIO_free(bio_req);
bio_req=NULL;
X509_REQ_free(pRequest);
EVP_PKEY_free(newkey);
EVP_PKEY_CTX_free(ctx);
UnloadEngine(engine_pkcs11);
return 0;
}