Public key infrastructure: GnuPG / SMIME and PKCS # 11 tokens with support for Russian cryptography

    The “H” hour is inexorably approaching: “the use of the signature scheme of GOST R 34.10-2001 to form a signature after December 31, 2018 is not allowed!”. And finally, on July 16, 2018, a notice appeared on the website of the Federal Situational Center of e-government on issuing key certificates for verifying electronic signatures of subordinate certifying centers at the parent certification center in accordance with GOST R 34.10-2012. True, it is still not clear when the issue of old certificates will cease. But the “process has begun” and it pleases.

    The introduction of the new electronic signature into use implies the availability of funds not only for issuing certificates according to GOST R 34.10-2012, but also the availability of means for generating and verifying this signature. On one of these tools are available on pages habr already told



    An electronic signature is widely used not only in the electronic document management (EDM), but also for e-mails (email):



    If we talk about OpenSource projects in the field of PKI based X509 certificates, then along with the Network Security Services (NSS), OpenSSL, the GnuPG / SMIME project is widely popular . The libcrypt library is the cryptographic core of this project .. It is significant that this library supports GOST R 34.10-2012 cryptoalgorithms and GOST R 34.11-2012 (STRIBOG256 and STRIBOG512) and does not require any refinement, at least for the functions of electronic signature generation and its verification. GnuPG uses the LibKSBA library to parse, manipulate PKI objects (certificates, certificate requests, electronic signatures, etc.) . Unlike LibGCrypt, libksba does not implement TK-26 recommendations for cryptographic messages of the CMS format. In the articleshows the upgrade of the libksba library related to the parsing of X509 certificates and CMS messages (PKCS7), which made it possible to verify the electronic signature of certificates and the electronic signature of PKCS # 7 documents. Today we will go further and add functionality to the library to form the PKCS # 7 CMS format. This will enable electronic signature support in accordance with GOST R 34.10-2001.2012 (after a certain upgrade of the GnuPG project itself) and in the GnuPG / SM module.

    And ultimately, it will be possible to use the domestic electronic signature in the KMail mail client (by the way, in many other mail clients), and in the Kleopatra utility both for checking the electronic signature and its generation for various files:



    imageNow about the main thing. In the future we will proceed from the fact that users store their personal certificates (private key and electronic signature verification key certificate) on PKCS # 11 tokens / smartcards with implementation of at least electronic signature generation functions (CKM_GOSTR3410 mechanisms for generating electronic signature according to GOST R 34.10-2001 / 2012 with a key length of 256 bits and / or CKM_GOSTR3410_512 for the formation of electronic signature according to GOST R 34.10-2012 with a key length of 512 bits). The best option is when the functionality of the token / smarcard supports the PKCS # 11 v.2.40 standard.

    To access PKCS # 11 tokens in GnuPG, use the gnupg_pkcs11_scd utility . Our examples use the gnupg_pkcs11_csd version 0.9.1 utility .. The main task of this utility (in addition to organizing the execution of PKCS # 11 cryptographic functions, for example, to sign a hash), is to parse certificates stored on a token, obtain information about an open key in the form of an S-expression and send it up the chain. This operation is performed by the keyutil_get_cert_sexp function, which is located in the keyutil.c file. Unfortunately, this utility is sharpened only for work with RSA-certificates. But since we have the libksba library at our disposal, and it has also been modified to work with GOST certificates, it turned out to be surprisingly simple to rewrite the keyutil_get_cert_sexp function to process any certificates supported by the libksba library:

    /*
       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 (
    	unsignedchar *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 0if (
    		(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;
    }

    The original code is enclosed in a block:

    #if 0#endif

    The full patch for the gnupg_pkcs11_scd utility is here.
    diff -u PKCS11_CSD_ORIG/command.c PKCS11_CSD/command.c
    --- PKCS11_CSD_ORIG/command.c	2018-07-19 16:25:20.778692015 +0300+++ PKCS11_CSD/command.c	2018-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,"\x2A\x85\x03", 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	2018-07-19 16:25:20.779692015 +0300+++ PKCS11_CSD/keyutil.c	2018-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:


    Given this patch, the following script is used to build the gnupg_pkcs11_scd utility:

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

    As a result, this patch will allow using GOST-tokens (including those certified by the Federal Security Service of Russia), at least for generating an electronic signature.

    We will not dwell on the modifications of the libksba library, but simply give
    full patch for libksba
    diff -u KSBA_ORIG/cms.c KSBA/cms.c
    --- KSBA_ORIG/cms.c	2013-03-15 23:26:38.000000000 +0400+++ KSBA/cms.c	2018-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	2016-08-22 11:40:58.000000000 +0300+++ KSBA/dn.c	2018-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 @@
         "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19", "0.9.2342.19200300.100.1.25" },
     {"UID", 1, "userid",              10,
         "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01", "0.9.2342.19200300.100.1.1 " },
    -{"EMAIL", 3, "emailAddress",       9,+{"E", 1, "emailAddress",       9,
         "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01",     "1.2.840.113549.1.9.1" },
    +/*oid-ы квалифицированного сертификата от TK-26*/+{"OGRN", 4, "OGRN",             5, "\x2a\x85\x03\x64\x01", "1.2.643.100.1" },+{"INN", 4, "INN",             8, "\x2a\x85\x03\x03\x81\x03\x01\x01", "1.2.643.3.131.1.1" },+{"SNILS", 4, "SNILS",             5, "\x2a\x85\x03\x64\x03", "1.2.643.100.3" },+{"OGRNIP", 4, "OGRNIP",             5, "\x2a\x85\x03\x64\x05", "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	2015-10-28 13:41:48.000000000 +0300+++ KSBA/keyinfo.c	2018-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 */
         "\x2a\x86\x48\xce\x3d\x02\x01", 7,
         1, PKALGO_ECC, "ecc", "q", "\x80" },
    +/*oid-ы ГОСТ-овых ключей*/+  { /* GOST3410-2001 */+    "1.2.643.2.2.19", /*  gostPublicKey-2001 */+    "\x2a\x85\x03\x02\x02\x13", 6,+    1, PKALGO_ECC, "ecc", "q", "\x80" },+  { /* GOST3410-2012-256 */+    "1.2.643.7.1.1.1.1", /*  gostPublicKey-2012-256 */+    "\x2a\x85\x03\x07\x01\x01\x01\x01", 8,+    1, PKALGO_ECC, "ecc", "q", "\x80" },+  { /* GOST3410-2012-512 */+    "1.2.643.7.1.1.1.2", /*  gostPublicKey-2012-512 */+    "\x2a\x85\x03\x07\x01\x01\x01\x02", 8,+    1, PKALGO_ECC, "ecc", "q", "\x80" },
       {NULL}
     };
    @@ -209,6 +221,31 @@
         "1.3.36.3.4.3.2.2",     /* sigS_ISO9796-2rndWithrsa_ripemd160 */
         "\x2B\x24\x03\x04\x03\x02\x02", 7,
         0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" },
    +  { /* GOST3410-2001 */+    "1.2.643.2.2.19", /*  gostPublicKey-2001 */+    "\x2a\x85\x03\x02\x02\x13", 6,+    1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "gostr3411_CP" },+  { /* GOST3410-2012-256 */+    "1.2.643.7.1.1.1.1", /*  gostPublicKey-2012-256 */+    "\x2a\x85\x03\x07\x01\x01\x01\x01", 8,+    1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog256"},+  { /* GOST3410-2012-512 */+    "1.2.643.7.1.1.1.2", /*  gostPublicKey-2012-512 */+    "\x2a\x85\x03\x07\x01\x01\x01\x02", 8,+    1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog512"},++  { /* GOST3411-2012-256 */+    "1.2.643.7.1.1.3.2", /*  STRIBOG256 */+    "\x2a\x85\x03\x07\x01\x01\x03\x02", 8,+    1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog256" },+  { /* GOST3411-2012-512 */+    "1.2.643.7.1.1.3.3", /*  STRIBOG512 */+    "\x2a\x85\x03\x07\x01\x01\x03\x03", 8,+    1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog512" },+  { /* GOST3410-2001-Signature */+    "1.2.643.2.2.3", /*  gosrPublicKey-2001 avec signature */+    "\x2a\x85\x03\x02\x02\x03", 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) */
         "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9,
         1, PKALGO_RSA, "rsa", "a", "\x82" },
    +/*oid-ы ГОСТ-ых ключей для ассиметричного шифрования*/+  {+    "1.2.643.2.2.19", /*GOST R34.10-2001 */+    "\x2A\x85\x03\x02\x02\x13", 6,+    1, PKALGO_ECC, "ecc", "a", "\x80" },+  {+    "1.2.643.7.1.1.1.1", /*GOST R34.10-2012-256 */+    "\x2A\x85\x03\x07\x01\x01\x01\x01", 8,+    1, PKALGO_ECC, "ecc", "a", "\x80" },+  {+    "1.2.643.7.1.1.1.2", /*GOST R34.10-2012-512 */+    "\x2A\x85\x03\x07\x01\x01\x01\x02", 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/2012*/+//    "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", "GOST2012-tc26-A"      },
         { "1.2.643.7.1.2.1.2.2", "GOST2012-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 %02x - ignored\n", 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, ")");
    


    The available comments allow us to understand the logic of the changes being made, which is primarily determined by the recommendations of TC-26 .

    It remains to make changes to the gnupg-agent modules (agent directory) and the gpgsm module (sm directory).

    The patch for the gnupg-agent module is here.
    diff -u AGENT_ORIG/call-scd.c AGENT/call-scd.c
    --- AGENT_ORIG/call-scd.c	2017-05-15 15:13:22.000000000 +0300+++ AGENT/call-scd.c	2018-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/2012*/+    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	2017-04-03 18:13:56.000000000 +0300+++ AGENT/divert-scd.c	2018-07-19 09:29:03.896318684 +0300@@ -160,6 +160,17 @@
           log_error ("no object identifier for algo %d\n", 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	2017-05-15 15:13:22.000000000 +0300+++ AGENT/pksign.c	2018-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: %s\n", gpg_strerror (rc));+          log_error ("agent_pksign_do: smartcard signing failed: %s\n", 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);
    

    The patch for the gpgsm module can be seen here.
    diff -u SM_ORIG/call-agent.c SM/call-agent.c
    --- SM_ORIG/call-agent.c	2017-04-03 18:13:56.000000000 +0300+++ SM/call-agent.c	2018-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	2017-04-03 18:13:56.000000000 +0300+++ SM/certcheck.c	2018-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 (" %02X", 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 (" %02X", 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	2017-05-15 15:13:22.000000000 +0300+++ SM/gpgsm.c	2018-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	2017-05-15 15:13:22.000000000 +0300+++ SM/keylist.c	2018-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-2012-512  */+		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))\n",+            	    nbits, "GOST R 34.10-2012", str_curve);+	} else if(!memcmp(str_hash, "1.2.643.2", 9)){+/*    GOST R 34.10-2012-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 %s\n",+                nbits, algoname? algoname:"?");+    }+    else+
         es_fprintf (fp, "      keyType: %u bit %s\n",
                     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-2012-512  */+		es_fprintf (fp, "      keyType: %u bit %s (Curve: %s))\n",+            	    nbits, "GOST R 34.10-2012", str_curve);+	} else if(!memcmp(str_hash, "1.2.643.2", 9)){+/*    GOST R 34.10-2012-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 %s\n",+                nbits, algoname? algoname:"?");+    }+    else+
         es_fprintf (fp, "     key type: %u bit %s\n",
                     nbits, algoname? algoname:"?");
       }
    diff -u SM_ORIG/sign.c SM/sign.c
    --- SM_ORIG/sign.c	2017-05-15 15:13:22.000000000 +0300+++ SM/sign.c	2018-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] = '\0';+    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-2012, 256 bit.  */+	    oid = "1.2.643.7.1.1.2.2"; break;+	case GCRY_MD_STRIBOG512:	 /* GOST R 34.11-2012, 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.  */
    


    Patches of the gnupg-agent and gpgsm modules were made for the gnupg-2.1.21 version . To transfer these patches to say the version of gnupg-2.2.9 is not difficult.

    After building modified versions of libksba, gnupg, gnupg_pkcs11_scd utilities and installing them into the system, to provide an interface with PKCS # 11 tokens, you need to add the following lines to the gpg-agent.conf configuration file:

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

    Then, in the gnu-pkcs11-scd.conf configuration file, specify the PKCS # 11 libraries for the tokens to be used:

    #В примере предполагается использование токена #с библиотекой /usr/local/lib64/libls11sw2016.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/libls11sw2016.so
    #Если используется, например, токен ruTokenECP#provider-metokenpkcs11-library /usr/local/lib64/librutokenecp.so
    provider-mytokenpkcs11-cert-private
    …
    

    Everything is ready to go. Check that the gpg-agent module is running:

    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$

    So, the kleopatra enters the arena:



    The “Import” button is used primarily to import root certificates into the system, which are necessary to verify the validity of personal certificates.

    When launching the kleopatra utility, the PIN code for the connected token can be requested:



    In the future, this window will appear whenever you need access to the private key stored on the token, for example, when signing an e-mail or file.

    So, you choose the menu item "Tools → Manage Smartcards". If the certificates were not loaded from the token before, the following window will be displayed:



    In order to see the certificates on the connected token, just click the LoadCertificates button (Download Certificates):



    If there are no root certificates for user certificates (not certified), then you need to install them (File → Import or Import button):



    Now, after installing root certificates, you can see good certificates in the list of trusted certificates:



    If everything is good with the certificate ( has not expired, the chain of root certificates is present, the certificate is not revoked), it has been tested for validity, then such a certificate with the private key on the token can be used to create an electronic signature.

    All this can be clearly seen when installing a certificate, which by default will be used to sign letters in the KMail mail client (Settings → KMail Settings → Profiles → <owner profile> → Change → Cryptography → SMIME Signature Certificate):



    You can also try to sign the file “bad certificate ":



    Attempting to sign a document with a" bad certificate "fails, you need to use good certificates:



    In order for the letter to be signed with an electronic signature, just tick the Settings → Signature box ь letter: The



    letter will be automatically signed when it is sent (send button).

    In conclusion, once again I would like to draw the developers of applications for the domestic PKI that the GnuPG project as a development environment is not much inferior in its capabilities to both OpenSSL and NSS.

    Also popular now: