Infraestrutura de chave pública: biblioteca GCrypt como alternativa ao OpenSSL com suporte para criptografia russa

A segunda metade de 2018 está se aproximando e o "2000º ano" na PKI, com base na criptografia russa, deve chegar em breve. Isto é devido ao fato de que
Não é permitido usar o esquema de assinatura GOST R 34.10-2001 para gerar uma assinatura após 31 de dezembro de 2018!
Hoje, não faz sentido receber certificados com uma assinatura, de acordo com GOST R 34.10-2001.
Ao mesmo tempo, muitos serviços ou aplicativos são desenvolvidos com base no OpenSSL, que deu suporte ao trabalho com o GOST R 34.10-2001.

Hoje, porém, na versão padrão do openssl, não há suporte para o GOST R 34.11-2012 e o GOST R 34.10-2012. Além disso, na versão 1.1, o suporte à criptografia GOST é excluído da distribuição padrão (“O mecanismo GOST estava desatualizado e, portanto, foi removido.”).

Tudo isso nos leva a procurar formas alternativas de trabalhar com certificados, com assinatura eletrônica (“mensagens no formato CMS”) e outros objetos PKI baseados na nova criptografia russa.

Uma maneira possível é usar a biblioteca GCrypt . Esta biblioteca suporta os novos algoritmos GOST R 34.11-2012 (algoritmos de hash) e GOST R 34.10-2012 (algoritmos de assinatura).

Geração de par de chaves


Então, começamos gerando um par de chaves contendo chaves privadas e públicas. Atualmente, na criptografia russa, existem três tipos de chaves de assinatura com oid-s correspondente
- GOST R 34.10-2001 com um comprimento de chave de 256 bits, oid 1.2.643.2.2.19 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x13);
- GOST R 34.10-2012 com um comprimento de chave de 256 bits (doravante GOST R 34.10-12-256), oid 1.2.643.7.1.1.1.1 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x01);
- GOST R 34.10-2012 com um comprimento de chave de 512 bits (doravante GOST R 34.10-12-512), oid 1.2.643.7.1.1.1.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x02).
E observe imediatamente que, em termos de matemática, algoritmos de geração e sua implementação, as chaves do GOST R 34.10-2001 e GOST R 34.10-12-256 são absolutamente idênticas! Bem como algoritmos idênticos para a formação de assinaturas eletrônicas baseadas neles. Qual dessas chaves em questão pode ser julgada apenas por informações sobre a chave contida, por exemplo, no certificado. Então, por que levou dois oid diferentes? Apenas enfatize o fato de que, ao gerar uma assinatura eletrônica com a chave GOST R 34.10-2001, o hash obtido de acordo com o GOST R 34.10-94 deve ser usado e, ao usar a chave GOST R 34.10-12-256, o hash obtido de acordo com o GOST R 34.10-200 212 com um comprimento de 256 bits. Embora, para enfatizar isso, haja oid-s correspondentes:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algoritmo de assinatura GOST R 34.10-2001 com chave 256 com hash GOST R 34.11-94;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algoritmo de assinatura GOST R 34.10-2012 com chave 256 com hash GOST R 34.11-2012;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algoritmo de assinatura GOST R 34.10-2012 com chave 512 com hash GOST R 34.11-2012.
Portanto, há alguma classificação com oid-s, mas é isso.

As chaves da família GOST pertencem à família de chaves nas curvas elípticas. Para gerar um par de chaves, você precisa especificar um ponto base em uma curva elíptica.

O comitê técnico de padronização “Segurança da informação criptográfica” ( TC 26 ) recomendou o uso de dois pontos de base para as chaves GOST R 34.10-2012-512:
- GOST2012-tc26-A (apelido na terminologia libgcrypt) com oid 1.2.643.7.1.2.1.2.1 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01);
- GOST2012-tc26-B com oid 1.2.643.7.1.2.1.2.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x02);
Para chaves GOST R 34.10 com comprimento de 256 bits, três pontos base são recomendados:
- GOST2001-CryptoPro-A com oid om 1.2.643.2.2.35.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01);
- GOST2001-CryptoPro-B com oid 1.2.643.2.2.35.2 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02);
- GOST2001-CryptoPro-C com oid 1.2.643.2.2.35.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03).
Para chaves GOST R 34.10 com um comprimento de 256 bits, mais dois oid são definidos para os pontos base:
- GOST2001-CryptoPro-XchA com oid 1.2.643.2.2.36.0 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00);
- GOST2001-CryptoPro-XchB com oid 1.2.643.2.2.36.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x01).
No entanto, na realidade, esses oids se referem aos pontos base do GOST2001-CryptoPro-A com oid 1.2.643.2.2.35.1 e GOST2001-CryptoPro-C com oid 1.2.643.2.2.35.3, respectivamente.
E isso deve ser levado em consideração ao processar pontos de base com esses oid-s.

Para gerar um par de chaves, a função gcry_pk_genkey do seguinte formulário é usada:

gcry_error_t gcry_pk_genkey (gcry sexp t *key_pair, gcry sexp t key_spec ). 

Os parâmetros devem ser definidos na variável parms para gerar o par de chaves no formato da expressão S interna (sexp). Para gerar um par de chaves de acordo com GOST, os parâmetros são definidos na forma da seguinte expressão S:

 (genkey ( (curve _))),  

ecc determina a geração do par de chaves nas curvas elípticas, e o ponto base deve indicar o ponto específico recomendado pelo TK-26. É o ponto base especificado que determina qual par de chaves será gerado. Se você especificar, por exemplo, GOST2012-tc26-A ou GOST2012-tc26-B, um par de chaves será gerado de acordo com o GOST R 34.10-2012 com um comprimento de chave de 512 bits. Em vez do apelido do ponto base, você pode especificar oid diretamente:

 (genkey ( (curve «1.2.643.2.2.35.3»))) 

Neste último caso, supõe-se que o par de chaves seja gerado de acordo com o GOST R 34.10 com um comprimento de chave de 256 bits e um ponto base do GOST2001-CryptoPro-C.

Abaixo está um exemplo de programa C que demonstra a geração de chaves

