Вопросы о GnuPG

Вопросы о GnuPG Сертификаты

Rsa (алгоритм работы)

Алгоритм работает довольно просто. Мы просто берем два простых числа P и Q.

Умножим их друг на друга и в результате получим N.

Потом выберем случайное число D. Оно должно быть взаимно простым с (P-1)*(Q-1).

И определим число E, для которого будет истиной (E*D) mod ((P-1)*(Q-1))=1

И у нас открытый ключ это числа E и N, а приватный – числа D и N

Теперь, чтоб нам зашифровать что-нибудь нашим открытым ключом {E,N} нам надо разбить наш шифруемый текст на блоки, каждый из которых можно представить в виде числа M(I)=0,1,2…, N-1

И зашифровать, как последовательность M(I) по формуле C(I)=(M(I)^e)mod N.

А чтобы потом расшифровать это, просто используем секретный ключ {D,N}, по формуле M(I) = (C(I)^D) mod N

В результате получим исходный текст.

Алгоритм RSA он один, но мы можем использовать ключ разной длины. Когда ты создаешь RSA пару, то ты указываешь длину ключа в битах. Эта длина определяет количество бит в модуле.

Короче, для того чтобы хранить ваши данные в безопасности в течение двух десятилетий рекомендуется использовать RSA2048.

Два десятилетия это потому, что у нас до сих пор существует закон Мура. Некоторые говорят, что он уже не работает, вроде это говорил директор NVidia. Но не знаю откуда у него такая информация. По информации всех остальных людей в этом мире закон Мура работает и будет работать дальше.

И фактически сейчас взломать RSA2048 невозможно. Но через 20 лет это будет возможно.

Вы можете спросить, а почему не взять просто самый большой из возможных.

И с точки зрения безопасности естественно лучше выбирать тот, который больше. Так как он будет наиболее безопасным. И причем тут зависимость не линейная.

То есть 2048 не в 2 раза безопаснее чем 1024. А во много раз. И так же 4096 во много раз безопаснее чем 2048.

Но при этом сам процесс шифрования и процесс расшифровки он у нас естественно требует нашего с вами процессорного времени. И тут тоже большая разница. Каждое увеличение ключа в два раза увеличивает процессорное время примерно в 6-7 раз. Ну вот условно если ваш процессор будет расшифровывать rsa1024 одну минуту. То rsa2048 он будет расшифровывать 7 минут. А RSA4096 он уже будет расшифровывать 49 минут.

Инфраструктура открытых ключей: gnupg/smime и токены pkcs#11 с поддержкой российской криптографии

Вопросы о GnuPG

Неумолимо приближается час «Ч»: «использование схемы подписи ГОСТ Р 34.10-2001 для формирования подписи после 31 декабря 2021 года не допускается!». И вот, наконец, 16 июля 2021 г. на

сайте

Федерального ситуационного центра электронного правительства появилось

Уведомление

о начале выпуска сертификатов ключей проверки электронных подписей подчиненных удостоверяющих центров на головном удостоверяющем центре в соответствии с ГОСТ Р 34.10-2021. Правда, все равно не ясно когда же прекратится выпуск старых сертификатов. Но «процесс пошел» и это радует.

Введение в обиход новой электронной подписи предполагает наличие средств не только по выпуску сертификатов по ГОСТ Р 34.10-2021, но и наличие средств формирования и проверки этой подписи. Об одном из таких средств можно на страницах habr уже рассказывалось

Вопросы о GnuPG

Электронная подпись активно используется не только в электронном документообороте (ЭДО), но и при электронной переписке (email):

Вопросы о GnuPG

Если говорить об OpenSource проектах в области ИОК на базе сертификатов X509, то наряду с Network Security Services (NSS), OpenSSL, широкой популярностью пользуется проект GnuPG/SMIME. Криптографическим ядром данного проекта является библиотека LibGCrypt. Существенным при этом является то, что эта библиотека поддерживает криптоалгоритмы ГОСТ Р 34.10-2021 и ГОСТ Р 34.11-2021 (STRIBOG256 и STRIBOG512) и не требует никакой доработки, по крайней мере, для функций формирования электронной подписи и ее проверки. Для разбора, манипулирования объектами ИОК (сертификаты, запросы на сертификаты, электронная подпись и т.п.) в GnuPG используется библиотека LibKSBA. В отличии от LibGCrypt в библиотеке libksba не реализованы рекомендации ТК-26 для криптографических сообщений формата CMS. В статье показана модернизация библиотеки libksba, связанная с разбором сертификатов X509 и сообщений CMS (PKCS7), что позволило обеспечить проверку электронной подписи сертификатов и электронной подписи документов PKCS#7. Сегодня мы пойдем дальше и добавим в библиотеку функционал для формирования CMS формата PKCS#7. Это позволит обеспечить поддержку электронной подписи по ГОСТ Р 34.10-2001.2021 (после определенной модернизации самого проекта GnuPG) и в модуле GnuPG/SM.

Вопросы о GnuPGА в конечном итоге, окажется возможным использование отечественной электронной подписи как в почтовом клиенте KMail (кстати говоря, и во многих других почтовых клиентах), так и в утилите Kleopatra как для проверки электронной подписи, так и ее формирования для различных файлов:

Вопросы о GnuPG

imageТеперь о главном. В дальнейшем будем исходим из того, что пользователи хранят свои личные сертификаты (закрытый ключ и сертификат ключа проверки электронной подписи) на токенах/смарткартах PKCS#11 с реализацией на них по крайней мере функций формирования электронной подписи (механизмы CKM_GOSTR3410 для формирования ЭП по ГОСТ Р 34.10-2001/2021 с длиной ключа 256 бит и/или CKM_GOSTR3410_512 для формирования ЭП по ГОСТ Р 34.10-2021 с длиной ключа 512 бит). Оптимальный вариант это когда функционал токена/смаркарты поддерживает стандарт PKCS#11 v.2.40.

Для доступа к токенам PKCS#11 в GnuPG используется утилита gnupg_pkcs11_scd. В наших примерах используется утилита gnupg_pkcs11_csd версии 0.9.1. Главной задачей этой утилиты (помимо организации выполнения криптографических функций PKCS#11, например, подписать хэш), является разбор сертификатов, хранящихся на токене, получение информации об отрытом ключе в виде S-выражения и передача ее вверх по цепочке. Это операцию выполняет функция keyutil_get_cert_sexp, которая находится в файле keyutil.c. К сожалению, эта утилита заточена только на работу с RSA-сертификатами. Но поскольку в нашем распоряжении имеется библиотека libksba да еще доработанная для работы ГОСТ-сертификатами, то оказалось на удивление просто переписать функцию keyutil_get_cert_sexp для обработки любых сертификатов, поддерживаемых библиотекой libksba:

/*
   Convert X.509 RSA/ECC/DSA/GOST public key into gcrypt internal sexp form. The resul is stored 
   in *sexp, which must be freed (using ) when not needed anymore. *sexp must be
   NULL on entry, since it is overwritten.
*/
gpg_err_code_t
keyutil_get_cert_sexp (
	unsigned char *der,
	size_t len,
	gcry_sexp_t *p_sexp
) {
	gpg_err_code_t error = GPG_ERR_GENERAL;
	gcry_mpi_t n_mpi = NULL;
	gcry_mpi_t e_mpi = NULL;
	gcry_sexp_t sexp = NULL;
	gpg_error_t err;
	ksba_sexp_t p;
	ksba_cert_t ks_cert;
	size_t n;
        err = ksba_cert_new (&ks_cert);
	if (err) {
	    error = GPG_ERR_BAD_KEY;
	    goto cleanup;
	}
	err = ksba_cert_init_from_mem (ks_cert, der, len);
	if (err) {
	    error = GPG_ERR_BAD_KEY;
	    goto cleanup;
	}
  /* Get the public key from the certificate.  */
	p = ksba_cert_get_public_key (ks_cert);
	n = gcry_sexp_canon_len (p, 0, NULL, NULL);
	if (!n)
	{
    	    ksba_free (p);
	    error = GPG_ERR_BAD_KEY;
	    goto cleanup;
	}
	err = gcry_sexp_sscan ( p_sexp, NULL, p, n);
	if (err) {
	    error = GPG_ERR_BAD_KEY;
	    goto cleanup;
	}
	ksba_free (p);
	error = GPG_ERR_NO_ERROR;
	goto cleanup;
#if 0
	if (
		(error = keyutil_get_cert_mpi (
			der,
			len,
			&n_mpi,
			&e_mpi
		)) != GPG_ERR_NO_ERROR
	) {
		goto cleanup;
	}
	if (
		gcry_sexp_build (
			&sexp,
			NULL,
			"(public-key (rsa (n %m) (e %m)))",
			n_mpi,
			e_mpi
		)
	) {
		error = GPG_ERR_BAD_KEY;
		goto cleanup;
	}
	*p_sexp = sexp;
	sexp = NULL;
	error = GPG_ERR_NO_ERROR;
#endif
cleanup:
	if (n_mpi != NULL) {
		gcry_mpi_release (n_mpi);
		n_mpi = NULL;
	}
	if (e_mpi != NULL) {
		gcry_mpi_release (e_mpi);
		e_mpi = NULL;
	}
	if (sexp != NULL) {
		gcry_sexp_release (sexp);
		sexp = NULL;
	}
	return error;
}

Оригинальный код заключен в блок:

#if 0
…
#endif

Полный патч для утилиты gnupg_pkcs11_scd находится здесь
diff -u PKCS11_CSD_ORIG/command.c PKCS11_CSD/command.c
--- PKCS11_CSD_ORIG/command.c	2021-07-19 16:25:20.778692021  0300
    PKCS11_CSD/command.c	2021-07-19 16:25:10.238691435  0300
@@ -1002,6  1002,8 @@
 		{ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
 		0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
 		0x00, 0x04, 0x40  };
 	CK_MECHANISM_TYPE mech_type = CKM_RSA_PKCS;
 	int pubkey_type;
 
 	gpg_err_code_t error = GPG_ERR_GENERAL;
 	pkcs11h_certificate_id_t cert_id = NULL;
@@ -1021,8  1023,18 @@
 		INJECT_SHA224,
 		INJECT_SHA256,
 		INJECT_SHA384,