GenKey.c
 #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <gcrypt.h> /*  S-*/ static void show_sexp (const char *prefix, gcry_sexp_t a) { char *buf; size_t size; if (prefix) fputs (prefix, stderr); size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = gcry_xmalloc (size); gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); fprintf (stderr, "%.*s", (int)size, buf); gcry_free (buf); } int main(int argc, char* argv[]) { gpg_error_t err; gcry_sexp_t key_spec, key_pair, pub_key, sec_key; /* */ char *curve_gost[] = {"GOST2001-CryptoPro-A", "GOST2001-CryptoPro-B", "GOST2001-CryptoPro-C", "GOST2012-tc26-A", "GOST2012-tc26-B", NULL}; /*     */ err = gcry_sexp_build (&key_spec, NULL, "(genkey (ecc (curve %s)))", curve_gost[1]); if (err) { fprintf(stderr, "creating S-expression failed: %s\n", gcry_strerror (err)); exit (1); } err = gcry_pk_genkey (&key_pair, key_spec); if (err){ fprintf(stderr, "creating %s key failed: %s\n", argv[1], gcry_strerror (err)); exit(1); } /* S-  */ show_sexp ("ECC GOST key pair:\n", key_pair); /*  */ pub_key = gcry_sexp_find_token (key_pair, "public-key", 0); if (! pub_key) { fprintf(stderr, "public part missing in key\n"); exit(1); } /* S-  */ show_sexp ("ECC GOST public key:\n", pub_key); /*  */ sec_key = gcry_sexp_find_token (key_pair, "private-key", 0); if (! sec_key){ fprintf(stderr, "private part missing in key\n"); exit(1); } /* S-  */ show_sexp ("ECC GOST private key:\n", sec_key); /* ,   */ gcry_sexp_release (key_pair); /* ,    */ gcry_sexp_release (key_spec); } 


Para transmitir o exemplo, você precisa executar o comando:

 $cc –o GenKey GenKey.c –lgcrypt $ 

Depois de iniciar o módulo GenKey, obtemos