-		INJECT_SHA512
 		INJECT_SHA512,
 /*For GOST*/
 		INJECT_GOSTR3411_CP,
 		INJECT_STRIBOG256,
 		INJECT_STRIBOG512
 	} inject = INJECT_NONE;
 #define NSSCK_VENDOR_PKCS11_RU_TEAM 0xd4321000 //0x80000000|0x54321000
 #define NSSCK_VENDOR_PKSC11_RU_TEAM NSSCK_VENDOR_PKCS11_RU_TEAM
 #define CK_VENDOR_PKCS11_RU_TEAM_TC26 NSSCK_VENDOR_PKCS11_RU_TEAM
 #define CKM_GOSTR3410                0x00001201
 // TC 26
 #define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TC26 |0x006)
 
 	if (data->data == NULL) {
 		error = GPG_ERR_INV_DATA;
@@ -1107,6  1119,16 @@
 		else if (!strcmp(hash, "sha512") && data->size == 0x40) {
 			inject = INJECT_SHA512;
 		}
 		else if (!strcmp(hash, "gost3411_94") || !strcmp(hash, "gost3411_CP") || !strcmp(hash, "gostr3411_94") || !strcmp(hash, "gostr3411_CP")) {
 			inject = INJECT_GOSTR3411_CP;
 		}
 		else if (!strcmp(hash, "gost3411_12_256") || !strcmp(hash, "gostr3411_12_256") || !strcasecmp(hash, "stribog256")) {
 			inject = INJECT_STRIBOG256;
 		}
 		else if (!strcmp(hash, "gost3411_12_512") || !strcmp(hash, "gostr3411_12_512") || !strcasecmp(hash, "stribog512")) {
 			inject = INJECT_STRIBOG512;
 		}
 
 		else {
 			common_log (LOG_DEBUG, "unsupported hash algo (hash=%s,size=%d)", hash, data->size);
 			error = GPG_ERR_UNSUPPORTED_ALGORITHM;
@@ -1169,6  1191,23 @@
 				oid = sha512_prefix;
 				oid_size = sizeof(sha512_prefix);
 			break;
 /*Механизмы ГОСТ*/
 			case INJECT_GOSTR3411_CP:
 				oid = "";
 				oid_size = 0;
 				mech_type = CKM_GOSTR3410;
 			break;
 			case INJECT_STRIBOG256:
 				oid = "";
 				oid_size = 0;
 				mech_type = CKM_GOSTR3410;
 			break;
 			case INJECT_STRIBOG512:
 				oid = "";
 				oid_size = 0;
 				mech_type = CKM_GOSTR3410_512;
 			break;
 
 			default:
 				error = GPG_ERR_INV_DATA;
 				goto cleanup;
@@ -1232,7  1271,7 @@
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_signAny (
 				cert,
-				CKM_RSA_PKCS,
 				mech_type,
 				_data->data,
 				_data->size,
 				NULL,
@@ -1252,7  1291,7 @@
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_signAny (
 				cert,
-				CKM_RSA_PKCS,
 				mech_type,
 				_data->data,
 				_data->size,
 				sig,
@@ -1298,6  1337,9 @@
 	return gpg_error (error);
 }
 
 /*для ГОСТ*/
 #define CKM_GOSTR3410_KEY_WRAP       0x00001203
 
 /** Decrypt data (set by SETDATA) with certificate id in line. */
 gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
 {
@@ -1309,6  1351,7 @@
 	int session_locked = 0;
 	cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx);
 	cmd_data_t _data;
 	CK_MECHANISM_TYPE mech_type;
 
 	if (
 		data == NULL ||
@@ -1317,6  1360,13 @@
 		error = GPG_ERR_INV_DATA;
 		goto cleanup;
 	}
 	if(memmem(data->data, data->size,"x2Ax85x03", 3)){
 /*Если ГОСТ*/
 	    mech_type = CKM_GOSTR3410_KEY_WRAP;
 	}
 	else{
 	    mech_type = CKM_RSA_PKCS;
 	}
 
 	/*
 	 * Guess.. taken from openpgp card implementation
@@ -1376,7  1426,7 @@
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_decryptAny (
 				cert,
-				CKM_RSA_PKCS, 
 				mech_type,
 				_data.data,
 				_data.size,
 				NULL,
@@ -1396,7  1446,7 @@
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_decryptAny (
 				cert,
-				CKM_RSA_PKCS, 
 				mech_type,
 				_data.data,
 				_data.size,
 				ptext,
@@ -1591,6  1641,29 @@
 			goto cleanup;
 		}
 	}
 	else if (!strcmp (line, "APPTYPE")) {
 		if (
 			(error = assuan_write_status(
 				ctx,
 				"APPTYPE",
 				"NKS"
 			)) != GPG_ERR_NO_ERROR
 		) {
 			goto cleanup;
 		}
 	}
 	else if (!strcmp (line, "NKS-VERSION")) {
 		if (
 			(error = assuan_write_status(
 				ctx,
 				"NKS-VERSION",
 				"3"
 			)) != GPG_ERR_NO_ERROR
 		) {
 			goto cleanup;
 		}
 	}
 
 	else if (!strcmp (line, "KEY-ATTR")) {
 		int i;
 		for (i=0;i<3;i  ) {
diff -u PKCS11_CSD_ORIG/keyutil.c PKCS11_CSD/keyutil.c
--- PKCS11_CSD_ORIG/keyutil.c	2021-07-19 16:25:20.779692021  0300
    PKCS11_CSD/keyutil.c	2021-07-19 16:51:48.934779338  0300
@@ -45,6  45,7 @@
 typedef const unsigned char *my_openssl_d2i_t;
 #endif
 #endif
 #include <ksba.h>
 
 gpg_err_code_t
 keyutil_get_cert_mpi (
@@ -193,10  194,10 @@
 
 	return error;
 }
-/**
-   Convert X.509 RSA public key into gcrypt internal sexp form. Only RSA
-   public keys are accepted at the moment. The resul is stored in *sexp,
-   which must be freed (using ) when not needed anymore. *sexp must be
 
 /*
    Convert X.509 RSA/ECC/DSA/GOST public key into gcrypt internal sexp form. The resul is stored 
    in *sexp, which must be freed (using ) when not needed anymore. *sexp must be
    NULL on entry, since it is overwritten.
 */
 gpg_err_code_t
@@ -210,6  211,40 @@
 	gcry_mpi_t e_mpi = NULL;
 	gcry_sexp_t sexp = NULL;
 
 	gpg_error_t err;
 	ksba_sexp_t p;
 	ksba_cert_t ks_cert;
 	size_t n;
 
         err = ksba_cert_new (&ks_cert);
 	if (err) {
 	    error = GPG_ERR_BAD_KEY;
 	    goto cleanup;
 	}
 	err = ksba_cert_init_from_mem (ks_cert, der, len);
 	if (err) {
 	    error = GPG_ERR_BAD_KEY;
 	    goto cleanup;
 	}
   /* Get the public key from the certificate.  */
 	p = ksba_cert_get_public_key (ks_cert);
 	n = gcry_sexp_canon_len (p, 0, NULL, NULL);
 	if (!n)
 	{
     	    ksba_free (p);
 	    error = GPG_ERR_BAD_KEY;
 	    goto cleanup;
 	}
 	err = gcry_sexp_sscan ( p_sexp, NULL, p, n);
 	if (err) {
 	    error = GPG_ERR_BAD_KEY;
 	    goto cleanup;
 	}
 	ksba_free (p);
 	error = GPG_ERR_NO_ERROR;
 	goto cleanup;
 #if 0
 
 	if (
 		(error = keyutil_get_cert_mpi (
 			der,
@@ -237,6  272,7 @@
 	*p_sexp = sexp;
 	sexp = NULL;
 	error = GPG_ERR_NO_ERROR;
 #endif
 
 cleanup:

Про сертификаты:  Сертификация комбикормов для животных — Оптиматест

С учетом этого патча для сборки утилиты gnupg_pkcs11_scd используется следующий скрипт:

export LIBS=" -lksba"
#./configure  --without-gnutls
./configure  --without-openssl
make

Этот патч в итоге позволит использовать ГОСТ-токены (в том числе сертифицированные ФСБ России), по крайней мере, для формирования электронной подписи.

Не будем останавливаться на доработках библиотеки libksba, а просто приведем

полный патч для libksba
diff -u KSBA_ORIG/cms.c KSBA/cms.c
--- KSBA_ORIG/cms.c	2021-03-15 23:26:38.000000000  0400
    KSBA/cms.c	2021-07-19 08:24:48.774106713  0300
@@ -1581,6  1581,15 @@
 
   Note that IDX is only used for consistency checks.
  */
 /* For GOST
   r_sig  = (sig-val
  	      (gost
  		(r <mpi>)
  		(s <mpi>)
  	      )
  	      (hash <name_hash>))
 */
 
 gpg_error_t
 ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
 {
@@ -1588,6  1597,7 @@
   unsigned long n;
   struct sig_val_s *sv, **sv_tail;
   int i;
   int gost;
 
   if (!cms)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -1615,6  1625,11 @@
   /* Break out the algorithm ID. */
   if (!(n = snext (&s)))
     return gpg_error (GPG_ERR_INV_SEXP);
   gost = 0;
   if (n==4 && s[0] == 'g' && s[1] == 'o' && s[2] == 's' && s[3] == 't') {
    /* kludge to allow "gost" to be passed as algorithm name */
       gost = 1;
   }
 
   sv = xtrycalloc (1, sizeof *sv);
   if (!sv)
@@ -1680,6  1695,11 @@
       s  ;
       n--;
     }
 if(gost){
   sv->value = xtrymalloc (n * 2);
 }
 else
 
   sv->value = xtrymalloc (n);
   if (!sv->value)
     {
@@ -1687,6  1707,11 @@
       xfree (sv);
       return gpg_error (GPG_ERR_ENOMEM);
     }
 /*r и s в подписи меняем местами - ТК-26*/
 if(gost == 1)
   memcpy (sv->value   n, s, n);
 else
 
   memcpy (sv->value, s, n);
   sv->valuelen = n;
   s  = n;
@@ -1698,6  1723,84 @@
       return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
     }
   s  ;
 if(gost == 1){
     unsigned char sh[30];
 
   if (*s != '(')
     {
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
     }
   s  ;
   if (!(n = snext (&s)))
     {
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (GPG_ERR_INV_SEXP);
     }
 
   s  = n; /* ignore the name of the parameter */
 
   if (!digitp(s))
     {
       xfree (sv->algo);
       xfree (sv);
       /* May also be an invalid S-EXP.  */
       return gpg_error (GPG_ERR_UNKNOWN_SEXP);
     }
   if (!(n = snext (&s)))
     {
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (GPG_ERR_INV_SEXP);
     }
 
   if (n > 1 && !*s)
     { /* We might have a leading zero due to the way we encode
          MPIs - this zero should not go into the OCTECT STRING.  */
       s  ;
       n--;
     }
 /*r и s в подписи меняем местами - ТК-26*/
   memcpy (sv->value, s, n);
   sv->valuelen  = n;
   s  = n;
 
   if ( *s != ')' && s[1] != ')' &&  s[2] != '(' )
     {
       xfree (sv->value);
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
     }
   s  ;s  ;s  ;
   if (!(n = snext (&s)))
     {
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (GPG_ERR_INV_SEXP);
     }
    s  = n;
   if (!(n = snext (&s)))
     {
       xfree (sv->algo);
       xfree (sv);
       return gpg_error (GPG_ERR_INV_SEXP);
     }
   strncpy(sh, s, n);
   if(!strcmp(sh, "1.2.643.7.1.1.2.2") || !strcasecmp(sh, "stribog256"))
         sv->algo = xtrystrdup ("1.2.643.7.1.1.1.1");
   else if(!strcmp(sh, "1.2.643.7.1.1.2.3") || !strcasecmp(sh, "stribog512"))
         sv->algo = xtrystrdup ("1.2.643.7.1.1.1.2");
   else if(!strcmp(sh, "1.2.643.2.2.9") || !strcasecmp(sh, "gostr3411_CP"))
         sv->algo = xtrystrdup ("1.2.643.2.2.19");
   else {
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
   }
    s  = n;
 
 }
   /* fixme: end loop over parameters */
 
   /* we need 2 closing parenthesis */
diff -u KSBA_ORIG/dn.c KSBA/dn.c
--- KSBA_ORIG/dn.c	2021-08-22 11:40:58.000000000  0300
    KSBA/dn.c	2021-06-26 19:24:32.000000000  0300
@@ -48,6  48,7 @@
                  2 = David Chadwick, July 2003
                  <draft-ietf-pkix-dnstrings-02.txt>
                  3 = Peter Gutmann
                  4 = tk26
               */
   const char *description;
   size_t      oidlen;
@@ -74,12  75,17 @@
     "x09x92x26x89x93xF2x2Cx64x01x19", "0.9.2342.19200300.100.1.25" },
 {"UID", 1, "userid",              10,
     "x09x92x26x89x93xF2x2Cx64x01x01", "0.9.2342.19200300.100.1.1 " },
-{"EMAIL", 3, "emailAddress",       9,
 {"E", 1, "emailAddress",       9,
     "x2Ax86x48x86xF7x0Dx01x09x01",     "1.2.840.113549.1.9.1" },
 /*oid-ы квалифицированного сертификата от TK-26*/
 {"OGRN", 4, "OGRN",             5, "x2ax85x03x64x01", "1.2.643.100.1" },
 {"INN", 4, "INN",             8, "x2ax85x03x03x81x03x01x01", "1.2.643.3.131.1.1" },
 {"SNILS", 4, "SNILS",             5, "x2ax85x03x64x03", "1.2.643.100.3" },
 {"OGRNIP", 4, "OGRNIP",             5, "x2ax85x03x64x05", "1.2.643.100.5" },
 
 { NULL }
 };
 
-
 #define N 0x00
 #define P 0x01
 static unsigned char charclasses[128] = {
@@ -555,8  561,8 @@
   name = NULL;
   for (i=0; oid_name_tbl[i].name; i  )
     {
-      if (oid_name_tbl[i].source == 1
-          && node->len == oid_name_tbl[i].oidlen
 /*Все oid-ы из DN переводим в текстовую форму*/
       if (node->len == oid_name_tbl[i].oidlen
           && !memcmp (image node->off node->nhdr,
                       oid_name_tbl[i].oid, node->len))
         {
@@ -604,6  610,9 @@
     case TYPE_UTF8_STRING:
       append_utf8_value (image node->off node->nhdr, node->len, sb);
       break;
 /*Добавляем обработку NUMERIC_STRING*/
     case TYPE_NUMERIC_STRING:
 
     case TYPE_PRINTABLE_STRING:
     case TYPE_IA5_STRING:
       /* we assume that wrong encodings are latin-1 */
diff -u KSBA_ORIG/keyinfo.c KSBA/keyinfo.c
--- KSBA_ORIG/keyinfo.c	2021-10-28 13:41:48.000000000  0300
    KSBA/keyinfo.c	2021-07-19 09:03:27.936234230  0300
@@ -45,7  45,6 @@
 #include "convert.h"
 #include "ber-help.h"
 
-
 /* Constants used for the public key algorithms.  */
 typedef enum
   {
@@ -98,6  97,19 @@
     "1.2.840.10045.2.1", /*  ecPublicKey */
     "x2ax86x48xcex3dx02x01", 7,
     1, PKALGO_ECC, "ecc", "q", "x80" },
 /*oid-ы ГОСТ-овых ключей*/
   { /* GOST3410-2001 */
     "1.2.643.2.2.19", /*  gostPublicKey-2001 */
     "x2ax85x03x02x02x13", 6,
     1, PKALGO_ECC, "ecc", "q", "x80" },
   { /* GOST3410-2021-256 */
     "1.2.643.7.1.1.1.1", /*  gostPublicKey-2021-256 */
     "x2ax85x03x07x01x01x01x01", 8,
     1, PKALGO_ECC, "ecc", "q", "x80" },
   { /* GOST3410-2021-512 */
     "1.2.643.7.1.1.1.2", /*  gostPublicKey-2021-512 */
     "x2ax85x03x07x01x01x01x02", 8,
     1, PKALGO_ECC, "ecc", "q", "x80" },
 
   {NULL}
 };
@@ -209,6  221,31 @@
     "1.3.36.3.4.3.2.2",     /* sigS_ISO9796-2rndWithrsa_ripemd160 */
     "x2Bx24x03x04x03x02x02", 7,
     0, PKALGO_RSA, "rsa", "s", "x82", NULL, NULL, "rmd160" },
   { /* GOST3410-2001 */
     "1.2.643.2.2.19", /*  gostPublicKey-2001 */
     "x2ax85x03x02x02x13", 6,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "gostr3411_CP" },
   { /* GOST3410-2021-256 */
     "1.2.643.7.1.1.1.1", /*  gostPublicKey-2021-256 */
     "x2ax85x03x07x01x01x01x01", 8,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "stribog256"},
   { /* GOST3410-2021-512 */
     "1.2.643.7.1.1.1.2", /*  gostPublicKey-2021-512 */
     "x2ax85x03x07x01x01x01x02", 8,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "stribog512"},
 
   { /* GOST3411-2021-256 */
     "1.2.643.7.1.1.3.2", /*  STRIBOG256 */
     "x2ax85x03x07x01x01x03x02", 8,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "stribog256" },
   { /* GOST3411-2021-512 */
     "1.2.643.7.1.1.3.3", /*  STRIBOG512 */
     "x2ax85x03x07x01x01x03x03", 8,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "stribog512" },
   { /* GOST3410-2001-Signature */
     "1.2.643.2.2.3", /*  gosrPublicKey-2001 avec signature */
     "x2ax85x03x02x02x03", 6,
     1, PKALGO_ECC, "gost", "s", "x80", NULL, NULL, "gostr3411_CP" },
 
   {NULL}
 };
@@ -218,6  255,20 @@
     "1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */
     "x2Ax86x48x86xF7x0Dx01x01x01", 9,
     1, PKALGO_RSA, "rsa", "a", "x82" },
 /*oid-ы ГОСТ-ых ключей для ассиметричного шифрования*/
   {
     "1.2.643.2.2.19", /*GOST R34.10-2001 */
     "x2Ax85x03x02x02x13", 6,
     1, PKALGO_ECC, "ecc", "a", "x80" },
   {
     "1.2.643.7.1.1.1.1", /*GOST R34.10-2021-256 */
     "x2Ax85x03x07x01x01x01x01", 8,
     1, PKALGO_ECC, "ecc", "a", "x80" },
   {
     "1.2.643.7.1.1.1.2", /*GOST R34.10-2021-512 */
     "x2Ax85x03x07x01x01x01x02", 8,
     1, PKALGO_ECC, "ecc", "a", "x80" },
 
   {NULL}
 };
 
@@ -267,6  318,13 @@
     { "1.2.643.2.2.35.1",    "GOST2001-CryptoPro-A" },
     { "1.2.643.2.2.35.2",    "GOST2001-CryptoPro-B" },
     { "1.2.643.2.2.35.3",    "GOST2001-CryptoPro-C" },
 /*дополнительные oid-ы точек эллиптической кривой для ГОСТ Р 34.10-2001/2021*/
 //    "GOST2001-CryptoPro-XchA" 
     { "1.2.643.2.2.36.0", "GOST2001-CryptoPro-A" }, 
 //    "GOST2001-CryptoPro-XchB" 
     { "1.2.643.2.2.36.1", "GOST2001-CryptoPro-C" }, 
 
 
     { "1.2.643.7.1.2.1.2.1", "GOST2021-tc26-A"      },
     { "1.2.643.7.1.2.1.2.2", "GOST2021-tc26-B"      },
 
@@ -393,7  451,8 @@
   /* get the object identifier */
   if (!derlen)
     return gpg_error (GPG_ERR_INV_KEYINFO);
-  c = *der  ; derlen--;
   c = *der  ;
    derlen--;
   if ( c != 0x06 )
     return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not an OBJECT IDENTIFIER */
   TLV_LENGTH(der);
@@ -418,6  477,7 @@
       if (!derlen)
         return gpg_error (GPG_ERR_INV_KEYINFO);
       c = *der  ; derlen--;
 
       if ( c == 0x05 )
         {
           /*printf ("parameter: NULL n"); the usual case */
@@ -471,6  531,7 @@
       else
         {
 /*            printf ("parameter: with tag x - ignoredn", c); */
 
           TLV_LENGTH(der);
           seqlen -= der - startparm;
           /* skip the value */
@@ -692,6  753,8 @@
   const unsigned char *ctrl;
   const char *elem;
   struct stringbuf sb;
   int gost_key;
   char *parm_oid_hash = NULL;
 
   *r_string = NULL;
 
@@ -701,6  764,7 @@
   c = *der  ; derlen--;
   if ( c != 0x30 )
     return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */
 
   TLV_LENGTH(der);
   /* and now the inner part */
   err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr,
@@ -715,13  779,36 @@
            && !memcmp (der off, pk_algo_table[algoidx].oid, len))
         break;
     }
 
   if (!pk_algo_table[algoidx].oid)
     return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
   if (!pk_algo_table[algoidx].supported)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 /*Определяем тип ключа 1 - ГОСТ-овый ключ*/
   gost_key = !memcmp(pk_algo_table[algoidx].oidstring, "1.2.643", 7);
 
   if (parm_off && parm_len && parm_type == TYPE_OBJECT_ID)
     parm_oid = ksba_oid_to_str (der parm_off, parm_len);
   else 
 /*Извлекаем параметры ГОСТ-ового ключа*/
   if (parm_off && parm_len && parm_type == TYPE_SEQUENCE && gost_key && (*(der parm_off   off - 2) == TYPE_OBJECT_ID)){
 /*Вытаскиваем oid curve для ГОСТ-ключа*/
     int len_hash;
     int len_curve;
     unsigned char* addr_hash;
     unsigned char* addr_curve;
     len_curve = (int) *(der parm_off   off -1);
     addr_curve = der parm_off   off;
     parm_oid = ksba_oid_to_str (addr_curve, len_curve);
 /*Вытаскиваем oid хэша для ГОСТ-ключа*/
     if( *(addr_curve   len_curve)== TYPE_OBJECT_ID) {
 	len_hash = (unsigned int) *(der parm_off   off   len_curve   1);
 	addr_hash = addr_curve   len_curve   2;
 	parm_oid_hash = ksba_oid_to_str (addr_hash, len_hash);
     }
 /*Вытаскиваем oid алгоритма шифрования для ГОСТ-ключа*/
   }
 
   else if (parm_off && parm_len)
     {
       parmder = der   parm_off;
@@ -762,6  849,13 @@
       put_stringbuf_sexp (&sb, "curve");
       put_stringbuf_sexp (&sb, parm_oid);
       put_stringbuf (&sb, ")");
 /*Устанавливаем oid-хэша для ГОСТ-ового ключа*/
       if(gost_key && parm_oid_hash) {
         put_stringbuf (&sb, "(");
 	put_stringbuf_sexp (&sb, "hash");
         put_stringbuf_sexp (&sb, parm_oid_hash);
         put_stringbuf (&sb, ")");
       }
     }
 
   /* If parameters are given and we have a description for them, parse
@@ -851,6  945,43 @@
           put_stringbuf (&sb, "(");
           tmp[0] = *elem; tmp[1] = 0;
           put_stringbuf_sexp (&sb, tmp);
 /*Извлечение значения открытого ключа в соответствии с рекомендациями TK-26*/
           if(gost_key){
             unsigned char pk[129];
             unsigned char *x;
             unsigned char *y;
             int len_pk;
             int len_xy;
 	    int i;
 	    unsigned char c_inv;
 	    int offset;
             pk[0] = 0x04;
             if(len == 131 || len == 66){
         	offset = 0;
     		if(der[0] == 0x04 && der[1] & 0x80)
     		    offset = 3;
     		else if(der[0] == 0x04 && der[1] & 0x40)
     		    offset = 2;
 		len_pk = len - offset;
 		memcpy(&pk[1], der   offset, len_pk);
 		x = &pk[1];
 		len_xy = len_pk / 2;
 		y = x   len_xy;
 /*REVERT-INVERTIROVANIE*/
         	for (i = 0; i < (len_xy/2); i  ) {
             	    c_inv = *(x   i);
             	    *(x   i) = *(x   len_xy - i - 1);
             	    *(x   len_xy - i - 1) = c_inv;
         	}
         	for (i = 0; i < (len_xy/2); i  ) {
             	    c_inv = y[i];
             	    y[i] = y[len_xy - i -1];
             	    y[len_xy - i - 1] = c_inv;
         	}
         	put_stringbuf_mem_sexp (&sb, pk , len_pk   1);
 	    }
           } else
 
           put_stringbuf_mem_sexp (&sb, der, len);
           der  = len;
           derlen -= len;
@@ -1606,6  1737,7 @@
   const unsigned char *ctrl;
   const char *elem;
   struct stringbuf sb;
   int gost_sign;
 
   /* FIXME: The entire function is very similar to keyinfo_to_sexp */
   *r_string = NULL;
@@ -1615,7  1747,6 @@
   else
     algo_table = enc_algo_table;
 
-
   err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr,
                        NULL, NULL, NULL);
   if (err)
@@ -1628,11  1759,16 @@
            && !memcmp (der off, algo_table[algoidx].oid, len))
         break;
     }
 
   if (!algo_table[algoidx].oid)
     return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
 
   if (!algo_table[algoidx].supported)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 
 /*Определяем тип подписи по oid-у*/
   gost_sign = !memcmp(algo_table[algoidx].oidstring, "1.2.643", 7);
 
   der  = nread;
   derlen -= nread;
 
@@ -1682,8  1818,21 @@
 
           put_stringbuf (&sb, "(");
           tmp[0] = *elem; tmp[1] = 0;
 /*Если ЭП по ГОСТ, то r находится справа, а s находится слева */
 	  if(gost_sign == 1 && algo_table == sig_algo_table){
 	    put_stringbuf_sexp (&sb, "r");
 	    put_stringbuf_mem_sexp (&sb, der (len/2), len/2);
     	    put_stringbuf (&sb, ")");
     	    put_stringbuf (&sb, "(");
 	    put_stringbuf_sexp (&sb, "s");
 	    put_stringbuf_mem_sexp (&sb, der, len/2);
 	  }
 	  else{
 	  
           put_stringbuf_sexp (&sb, tmp);
           put_stringbuf_mem_sexp (&sb, der, len);
 	  }
 
           der  = len;
           derlen -= len;
           put_stringbuf (&sb, ")");

Про сертификаты:  Как найти файл эцп на компьютере

Имеющиеся комментарии позволяют понять логику вносимых изменений, которая в первую очередь определяется рекомендациями

ТК-26

.

Осталось внести изменения в модули gnupg-agent (каталог agent) и модуль gpgsm (каталог sm).

Патч для модуля gnupg-agent находится здесь
diff -u AGENT_ORIG/call-scd.c AGENT/call-scd.c
--- AGENT_ORIG/call-scd.c	2021-05-15 15:13:22.000000000  0300
    AGENT/call-scd.c	2021-07-19 09:27:36.904313900  0300