par de chaves
 ECC GOST key pair: (key-data (public-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) ) ) (private-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) (d #1ABB5A62BFF88C97567B467C6F4017242FE344B4F4BC8906CE40A0F9D51CBE48#) ) ) ) ECC GOST public key: (public-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) ) ) ECC GOST private key: (private-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) (d #1ABB5A62BFF88C97567B467C6F4017242FE344B4F4BC8906CE40A0F9D51CBE48#) ) ) 

Agora, com a chave privada em mãos, você pode criar uma assinatura eletrônica.

Hashing de documentos


A criação de uma assinatura eletrônica (ES) de um documento começa com a obtenção de um valor de hash do documento que está sendo assinado. Para assinar um documento, um dos algoritmos pode ser selecionado:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algoritmo de assinatura GOST R 34.10-2001 com chave 256 com hash de acordo com GOST R 34.11-94 com comprimento de hash 256 bits;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algoritmo de assinatura GOST R 34.10-2012 com uma chave de 256 com hash de acordo com GOST R 34.11-2012 com um comprimento de 256 bits;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algoritmo de assinatura GOST R 34.10-2012 com uma chave 512 com hash de acordo com GOST R 34.11-2012 com um comprimento de hash de 512 bits.
Esses algoritmos determinam não apenas o tipo de chave privada que será usada para obter a assinatura eletrônica, mas também o algoritmo da função hash. A biblioteca GCrypt implementa todos os três tipos de funções. O algoritmo hash GOST R 34.11-94 (com o parâmetro oid 1.2.643.2.2.30.1 - 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01) é implementado sob o apelido GOSTR3411_CP, o algoritmo GOST R 34.11-2012 com comprimento 256 bits (oid 1.2.43.7.1.1.2.2 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02) são implementados sob o apelido STRIBOG256 e o ​​algoritmo GOST R 34.11-2012 com um comprimento de 512 bits (oid 1.2.43.7 .1.1.2.3 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03) é implementado sob o apelido STRIBOG512.

Abaixo está o código do módulo em C para calcular o valor do hash de um documento armazenado em um arquivo

digest_gcrypt.c:
 #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <gcrypt.h> int main(int argc, char* argv[]) { gpg_error_t err; int algo = GCRY_MD_NONE; int i; unsigned char *h; size_t size; gcry_md_hd_t hd; FILE *fp; unsigned char buf[1024]; char *dgst_gost[] = {"GOSTR3411_CP", "STRIBOG256", "STRIBOG512", NULL}; i = 0; /*  -*/ if (argc == 3) algo = gcry_md_map_name (argv[1]); if (algo == GCRY_MD_NONE) { fprintf(stderr, "Usage: digest_gcrypt <nick_name_digest> <file_for_digest>\n"); fprintf(stderr, "<nick_name_digest>="); while (dgst_gost[i] != NULL){ if (i > 0 ) fprintf(stderr, " | ", dgst_gost[i]); fprintf(stderr, "%s", dgst_gost[i]); i++; } fprintf(stderr, "\n"); exit (1); } /*   */ err = gcry_md_open(&hd, algo, 0); if (err) { fprintf (stderr, "LibGCrypt error %s\n", gcry_strerror (err)); exit (1); } /*  */ if (!strcmp (argv[2], "-")) fp = stdin; else fp = fopen (argv[2], "r"); if (fp == NULL) { fprintf(stderr, "Cannot fopen file=%s\n", argv[2]); exit(1); } /*   */ while (!feof (fp)) { size = fread (buf, 1, sizeof(buf), fp); gcry_md_write (hd, buf, size); } /*  */ h = gcry_md_read(hd, 0); /*  */ printf("  %s = %d ( )\n", argv[1], gcry_md_get_algo_dlen (algo)); printf("   %s:\n", argv[2]); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02x", h[i]); printf("\n"); fflush(stdout); /* */ gcry_md_reset(hd); gcry_md_close(hd); } 

Traduzimos este módulo e obtemos o utilitário de cálculo de hash:

 $cc -o digest_file digest_file.c -lgcrypt $./digest_file Usage: digest_gcrypt <nick_name_digest> <file_for_digest> <nick_name_digest>=GOSTR3411_CP | STRIBOG256 | STRIBOG512 $./digest_file STRIBOG256 digest_file.c   STRIBOG256 = 32 ( )    digest_file.c: f6818dfb26073747266dc721c332d703eb21f2b17e3433c809e0e23b68443d4a $ 

Formação da assinatura eletrônica e sua verificação


Agora que temos a chave privada e o hash do documento, também podemos gerar uma assinatura eletrônica do documento:

 gcry_error_t gcry_pk_sign (gcry sexp t *r_sig, gcry sexp t data, gcry sexp t skey ),  

r_sig -sexp-variable na qual a assinatura eletrônica será salva,
skey - uma variável sexp com uma chave privada (veja acima), usada para gerar uma assinatura,
data - sexp-variable, que indica o tipo de assinatura (no nosso caso, gost) e o hash do documento assinado.
Deve-se lembrar que o valor do hash fornecido à entrada gcry_pk_sign para gerar a assinatura GOST R 34.10 deve estar no formato big-endian, ou seja, de fato virou de cabeça para baixo (como um amigo disse: "A tradição russa de tratar bytes de digestão em ordem little-endian"). Com isso em mente, a preparação da variável sexo datap é a seguinte:

 ... gcry_sexp_t data; unsigned char c; int len_xy; gcry_mpi_t x; … /*    */ printf("%s\n", *((unsigned char *) &arch) == 0 ? " big-endian" : " little-endian"); len_xy = *((unsigned char *) &arch) == 0 ? 0:gcry_md_get_algo_dlen (algo); for (i = 0; i < (len_xy/2); i++) { c = *(h + i); *(h + i) = *(h + len_xy - i - 1); *(h + len_xy - i - 1) = c; } fprintf(stderr, " =%d\n", gcry_md_get_algo_dlen (algo)); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02X", h[i]); fflush(stdout); /*   mpi-*/ x = gcry_mpi_set_opaque_copy(NULL, h, gcry_md_get_algo_dlen (algo) * 8); /* sexp- data   –   */ err = gcry_sexp_build (&data, NULL, "(data (flags gost) (value %m))", x); /*    data */ show_sexp ("data :\n", data); 
Está tudo pronto para receber a assinatura e visualizá-la <fonte: lang = "cpp"> ...
gcry_sexp_t sig_r, sig_s;
...
/ * Assine o hash * /
err = gcry_pk_sign (& sig, dados, sec_key);
if (err) {
fprintf (stderr, "falha na assinatura:% s \ n", gcry_strerror (err));
saída (1);
}
/ * Imprimimos a assinatura * /
show_sexp ("ECC GOST SIG: \ n", sig);
/ * Selecione e imprima os componentes das assinaturas re es * /
sig_r = gcry_sexp_find_token (sig, "r", 0);
if (! sig_r) {
fprintf (stderr, "parte r ausente em sig \ n");
saída (1);
}
show_sexp ("Peça R do ECC GOST Sig: \ n", sig_r);
sig_s = gcry_sexp_find_token (sig, "s", 0);
if (! sig_s) {
fprintf (stderr, parte de s ausente em sig \ n ");
saída (1);
}
show_sexp ("ECC GOST Sig parte S: \ n", sig_s);
... Você pode verificar a assinatura da seguinte maneira:
 … err = gcry_pk_verify (sig, data, pub_key); if (err) { putchar ('\n'); show_sexp ("seckey:\n", sec_key); show_sexp ("data:\n", data); show_sexp ("sig:\n", sig); fprintf(stderr, "verify failed: %s\n", gcry_strerror (err)); exit(1); } … 

Verificação da assinatura eletrônica do GOST R 34.10 em certificados


Um dos principais objetos da infraestrutura de chave pública (PKI) é o certificado X509. As recomendações do TC-26 sobre a composição e a estrutura dos certificados estão estabelecidas no documento “ESPECIFICAÇÕES TÉCNICAS DE USO DOS ALGORITMOS GOST R 34.10, GOST R 34.11 NO PERFIL DE CERTIFICADO E CRL (REVISÃO DE CERTIFICADO DE CRIANÇAS) DA INFRA-ESTRUTURA ABERTA DA CHAVE 9 DA X. proteção da informação ”(ata nº 13 de 24/04/2014).” É de acordo com essas recomendações que todas as CAs credenciadas no Ministério das Comunicações da Rússia emitem certificados.

Para verificar a assinatura no certificado, é necessário (veja acima) obter o hash do certificado verificado, sua assinatura e a chave pública do certificado raiz. Observe que, para um certificado autoassinado, todos esses dados são armazenados em um certificado.
Para trabalhar com objetos PKI / PKI (certificados, CMS, solicitações
etc.), como regra, é usada a biblioteca KSBA, que no momento não suporta as recomendações do TK-26, embora exista experiência com esse suporte. Basicamente, nada impede a adição de suporte para as recomendações do TK-26 no projeto ksba.
Nesse estágio, no estágio de teste do GCrypt, é conveniente usar linguagens de script como Python , Tcl etc. para trabalhar com objetos PKI / PKI (certificados, etc.) com criptografia russa. A linguagem de script Tcl foi selecionada. É fácil e simples escrever um programa de protótipo nele, que pode ser transferido para a linguagem C. O Tcl inclui um pacote PKI que contém procedimentos para analisar objetos PKI, em particular, o procedimento para analisar os certificados :: pki :: x509 :: parse_cert. Com base no procedimento parse_cert, o procedimento parse_gost_cert foi desenvolvido, localizado no arquivo

parse_cert_gost_oid.tcl
 proc parse_cert_gost {cert} { # parray ::pki::oids #puts "parse_cert_gost=$cert" set cert_seq "" if { [string range $cert 0 9 ] == "-----BEGIN" } { array set parsed_cert [::pki::_parse_pem $cert "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"] set cert_seq $parsed_cert(data) } else { #FORMAT DER set cert_seq $cert } set finger [::sha1::sha1 $cert_seq] set ret(fingerprint) $finger binary scan $cert_seq H* certdb set ret(certdb) $certdb #puts "CERTDB=$certdb" array set ret [list] # Decode X.509 certificate, which is an ASN.1 sequence ::asn::asnGetSequence cert_seq wholething ::asn::asnGetSequence wholething cert set ret(cert) $cert set ret(cert) [::asn::asnSequence $ret(cert)] if {0} { set ff [open "/tmp/tbs.der" w] fconfigure $ff -translation binary puts -nonewline $ff $ret(cert) close $ff } binary scan $ret(cert) H* ret(cert) ::asn::asnPeekByte cert peek_tag if {$peek_tag != 0x02} { # Version number is optional, if missing assumed to be value of 0 ::asn::asnGetContext cert - asn_version ::asn::asnGetInteger asn_version ret(version) incr ret(version) } else { set ret(version) 1 } ::asn::asnGetBigInteger cert ret(serial_number) ::asn::asnGetSequence cert data_signature_algo_seq ::asn::asnGetObjectIdentifier data_signature_algo_seq ret(data_signature_algo) ::asn::asnGetSequence cert issuer ::asn::asnGetSequence cert validity ::asn::asnGetUTCTime validity ret(notBefore) ::asn::asnGetUTCTime validity ret(notAfter) ::asn::asnGetSequence cert subject ::asn::asnGetSequence cert pubkeyinfo binary scan $pubkeyinfo H* pubkeyinfoG set ret(pubkeyinfo) $pubkeyinfoG ::asn::asnGetSequence pubkeyinfo pubkey_algoid binary scan $pubkey_algoid H* pubkey_algoidG set ret(pubkey_algoid) $pubkey_algoidG ::asn::asnGetObjectIdentifier pubkey_algoid ret(pubkey_algo) ::asn::asnGetBitString pubkeyinfo pubkey set extensions_list [list] while {$cert != ""} { ::asn::asnPeekByte cert peek_tag switch -- [format {0x%02x} $peek_tag] { "0xa1" { ::asn::asnGetContext cert - issuerUniqID } "0xa2" { ::asn::asnGetContext cert - subjectUniqID } "0xa3" { ::asn::asnGetContext cert - extensions_ctx ::asn::asnGetSequence extensions_ctx extensions while {$extensions != ""} { ::asn::asnGetSequence extensions extension ::asn::asnGetObjectIdentifier extension ext_oid ::asn::asnPeekByte extension peek_tag if {$peek_tag == 0x1} { ::asn::asnGetBoolean extension ext_critical } else { set ext_critical false } ::asn::asnGetOctetString extension ext_value_seq set ext_oid [::pki::_oid_number_to_name $ext_oid] set ext_value [list $ext_critical] switch -- $ext_oid { id-ce-basicConstraints { ::asn::asnGetSequence ext_value_seq ext_value_bin if {$ext_value_bin != ""} { ::asn::asnGetBoolean ext_value_bin allowCA } else { set allowCA "false" } if {$ext_value_bin != ""} { ::asn::asnGetInteger ext_value_bin caDepth } else { set caDepth -1 } lappend ext_value $allowCA $caDepth } default { binary scan $ext_value_seq H* ext_value_seq_hex lappend ext_value $ext_value_seq_hex } } lappend extensions_list $ext_oid $ext_value } } } } set ret(extensions) $extensions_list ::asn::asnGetSequence wholething signature_algo_seq ::asn::asnGetObjectIdentifier signature_algo_seq ret(signature_algo) ::asn::asnGetBitString wholething ret(signature) # Convert values from ASN.1 decoder to usable values if needed set ret(notBefore) [::pki::x509::_utctime_to_native $ret(notBefore)] set ret(notAfter) [::pki::x509::_utctime_to_native $ret(notAfter)] set ret(serial_number) [::math::bignum::tostr $ret(serial_number)] set ret(data_signature_algo) [::pki::_oid_number_to_name $ret(data_signature_algo)] set ret(signature_algo) [::pki::_oid_number_to_name $ret(signature_algo)] set ret(pubkey_algo) [::pki::_oid_number_to_name $ret(pubkey_algo)] set ret(issuer) [::pki::x509::_dn_to_string $issuer] set ret(subject) [::pki::x509::_dn_to_string $subject] set ret(signature) [binary format B* $ret(signature)] binary scan $ret(signature) H* ret(signature) # Handle RSA public keys by extracting N and E #puts "PUBKEY_ALGO=$ret(pubkey_algo)" switch -- $ret(pubkey_algo) { "rsaEncryption" { set pubkey [binary format B* $pubkey] binary scan $pubkey H* ret(pubkey) ::asn::asnGetSequence pubkey pubkey_parts ::asn::asnGetBigInteger pubkey_parts ret(n) ::asn::asnGetBigInteger pubkey_parts ret(e) set ret(n) [::math::bignum::tostr $ret(n)] set ret(e) [::math::bignum::tostr $ret(e)] set ret(l) [expr {int([::pki::_bits $ret(n)] / 8.0000 + 0.5) * 8}] set ret(type) rsa } "GostR2012_256" - "GostR2012_512" - "GostR2001" - "1.2.643.2.2.19" - "1.2.643.7.1.1.1.1" - "1.2.643.7.1.1.1.2" { # gost2001, gost2012-256,gost2012-512 set pubkey [binary format B* $pubkey] #puts "LL=[string length $pubkey]" if {[string length $pubkey] < 100} { set pubk [string range $pubkey 2 end] } else { set pubk [string range $pubkey 3 end] } set pubkey_revert [string reverse $pubk] binary scan $pubkey_revert H* ret(pubkey_rev) binary scan $pubkey H* ret(pubkey) set ret(type) gost ::asn::asnGetSequence pubkey_algoid pubalgost #OID -  ::asn::asnGetObjectIdentifier pubalgost ret(paramkey) set ret(paramkey) [::pki::_oid_number_to_name $ret(paramkey)] #OID -   ::asn::asnGetObjectIdentifier pubalgost ret(hashkey) set ret(hashkey) [::pki::_oid_number_to_name $ret(hashkey)] #puts "ret(paramkey)=$ret(paramkey)\n" #puts "ret(hashkey)=$ret(hashkey)\n" } } return [array get ret] } proc set_nick_for_oid {} { # set ::pki::oids(1.2.643.2.2.19) "gost2001pubKey" # set ::pki::oids(1.2.643.2.2.3) "gost2001withGOST3411_94" set ::pki::oids(1.2.643.100.1) "OGRN" set ::pki::oids(1.2.643.100.5) "OGRNIP" set ::pki::oids(1.2.643.3.131.1.1) "INN" set ::pki::oids(1.2.643.100.3) "SNILS" #  set ::pki::oids(1.2.643.2.2.3) "  34.10-2001-256" set ::pki::oids(1.2.643.7.1.1.3.2) "  34.10-2012-256" set ::pki::oids(1.2.643.7.1.1.3.3) "  34.10-2012-512" # set ::pki::oids(1.2.643.2.2.3) "gost" # set ::pki::oids(1.2.643.7.1.1.3.2) "gost" # set ::pki::oids(1.2.643.7.1.1.3.3) "gost" #  # set ::pki::oids(1.2.643.2.2.19) "  34.10-2001" # set ::pki::oids(1.2.643.7.1.1.1.1) "  34.10-2012 256 " # set ::pki::oids(1.2.643.7.1.1.1.2) "  34.10-2012 512 " set ::pki::oids(1.2.643.2.2.19) "GostR2001" set ::pki::oids(1.2.643.7.1.1.1.1) "GostR2012_256" set ::pki::oids(1.2.643.7.1.1.1.2) "GostR2012_512" #Oid-     34.10-2001    34.10-2012-256 set ::pki::oids(1.2.643.2.2.35.0) "GOST2001-test" set ::pki::oids(1.2.643.2.2.35.1) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.35.2) "GOST2001-CryptoPro-B" set ::pki::oids(1.2.643.2.2.35.3) "GOST2001-CryptoPro-C" # { "GOST2001-CryptoPro-A", set ::pki::oids("GOST2001-CryptoPro-XchA" }, # { "GOST2001-CryptoPro-C", set ::pki::oids("GOST2001-CryptoPro-XchB" }, set ::pki::oids(1.2.643.2.2.36.0) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.36.1) "GOST2001-CryptoPro-C" #Oid-     34.10-2012-512 set ::pki::oids(1.2.643.7.1.2.1.2.1) "GOST2012-tc26-A" set ::pki::oids(1.2.643.7.1.2.1.2.2) "GOST2012-tc26-B" #Nick   set ::pki::oids(1.2.643.7.1.1.2.2) "STRIBOG256" set ::pki::oids(1.2.643.7.1.1.2.3) "STRIBOG512" set ::pki::oids(1.2.643.2.2.30.1) "GOSTR3411_CP" } 


O procedimento parse_gost_cert foi projetado para analisar certificados com base na criptografia russa.
Este arquivo também contém o procedimento para atribuir apelido-mov a oid-am da criptografia russa, levando em consideração o projeto GCrypt:
 proc set_nick_for_oid {} { set ::pki::oids(1.2.643.100.1) "OGRN" set ::pki::oids(1.2.643.100.5) "OGRNIP" set ::pki::oids(1.2.643.3.131.1.1) "INN" set ::pki::oids(1.2.643.100.3) "SNILS" #  set ::pki::oids(1.2.643.2.2.3) "  34.10-2001-256" set ::pki::oids(1.2.643.7.1.1.3.2) "  34.10-2012-256" set ::pki::oids(1.2.643.7.1.1.3.3) "  34.10-2012-512" #  set ::pki::oids(1.2.643.2.2.19) "GostR2001" set ::pki::oids(1.2.643.7.1.1.1.1) "GostR2012_256" set ::pki::oids(1.2.643.7.1.1.1.2) "GostR2012_512" #Oid-     34.10-2001    34.10-2012-256 set ::pki::oids(1.2.643.2.2.35.0) "GOST2001-test" set ::pki::oids(1.2.643.2.2.35.1) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.35.2) "GOST2001-CryptoPro-B" set ::pki::oids(1.2.643.2.2.35.3) "GOST2001-CryptoPro-C" set ::pki::oids(1.2.643.2.2.36.0) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.36.1) "GOST2001-CryptoPro-C" #Oid-     34.10-2012-512 set ::pki::oids(1.2.643.7.1.2.1.2.1) "GOST2012-tc26-A" set ::pki::oids(1.2.643.7.1.2.1.2.2) "GOST2012-tc26-B" #Nick   set ::pki::oids(1.2.643.7.1.1.2.2) "STRIBOG256" set ::pki::oids(1.2.643.7.1.1.2.3) "STRIBOG512" set ::pki::oids(1.2.643.2.2.30.1) "GOSTR3411_CP" } 

O procedimento parse_gost_cert permite obter um certificado TBS , assinatura de certificado, tipo de assinatura, chave pública. Se estamos considerando um certificado autoassinado, isso é suficiente para verificar a assinatura. Se verificarmos a assinatura de um certificado assinado (emitido) por outro certificado (o emissor e o sujeito do certificado não coincidem), o procedimento para obter informações para verificar a assinatura é o seguinte:

- a partir do certificado verificado, extraímos seu certificado TBS, tipo de assinatura e a própria assinatura;
- extraímos a chave pública do certificado raiz.

O mais responsável na preparação dos dados de origem para verificar a assinatura do certificado é a adesão estrita às recomendações do TK-26. Para um valor de chave pública, eles se parecem com isso:
A representação de chave pública de GostR3410-2012-256-PublicKey é idêntica à representação de chave pública de GOST R 34.10-2001 [IETF RFC 4491] e DEVE conter 64 octetos, onde os primeiros 32 octetos contêm a coordenada x na exibição little-endian e os segundos 32 octetos contêm a coordenada y na visão little-endian.

A exibição da chave pública GostR3410-2012-512-PublicKey DEVE conter
128 octetos, onde os primeiros 64 octetos contêm a coordenada x na visualização little-endian e os segundos 64 octetos contêm a coordenada y na visualização little-endian.
Ao enviar uma assinatura, você deve ser guiado pelo seguinte:
O algoritmo de assinatura GOST R 34.10-2012 com um comprimento de código hash de 256 bits é usado para gerar uma assinatura digital na forma de dois números de 256 bits, re s. Sua representação na forma de uma cadeia de octetos (OCTET STRING) é idêntica à representação da assinatura GOST R 34.10-2001 [IETF RFC 4491] e consiste em 64 octetos; os primeiros 32 octetos contêm o número s na representação de big endian (o octeto mais alto é escrito primeiro) e os segundos 32 octetos contêm o número r na representação de big endian.

O algoritmo de assinatura GOST R 34.10-2012 com comprimento de código de 512 hash é usado para gerar uma assinatura digital na forma de dois números de 512 bits, chaves públicas de acordo com re. Sua representação como uma cadeia de octetos (OCTET STRING) consiste em 128 octetos; os primeiros 64 octetos contêm o número s na representação de big endian (o octeto mais alto é escrito primeiro) e os segundos 64 octetos contêm o número r na representação de big endian.
Com base nessas recomendações, o módulo Tcl parse_certs_for_verify_load.tcl prepara os dados de origem para a verificação da assinatura do certificado

é assim:
 #!/usr/bin/tclsh #  PKI package require pki #     -  source parse_cert_gost_oid.tcl if {$argc != 2} { puts "Usage: parse_certs_for_verify_load.tcl < > < >" exit 1 } #  if {[file exists "[lindex $argv 0]"] == 0 } { puts "Usage: parse_certs_for_verify_load.tcl < > < >" puts "  [lindex $argv 0]" exit 1 } #  if {[file exists "[lindex $argv 1]"] == 0 } { puts "Usage: parse_certs_for_verify_load.tcl < > < >" puts "  [lindex $argv 1]" exit 1 } # nick-  - oid- set_nick_for_oid set file [lindex $argv 0] set f [open $file r] set cert [read $f] close $f #READ DER-format if { [string range $cert 0 9 ] != "-----BEGIN" } { set fd [open $file] chan configure $fd -translation binary set cert [read $fd] close $fd } array set cert_user [parse_cert_gost $cert] #  -26 set len_sign [expr [string length $cert_user(signature)] /2] set sign_r [string range $cert_user(signature) $len_sign end] set sign_s [string range $cert_user(signature) 0 [expr $len_sign - 1]] #puts " : $file" set file [lindex $argv 1] set f [open $file r] set cert [read $f] close $f #READ DER if { [string range $cert 0 9 ] != "-----BEGIN" } { set fd [open $file] chan configure $fd -translation binary set cert [read $fd] close $fd } #     array set cert_ca [parse_cert_gost $cert] #  -26 set len_key [expr [string length $cert_ca(pubkey_rev)]/2] set key_pub_left [string range $cert_ca(pubkey_rev) $len_key end] set key_pub_right [string range $cert_ca(pubkey_rev) 0 [expr $len_key - 1]] puts "/* C-:      */" #TBS-  puts "char tbc\[\] = \"[string toupper $cert_user(cert)]\";" # - puts "char hash_type\[\] = \"$cert_ca(hashkey)\";" #    puts "unsigned char pub_key_ca\[\] = \"(public-key \"" puts "\"(ecc \"" puts "\" (curve $cert_ca(paramkey))\"" puts "\" (q #04[string toupper $key_pub_left$key_pub_right]#)\"" puts "\")\"" puts "\")\";" #   puts "unsigned char sig_cert\[\] = \"(sig-val\"" puts "\"($cert_ca(type) \"" puts "\" (r #[string toupper $sign_r]#)\"" puts "\" (s #[string toupper $sign_s]#)\"" puts "\")\"" puts "\")\";" puts "/*    TEST_from_TCL.h*/" puts "/*  TEST_from_Tcl.c: cc -o TEST_from_Tcl TEST_from_Tcl.c -lgcrypt    TEST_from_Tcl*/" 


Para o teste, tomamos dois certificados reais:

certificado \ "UTs 1 IS GUTs.pem \"
-----BEGIN CERTIFICATE-----
MIIGrDCCBlugAwIBAgILAOvBBVQAAAAAAFkwCAYGKoUDAgIDMIIBSjEeMBwGCSqG
SIb3DQEJARYPZGl0QG1pbnN2eWF6LnJ1MQswCQYDVQQGEwJSVTEcMBoGA1UECAwT
Nzcg0LMuINCc0L7RgdC60LLQsDEVMBMGA1UEBwwM0JzQvtGB0LrQstCwMT8wPQYD
VQQJDDYxMjUzNzUg0LMuINCc0L7RgdC60LLQsCwg0YPQuy4g0KLQstC10YDRgdC6
0LDRjywg0LQuIDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQ
vtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMB
ARIMMDA3NzEwNDc0Mzc1MUEwPwYDVQQDDDjQk9C+0LvQvtCy0L3QvtC5INGD0LTQ
vtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgDAeFw0xNjAzMTYxMjAy
NTFaFw0yNzA3MTIxMjAyNTFaMIIBITEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQz
NzUxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEeMBwGCSqGSIb3DQEJARYPZGl0
QG1pbnN2eWF6LnJ1MTwwOgYDVQQJDDMxMjUzNzUg0LMuINCc0L7RgdC60LLQsCDR
g9C7LiDQotCy0LXRgNGB0LrQsNGPINC0LjcxLDAqBgNVBAoMI9Cc0LjQvdC60L7Q
vNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRUwEwYDVQQHDAzQnNC+0YHQutCy0LAx
HDAaBgNVBAgMEzc3INCzLiDQnNC+0YHQutCy0LAxCzAJBgNVBAYTAlJVMRswGQYD
VQQDDBLQo9CmIDEg0JjQoSDQk9Cj0KYwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYH
KoUDAgIeAQNDAARAx70Y7WYQ4ODtdiSSx3MJnr1GQBEIExiPO/LWj1TRKES1OcDI
YgtdOBGVYSvbsStl10jkAOG0OpnGsd2by4m+LaOCA0MwggM/MA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFBGIaV7vyOlz23pXNbzSAfMF/qfRMAsGA1UdDwQEAwIB
hjCCAYsGA1UdIwSCAYIwggF+gBSLmDuJGFHo75wCeLjqyNQgslXJXaGCAVKkggFO
MIIBSjEeMBwGCSqGSIb3DQEJARYPZGl0QG1pbnN2eWF6LnJ1MQswCQYDVQQGEwJS
VTEcMBoGA1UECAwTNzcg0LMuINCc0L7RgdC60LLQsDEVMBMGA1UEBwwM0JzQvtGB
0LrQstCwMT8wPQYDVQQJDDYxMjUzNzUg0LMuINCc0L7RgdC60LLQsCwg0YPQuy4g
0KLQstC10YDRgdC60LDRjywg0LQuIDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB
0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDEx
GjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MUEwPwYDVQQDDDjQk9C+0LvQvtCy
0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgIIQ
NGgeQMtB7zOpoLfIdpKaKTBZBgNVHR8EUjBQMCagJKAihiBodHRwOi8vcm9zdGVs
ZWNvbS5ydS9jZHAvZ3VjLmNybDAmoCSgIoYgaHR0cDovL3JlZXN0ci1wa2kucnUv
Y2RwL2d1Yy5jcmwwJgYFKoUDZG8EHQwb0JrRgNC40L/RgtC+LdCf0YDQviBDU1Ag
My42MCUGA1UdIAQeMBwwCAYGKoUDZHEBMAgGBiqFA2RxAjAGBgRVHSAAMIHGBgUq
hQNkcASBvDCBuQwj0J/QkNCa0JwgwqvQmtGA0LjQv9GC0L7Qn9GA0L4gSFNNwrsM
INCf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0KPQpsK7DDbQl9Cw0LrQu9GO0YfQ
tdC90LjQtSDihJYgMTQ5LzMvMi8yLTk5OSDQvtGCIDA1LjA3LjIwMTIMONCX0LDQ
utC70Y7Rh9C10L3QuNC1IOKEliAxNDkvNy8xLzQvMi02MDMg0L7RgiAwNi4wNy4y
MDEyMAgGBiqFAwICAwNBAKVYokUvb7XAMPJF38ZPKO2BFBldmGEfqsfmsiO35Y52
kTkx512H3YLqWMrOLjIfVMJhc+DTCNeXWY6bhK4/DRU=
-----END CERTIFICATE-----

e seu certificado raiz \ "SEC Minkomsvyaz.pem \":
-----BEGIN CERTIFICATE-----
MIIFGTCCBMigAwIBAgIQNGgeQMtB7zOpoLfIdpKaKTAIBgYqhQMCAgMwggFKMR4w
HAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYD
VQQIDBM3NyDQsy4g0JzQvtGB0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAx
PzA9BgNVBAkMNjEyNTM3NSDQsy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXR
gNGB0LrQsNGPLCDQtC4gNzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfR
jCDQoNC+0YHRgdC40LgxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqF
AwOBAwEBEgwwMDc3MTA0NzQzNzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg
0YPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMB4XDTEyMDcy
MDEyMzExNFoXDTI3MDcxNzEyMzExNFowggFKMR4wHAYJKoZIhvcNAQkBFg9kaXRA
bWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYDVQQIDBM3NyDQsy4g0JzQvtGB
0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxPzA9BgNVBAkMNjEyNTM3NSDQ
sy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXRgNGB0LrQsNGPLCDQtC4gNzEs
MCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40LgxGDAW
BgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQz
NzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHRgtC+0LLQtdGA
0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMGMwHAYGKoUDAgITMBIGByqFAwICIwEGByqF
AwICHgEDQwAEQI+lv3kQI8jWka1kMVdbvpvFioP0Pyn3Knmp+2XD6KgPWnXEIlSR
X8g/IYracDr51YsNc2KE3C7mkH6hA3M3ofujggGCMIIBfjCBxgYFKoUDZHAEgbww
gbkMI9Cf0JDQmtCcIMKr0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7DCDQn9CQ0Jog
wqvQk9C+0LvQvtCy0L3QvtC5INCj0KbCuww20JfQsNC60LvRjtGH0LXQvdC40LUg
4oSWIDE0OS8zLzIvMi05OTkg0L7RgiAwNS4wNy4yMDEyDDjQl9Cw0LrQu9GO0YfQ
tdC90LjQtSDihJYgMTQ5LzcvMS80LzItNjAzINC+0YIgMDYuMDcuMjAxMjAuBgUq
hQNkbwQlDCPQn9CQ0JrQnCDCq9Ca0YDQuNC/0YLQvtCf0YDQviBIU03CuzBDBgNV
HSAEPDA6MAgGBiqFA2RxATAIBgYqhQNkcQIwCAYGKoUDZHEDMAgGBiqFA2RxBDAI
BgYqhQNkcQUwBgYEVR0gADAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUi5g7iRhR6O+cAni46sjUILJVyV0wCAYGKoUDAgIDA0EA23Re
ec/Y27rpMi+iFbgWCazGY3skBTq5ZGsQKOUxCe4mO7UBDACiWqdA0nvqiQMXeHgq
o//fO9pxuIHtymwyMg==
-----END CERTIFICATE-----

Prepararemos os dados iniciais para verificar o certificado "UTs 1 IS GUTs.pem":

 $ ./parse_certs_for_verify_load.tcl " 1  .pem" " .pem" > TEST_from_TCL.h $echo "  TEST_from_TCL.h" $cat TEST_from_TCL.h /* C-:      */ char tbc[] = "3082065B . . . "; char hash_type[] = "GOSTR3411_CP"; unsigned char pub_key_ca[] = "(public-key " "(ecc " " (curve GOST2001-CryptoPro-A)" " (q #040FA8E8C365FBA9792AF7293FF4838AC59BBE5B573164AD91D6C8231079BFA58FFBA1377303A17E90E62EDC8462730D8BD5F93A70DA8A213FC85F915422C4755A#)" ")" ")"; unsigned char sig_cert[] = "(sig-val" "(gost " " (r #913931E75D87DD82EA58CACE2E321F54C26173E0D308D797598E9B84AE3F0D15#)" " (s #A558A2452F6FB5C030F245DFC64F28ED8114195D98611FAAC7E6B223B7E58E76#)" ")" ")"; /*    TEST_from_TCL.h*/ /*  TEST_from_Tcl.c: cc -o TEST_from_Tcl TEST_from_Tcl.c -lgcrypt    TEST_from_Tcl*/ $ 

A verificação da assinatura do certificado é realizada pelo módulo:

TEST_from_TCL.c
 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <gcrypt.h> #include "TEST_from_TCL.h" #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xmalloc(a) gcry_xmalloc ((a)) static void show_sexp (const char *prefix, gcry_sexp_t a) { char *buf; size_t size; if (prefix) fputs (prefix, stderr); size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = gcry_xmalloc (size); gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); fprintf (stderr, "%.*s", (int)size, buf); gcry_free (buf); } /* Convert STRING consisting of hex characters into its binary representation and return it as an allocated buffer. The valid length of the buffer is returned at R_LENGTH. The string is delimited by end of string. The function returns NULL on error. */ static void * hex2buffer (const char *string, size_t *r_length) { const char *s; unsigned char *buffer; size_t length; buffer = xmalloc (strlen(string)/2+1); length = 0; for (s=string; *s; s +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return NULL; /* Invalid hex digits. */ ((unsigned char*)buffer)[length++] = xtoi_2 (s); } *r_length = length; return buffer; } int main(int argc, char* argv[]) { gpg_error_t err; int algo; gcry_md_hd_t hd; unsigned char *tbs_ptr; size_t len_tbs; int i; unsigned char *h; gcry_sexp_t pub_key; gcry_sexp_t data; gcry_sexp_t sig; gcry_mpi_t x; int len_xy; unsigned char c; /*   little-endian  big-endian*/ unsigned short arch = 1; /* 0x0001 */ tbs_ptr = hex2buffer(tbc, &len_tbs); if (tbs_ptr == NULL) { fprintf (stderr, "Bad tbs\n"); exit(1); } algo = gcry_md_map_name (hash_type); if (algo == GCRY_MD_NONE) { fprintf (stderr, "Unknown algorithm '%s'\n", hash_type); exit (1); } err = gcry_md_open(&hd, algo, 0); if (err) { fprintf (stderr, "LibGCrypt error %s/%s\n", gcry_strsource (err), gcry_strerror (err)); exit (1); } gcry_md_write (hd, tbs_ptr, len_tbs); h = gcry_md_read(hd, 0); // len_xy = gcry_md_get_algo_dlen (algo); /*    */ printf("%s\n", *((unsigned char *) &arch) == 0 ? " big-endian" : " little-endian"); len_xy = *((unsigned char *) &arch) == 0 ? 0:gcry_md_get_algo_dlen (algo); for (i = 0; i < (len_xy/2); i++) { c = *(h + i); *(h + i) = *(h + len_xy - i - 1); *(h + len_xy - i - 1) = c; } fprintf(stderr, " =%d\n", gcry_md_get_algo_dlen (algo)); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02X", h[i]); // printf("\n %s\n", tbc); fflush(stdout); /*  */ x = gcry_mpi_set_opaque_copy(NULL, h, gcry_md_get_algo_dlen (algo) * 8); /*  */ gcry_md_reset(hd); gcry_md_close(hd); /* */ err = gcry_sexp_build (&data, NULL, "(data (flags gost) (value %m))", x); show_sexp ("ECC GOST data cert:\n", data); fprintf (stderr, "\nStep 1\n"); /*    */ err = gcry_sexp_sscan (&pub_key, NULL, pub_key_ca, strlen (pub_key_ca)); if (err){ fprintf(stderr, "TEST_SEXP: er gcry_sexp_sscan for pub_key_ca\n"); exit(1); } show_sexp ("ECC GOST public key:\n", pub_key); fprintf (stderr, "Step 2\n"); /*   */ err = gcry_sexp_sscan (&sig, NULL, sig_cert, strlen (sig_cert)); if (err){ fprintf(stderr, "TEST_SEXP: er gcry_sexp_sscan for sig_cert\n"); exit(1); } show_sexp ("ECC GOST sig cert:\n", sig); fprintf (stderr, "Step 3\n"); /*  */ err = gcry_pk_verify (sig, data, pub_key); if (err) { fprintf (stderr, "TEST_SEXP: verify cert failed\n"); exit (1); } fprintf (stderr, "TEST_SEXP: verify cert OK!!\n"); } 

Traduzimos e executamos o utilitário TEST_from_TCL:

 $cc –o TEST_from_TCL TEST_from_TCL.c –lgcrypt $./TEST_from_TCL  little-endian  =32 D485903E7E8D60820118329060C558B9C733D53CA608C0C79363ECE7B4C1F799ECC GOST data cert: (data (flags gost) (value #D485903E7E8D60820118329060C558B9C733D53CA608C0C79363ECE7B4C1F799#) ) Step 1 ECC GOST public key: (public-key (ecc (curve GOST2001-CryptoPro-A) (q #040FA8E8C365FBA9792AF7293FF4838AC59BBE5B573164AD91D6C8231079BFA58FFBA1377303A17E90E 62EDC8462730D8BD5F93A70DA8A213FC85F915422C4755A#) ) ) Step 2 ECC GOST sig cert: (sig-val (gost (r #913931E75D87DD82EA58CACE2E321F54C26173E0D308D797598E9B84AE3F0D15#) (s #A558A2452F6FB5C030F245DFC64F28ED8114195D98611FAAC7E6B223B7E58E76#) ) ) Step 3 TEST_SEXP: verify cert OK!! $ 

Como você pode ver, a verificação da assinatura do certificado "UTs 1 IS GUTs.pem" foi bem-sucedida. Resta verificar o próprio certificado raiz "GUT Minkomsvyaz.pem". É simples, basta executar o comando:

 $ parse_certs_for_verify_load.tcl " .pem" " .pem" > TEST_from_TCL.h $  .. 

Se alguém quiser verificar os certificados com outras chaves GOST (GOST R 34.10-2012-256 ou GOST R 34.10-2012-512), poderá usar o CAFL63 CA e preparar quaisquer certificados:

imagem

Portanto, as pesquisas realizadas mostraram que a biblioteca GCrypt pode muito bem costumava trabalhar com criptografia russa. A perspectiva imediata é vista na finalização da biblioteca KSBA e na expansão do pacote pki da linguagem de script Tcl (ou talvez Python) com suporte à criptografia russa.

Quem quiser usar a biblioteca GCrypt para criptografia, recomendo uma olhada aqui .

Source: https://habr.com/ru/post/pt414249/


All Articles