@@ -806,6  806,14 @@
     case GCRY_MD_SHA256: return "--hash=sha256";
     case GCRY_MD_SHA384: return "--hash=sha384";
     case GCRY_MD_SHA512: return "--hash=sha512";
 /*ГОСТ Р 34.11-2001/2021*/
     case GCRY_MD_STRIBOG512:
 	return "--hash=stribog512";
     case GCRY_MD_STRIBOG256:
 	return "--hash=stribog256";
     case GCRY_MD_GOSTR3411_CP:
 	return "--hash=gostr3411_CP";
 
     default:             return "";
     }
 }
@@ -884,6  892,7 @@
   else
     snprintf (line, sizeof line, "PKSIGN %s %s",
               hash_algo_option (mdalgo), keyid);
 
   rc = assuan_transact (ctrl->scd_local->ctx, line,
                         put_membuf_cb, &data,
                         inq_needpin, &inqparm,
@@ -901,6  910,7 @@
     }
 
   *r_buf = get_membuf (&data, r_buflen);
 
   return unlock_scd (ctrl, 0);
 }
 
diff -u AGENT_ORIG/divert-scd.c AGENT/divert-scd.c
--- AGENT_ORIG/divert-scd.c	2021-04-03 18:13:56.000000000  0300
    AGENT/divert-scd.c	2021-07-19 09:29:03.896318684  0300
@@ -160,6  160,17 @@
       log_error ("no object identifier for algo %dn", algo);
       return gpg_error (GPG_ERR_INTERNAL);
     }
 /*ГОСТ-алгоритмы*/
     switch(algo) {
 		case GCRY_MD_STRIBOG512:
 		case GCRY_MD_STRIBOG256:
 		case GCRY_MD_GOSTR3411_CP:
 		case GCRY_MD_GOSTR3411_94:
 		    asnlen = 0;
 		    break;
 		default :
 		    break;
     }
 
   frame = xtrymalloc (asnlen   digestlen);
   if (!frame)
@@ -423,6  434,7 @@
   (void)desc_text;
 
   rc = ask_for_card (ctrl, shadow_info, &kid);
 
   if (rc)
     return rc;
 
@@ -498,7  510,7 @@
   n = snext (&s);
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
-  if (smatch (&s, n, "rsa"))
   if (smatch (&s, n, "rsa") || smatch (&s, n, "ecc"))
     {
       if (*s != '(')
         return gpg_error (GPG_ERR_UNKNOWN_SEXP);
diff -u AGENT_ORIG/pksign.c AGENT/pksign.c
--- AGENT_ORIG/pksign.c	2021-05-15 15:13:22.000000000  0300
    AGENT/pksign.c	2021-07-19 09:30:28.771323350  0300
@@ -328,6  328,7 @@
       int is_RSA = 0;
       int is_ECDSA = 0;
       int is_EdDSA = 0;
       int is_ECGOST = 0;
 
       rc = agent_public_key_from_file (ctrl, ctrl->keygrip, &s_pkey);
       if (rc)
@@ -345,6  346,21 @@
             is_RSA = 1;
           else if (key_type == GCRY_PK_ECDSA)
             is_ECDSA = 1;
 /*Проверка ГОСТ-алгоритма*/
 	  if (is_ECDSA) {
 	    switch(ctrl->digest.algo)
 	    {
 		case GCRY_MD_STRIBOG512:
 		case GCRY_MD_STRIBOG256:
 		case GCRY_MD_GOSTR3411_CP:
 		case GCRY_MD_GOSTR3411_94:
 		    is_ECGOST = 1;
 		    break;
 		default :
 		    is_ECGOST = 0;
 		    break;
 	    }
 	  }
         }
 
       {
@@ -361,7  377,7 @@
       }
       if (rc)
         {
-          log_error ("smartcard signing failed: %sn", gpg_strerror (rc));
           log_error ("agent_pksign_do: smartcard signing failed: %sn", gpg_strerror (rc));
           goto leave;
         }
 
@@ -396,7  412,8 @@
 
           r_buflen = s_buflen = len/2;
 
-          if (*buf & 0x80)
           if ((*buf & 0x80) && !is_ECGOST)
 
             {
               r_buflen  ;
               r_buf_allocated = xtrymalloc (r_buflen);
@@ -409,8  426,8 @@
             }
           else
             r_buf = buf;
           if ((*(buf   len/2) & 0x80) && !is_ECGOST)
 
-          if (*(buf   len/2) & 0x80)
             {
               s_buflen  ;
               s_buf_allocated = xtrymalloc (s_buflen);
@@ -427,6  444,15 @@
           else
             s_buf = buf   len/2;
 
     if(is_ECGOST){
           rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(gost(r%b)(s%b))(hash %s))",
                                 s_buflen, s_buf,
                                 r_buflen, r_buf,
                                 gcry_md_algo_name(ctrl->digest.algo));
           gcry_log_debugsxp ("SIG_VAL", s_sig);
     }
     else
 
           rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))",
                                 r_buflen, r_buf,
                                 s_buflen, s_buf);

Патч для модуля gpgsm можно увидеть здесь
diff -u SM_ORIG/call-agent.c SM/call-agent.c
--- SM_ORIG/call-agent.c	2021-04-03 18:13:56.000000000  0300
    SM/call-agent.c	2021-07-19 09:37:08.411345324  0300
@@ -234,6  234,7 @@
   rc = start_agent (ctrl);
   if (rc)
     return rc;
 
   inq_parm.ctrl = ctrl;
   inq_parm.ctx = agent_ctx;
 
@@ -313,6  314,14 @@
     case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break;
     case GCRY_MD_MD5:   hashopt = "--hash=md5"; break;
     case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break;
 /*ГОСТ-алгоритмы*/
     case GCRY_MD_STRIBOG512:
 	hashopt = "--hash=stribog512"; break;
     case GCRY_MD_STRIBOG256:
 	hashopt = "--hash=stribog256"; break;
     case GCRY_MD_GOSTR3411_CP:
 	hashopt = "--hash=gostr3411_CP"; break;
 
     default:
       return gpg_error (GPG_ERR_DIGEST_ALGO);
     }
@@ -357,7  366,20 @@
       xfree (sigbuf);
       return 0;
     }
-  p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
 /*Обработка для ГОСТ-алгоритмов*/
   switch(digestalgo)
     {
     case GCRY_MD_STRIBOG512:
     case GCRY_MD_STRIBOG256:
     case GCRY_MD_GOSTR3411_CP:
     case GCRY_MD_GOSTR3411_94:
 	p = stpcpy (p, "(7:sig-val(3:ecc(1:s" );
 	break;
     default: 
 	p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
 	break;
     }
 
   sprintf (p, "%u:", (unsigned int)sigbuflen);
   p  = strlen (p);
   memcpy (p, sigbuf, sigbuflen);
diff -u SM_ORIG/certcheck.c SM/certcheck.c
--- SM_ORIG/certcheck.c	2021-04-03 18:13:56.000000000  0300
    SM/certcheck.c	2021-07-19 09:37:52.028347722  0300
@@ -71,11  71,13 @@
   size_t nframe;
   unsigned char *frame;
 
-  if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
   if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA || pkalgo == GCRY_PK_ECC)
 
     {
       unsigned int qbits;
 
-      if ( pkalgo == GCRY_PK_ECDSA )
       if ( pkalgo == GCRY_PK_ECDSA || pkalgo == GCRY_PK_ECC)
       
         qbits = gcry_pk_get_nbits (pkey);
       else
         qbits = get_dsa_qbits (pkey);
@@ -169,7  171,7 @@
       memcpy ( frame n, gcry_md_read(md, algo), len ); n  = len;
       assert ( n == nframe );
     }
-  if (DBG_CRYPTO)
 //  if (DBG_CRYPTO)
     {
       int j;
       log_debug ("encoded hash:");
@@ -177,6  179,22 @@
         log_printf (" X", frame[j]);
       log_printf ("n");
     }
     int i;
     for (i = 0; i < (nframe/2); i  ) {
 	unsigned char c;
         c = frame[i];
         frame[i] = frame[nframe - i -1];
         frame[nframe - i - 1] = c;
     }
     if (DBG_CRYPTO)
     {
       int j;
       log_debug ("GOST encoded hash:");
       for (j=0; j < nframe; j  )
         log_printf (" X", frame[j]);
       log_printf ("n");
     }
   }
 
   gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe);
   xfree (frame);
diff -u SM_ORIG/gpgsm.c SM/gpgsm.c
--- SM_ORIG/gpgsm.c	2021-05-15 15:13:22.000000000  0300
    SM/gpgsm.c	2021-07-19 09:38:33.277349990  0300
@@ -471,6  471,7 @@
 {
   switch (algo)
     {
     case GCRY_PK_ECC:
     case GCRY_PK_RSA:
     case GCRY_PK_ECDSA:
       return gcry_pk_test_algo (algo);
@@ -1577,6  1578,10 @@
     opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.3";
   else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA256") )
     opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.4";
   else if (!strcmp (opt.def_cipher_algoid, "GOST28147") )
 //    opt.def_cipher_algoid = "1.2.643.2.2.21";
     opt.def_cipher_algoid = "1.2.643.2.2.31.1";
 
 
   if (cmd != aGPGConfList)
     {
diff -u SM_ORIG/keylist.c SM/keylist.c
--- SM_ORIG/keylist.c	2021-05-15 15:13:22.000000000  0300
    SM/keylist.c	2021-07-19 20:20:11.560466784  0300
@@ -769,6  769,42 @@
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
 /*Информация по ГОСТ-ключам*/
     if(!strncasecmp(algoname, "ecc", 3)){
 	gpg_error_t err;
 	ksba_sexp_t p;
 	size_t n;
 	gcry_sexp_t s_pkey, l2;
 	const char *str_hash;
 	const char *str_curve;
   /* Get the public key from the issuer certificate.  */
 	p = ksba_cert_get_public_key (cert);
 	n = gcry_sexp_canon_len (p, 0, NULL, NULL);
 	err = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
 	ksba_free (p);
 /*Ищем хэш*/
 	l2 = gcry_sexp_find_token (s_pkey, "hash", 0);
 	str_hash = gcry_sexp_nth_string (l2, 1);
         gcry_sexp_release (l2);
 /*Ищем curve*/
 	l2 = gcry_sexp_find_token (s_pkey, "curve", 0);
 	str_curve = gcry_sexp_nth_string (l2, 1);
         gcry_sexp_release (l2);
         gcry_sexp_release (s_pkey);
 	if(!memcmp(str_hash, "1.2.643.7", 9)){
 /*    GOST R 34.10-2021-512  */
 		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))n",
             	    nbits, "GOST R 34.10-2021", str_curve);
 	} else if(!memcmp(str_hash, "1.2.643.2", 9)){
 /*    GOST R 34.10-2021-246   */
 		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))n",
             	    nbits, "GOST R 34.10-2001", str_curve);
 	} else
 	    es_fprintf (fp, "      keyType: %u bit %sn",
                 nbits, algoname? algoname:"?");
     }
     else
 
     es_fprintf (fp, "      keyType: %u bit %sn",
                 nbits, algoname? algoname:"?");
   }
@@ -1118,6  1154,42 @@
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
 /*Информация по ГОСТ-ключам*/
     if(!strncasecmp(algoname, "ecc", 3)){
 	gpg_error_t err;
 	ksba_sexp_t p;
 	size_t n;
 	gcry_sexp_t s_pkey, l2;
 	const char *str_hash;
 	const char *str_curve;
   /* Get the public key from the issuer certificate.  */
 	p = ksba_cert_get_public_key (cert);
 	n = gcry_sexp_canon_len (p, 0, NULL, NULL);
 	err = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
 	ksba_free (p);
 /*Ищем хэш*/
 	l2 = gcry_sexp_find_token (s_pkey, "hash", 0);
 	str_hash = gcry_sexp_nth_string (l2, 1);
         gcry_sexp_release (l2);
 /*Ищем curve*/
 	l2 = gcry_sexp_find_token (s_pkey, "curve", 0);
 	str_curve = gcry_sexp_nth_string (l2, 1);
         gcry_sexp_release (l2);
         gcry_sexp_release (s_pkey);
 	if(!memcmp(str_hash, "1.2.643.7", 9)){
 /*    GOST R 34.10-2021-512  */
 		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))n",
             	    nbits, "GOST R 34.10-2021", str_curve);
 	} else if(!memcmp(str_hash, "1.2.643.2", 9)){
 /*    GOST R 34.10-2021-246   */
 		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))n",
             	    nbits, "GOST R 34.10-2001", str_curve);
 	} else
 	    es_fprintf (fp, "      keyType: %u bit %sn",
                 nbits, algoname? algoname:"?");
     }
     else
 
     es_fprintf (fp, "     key type: %u bit %sn",
                 nbits, algoname? algoname:"?");
   }
diff -u SM_ORIG/sign.c SM/sign.c
--- SM_ORIG/sign.c	2021-05-15 15:13:22.000000000  0300
    SM/sign.c	2021-07-19 09:41:05.738358373  0300
@@ -33,7  33,41 @@
 
 #include "keydb.h"
 #include "../common/i18n.h"
 char *cert_get_digest_algo_from_pk(ksba_cert_t cert) {
   gpg_error_t err;
   ksba_sexp_t p;
   size_t n;
   gcry_sexp_t s_pubkey;
   char buf[50];
   const char *s;
   gcry_sexp_t h_pk;
 
   /* Get the public key from the certificate.  */
   p = ksba_cert_get_public_key (cert);
   n = gcry_sexp_canon_len (p, 0, NULL, NULL);
   if (!n)
     {
       ksba_free (p);
       return NULL;
     }
   err = gcry_sexp_sscan ( &s_pubkey, NULL, p, n);
   ksba_free (p);
   if (err) {
       return NULL;
     }
 
   h_pk = gcry_sexp_find_token (s_pubkey, "hash", 0);
   if (h_pk){
     s = gcry_sexp_nth_data (h_pk, 1, &n);
     memcpy(buf, s, n);
     buf[n] = '';
     gcry_sexp_release (s_pubkey);
     gcry_sexp_release (h_pk);
     return (strdup(buf));
   } else {
     return NULL;
   }
 }
 
 /* Hash the data and return if something was hashed.  Return -1 on error.  */
 static int
@@ -301,8  335,6 @@
   return err;
 }
 
-
-
 
 /* Perform a sign operation.
 
@@ -328,6  360,8 @@
   ksba_isotime_t signed_at;
   certlist_t cl;
   int release_signerlist = 0;
   const unsigned char *shash;
   size_t lshash;
 
   audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
 
@@ -435,8  469,15 @@
         }
       else
         {
 	  oid = (char *)cert_get_digest_algo_from_pk(cl->cert);
 	  if (oid == NULL || strncmp(oid, "1.2.643", 7))
         
           oid = ksba_cert_get_digest_algo (cl->cert);
           cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
 	  if(oid != NULL && !strcmp(oid, "1.2.643.2.2.30.1")){
 	    cl->hash_algo = GCRY_MD_GOSTR3411_CP; 
 	  }
 
         }
       switch (cl->hash_algo)
         {
@@ -446,6  487,15 @@
         case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
         case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
         case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
 	case GCRY_MD_STRIBOG256:	/* GOST R 34.11-2021, 256 bit.  */
 	    oid = "1.2.643.7.1.1.2.2"; break;
 	case GCRY_MD_STRIBOG512:	 /* GOST R 34.11-2021, 512 bit.  */
 	    oid = "1.2.643.7.1.1.2.3"; break;
 	case GCRY_MD_GOSTR3411_CP:	/* GOST R 34.11-94.  */
 	    oid = "1.2.643.2.2.9"; break;
 	case GCRY_MD_GOSTR3411_94:	/* GOST R 34.11-94. TK26 */
 	    oid = "1.2.643.2.2.9"; break;
 
 /*         case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */
 
         case GCRY_MD_MD5:  /* We don't want to use MD5.  */

Про сертификаты:  открытая и закрытая часть эцп: как сделать экспорт |

Патчи модулей gnupg-agent и gpgsm делались для версии

gnupg-2.1.21

. Перенести эти патчи скажем на версию gnupg-2.2.9 не составляет большого труда.

После сборки доработанных версий libksba, gnupg, утилиты gnupg_pkcs11_scd и установки их в систему, для обеспечения интерфейса с токенами PKCS#11 необходимо в конфигурационный файл gpg-agent.conf добавить следующие строки:

…
scdaemon-program /usr/local/bin64/gnupg-pkcs11-scd
pinentry-program /usr/bin/pinentry-qt
…

Затем необходимо в конфигурационном файле gnu-pkcs11-scd.conf указать библиотеки PKCS#11 для токенов, которые будут использоваться:

#В примере предполагается использование токена 
#с библиотекой /usr/local/lib64/libls11sw2021.so
…
# Comma-separated list of available provider names. Then set
# attributes for each provider using the provider-[name]-attribute
# syntax.
providers mytokenpkcs11
provider-mytokenpkcs11-library /usr/local/lib64/libls11sw2021.so
#Если используется, например, токен ruTokenECP
#provider-metokenpkcs11-library /usr/local/lib64/librutokenecp.so
provider-mytokenpkcs11-cert-private
…

Все готово к работе. Проверяем, что модуль gpg-agent запущен:

bash-4.3$ gpg-agent --daemon 
gpg-agent[1196]: enabled debug flags: ipc 
gpg-agent: агент gpg уже запущен - еще один, новый, запущен не будет 
gpg-agent: secmem usage: 0/32768 bytes in 0 blocks 
bash-4.3$

Итак, на арену выходит kleopatra:

Вопросы о GnuPG

Кнопка «Импорт» используется прежде всего для импорта в систему корневых сертификатов, которые необходимы для проверки валидности личных сертификатов.

При запуске утилиты kleopatra может быть запрошен PIN-код к подключенному токену:

Вопросы о GnuPG

В дальнейшем это окно будет появляться всегда, когда потребуется доступ к закрытому ключу, хранящемуся на токене, например, при подписи электронного письма или файла.

Итак, вы бираем пункт меню «Сервис → Manage Smartcards». Если ранее с токена сертификаты не загружались, то высветится следующее окно:

Вопросы о GnuPG

Для того, чтобы увидеть сертификаты на подключенном токене, достаточно нажать кнопку LoadCertificates (Загрузить Сертификаты):

Вопросы о GnuPG

Если корневые сертификаты отсутствуют для сертификатов пользователя отсутствуют (not certified), то необходимо их установить (Файл → Импорт или кнопка Импорт):

Вопросы о GnuPG

Теперь, после установки корневых сертификатов, хорошие сертификаты можно видеть в списке доверенных сертификатов:

Вопросы о GnuPG

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

Вопросы о GnuPGВсе это наглядно можно увидеть при установке сертификата, который по умолчанию будет использоваться для подписания писем в почтовом клиенте KMail (Настройки → Настройки KMail → Профили → <профиль владельца> → Изменить → Криптография → Сертификат подписи SMIME):

Вопросы о GnuPG

Можно также попытаться подписать файл «плохим сертификатом»:

Вопросы о GnuPG

Попытка подписать документ «плохим сертификатом» не удается, надо использовать хорошие сертификаты:

Вопросы о GnuPG

Для того, чтобы письмо при отправке было подписано электронной подписью достаточно выставить галочку Параметры → Подписать письмо:

Вопросы о GnuPG

Письмо будет автоматически подписано при отправке (кнопка отправить).

В заключение еще раз хочется обратить разработчиков приложений для отечественной ИОК, что проект GnuPG как среда разработки мало чем уступает по своим возможностям как OpenSSL, так и NSS.

Практика

Ладно, давайте уже перейдем к практике.

Для шифрования мы будем использовать пакет gpg это программа для асимметричного шифрования с открытым кодом, которая очень популярна. С помощью нее вы можете шифровать данные, подписывать свои файлы.

Подпись нужна для того чтобы в сети знали, что это вы. Программисты, например, часто используют gpg для того чтобы подписывать им свои программы. Просто чтобы вы были уверены, что программа именно от программиста, а не кто-то внедрил в программу вирус и подменил файл.

Или, например, создатель биткоина писал сообщения сообществу и подписывал их своим ключом.

Мы все знаем что его ключ

pub 1024D/5EC948A1 2008-10-30

И если мы видим такую подпись, то мы скорее будем доверять ей. Чем какому-то сообщению без подписи.

Так же с помощью gpg мы можем авторизоваться на SSH серверах.

Короче говоря, очень крутая программа, которой пользуются просто все. Вы если про нее не знаете, то рекомендую узнать о ней побольше.

Как я уже сказал, она может шифровать, подписывать и авторизовывать.

Давайте же начнем ее использовать.

Для начала установим пакет gpg

Проверим, что версия актуальная.

gpg --version

У меня, например версия 2.2.13.

Давайте проверим так же, что у нас нет ключей. Я свои удалил, чтобы показать вам весь процесс создания ключей с самого начала.

Для того чтобы посмотреть наши публичные ключи есть команда

gpg -k

gpg –list-keys –keyid-format LONG (Мы так же можем использовать эту команду если нам нужно видеть ключи)

А для того чтобы увидеть приватные ключи есть команда:

gpg -K

gpg –list-secret-keys –keyid-format LONG (Мы так же можем использовать эту команду если нам нужно видеть ключи)

Мы убедились, что у нас нет ключей. Теперь давайте мы их создадим. Делается это командой:

gpg --full-generate-key

Как я уже говорил, мы можем выбрать любой из вариантов. RSA Lab нам рекомендует 2048. Лично я выберу 4096, если ваш компьютер мощный, то я и вам его рекомендую.

Так, теперь мы можем опять выполнить команды gpg -k и gpg -K

И теперь мы видим, что у нас есть наш ключ.

Давайте теперь я кратко расскажу о том, как это читается.

sec  2048R/920B1221 2021-12-01 [expires: 2021-12-01]
  • sec – это primary private часть нашего ключа
  • 4096R – это собственно алгоритм и количество бит
  • 920B1221 – это айди ключа, ну вернее последняя его часть, на самом деле он немного длиннее.
  • 2021-12-01 – дата создания ключа
  • [expires: 2021-12-01] – когда истекает ключ

uid

ssb  2048R/2AB141A0 2021-12-01
  • ssb – Это наш сабкей в приватной части нашего ключа
  • 2048R – его алгоритм
  • 2AB141A0 – его айди
  • 2021-12-01 – его дата создания

Когда мы создаем наш ключ, то это вообще все сохраняется в директории ~/.gnupg

  • ~/.gnupg/gpg.conf – файл конфиругации
  • ~/.gnupg/secring.gpg – тут секретная часть ключей
  • ~/.gnupg/pubring.gpg – тут приватная часть ключей
Оцените статью
Мой сертификат
Добавить комментарий