
Aujourd'hui, l'utilisation des certificats numériques X509 v.3 est devenue courante. De plus en plus de personnes les utilisent pour accéder au site Internet du Service d'État, du Service fédéral des impôts, des enchères électroniques, etc. Et de plus en plus de gens veulent savoir ce qu'il y a dans ce «coffre» appelé certificat. Et si le certificat est un analogue d'un passeport, comment peut-il être lu / consulté? Oui, dans les systèmes d'exploitation, il existe différents utilitaires de visualisation. Mais ils donneront peu au citoyen ordinaire. Prenons par exemple l'utilitaire gcr-viewer, qui, en fait, est l'outil standard pour la visualisation sur les systèmes Linux, et donc dans
les OS nationaux :

Visionneuse standard
L'utilitaire est bien fait, pratique. En général, il est conçu comme un utilitaire universel pour visualiser des fichiers contenant des données dans divers formats cryptographiques (certificats, demandes, signatures électroniques / PKCS # 7, conteneurs protégés PKCS # 12, etc.). Mais, malheureusement, il est conçu pour la cryptographie occidentale et ne prend pas en compte les OID qui sont entrés dans votre pays. Et si vous regardez la capture d'écran, lorsque les informations sur le titulaire du certificat s'affichent, des caractères incompréhensibles apparaissent. A gauche se trouvent les oides eux-mêmes et à droite sous la 16e forme, la structure asn1 avec leurs valeurs. Dans ce cas, ce sont OGRN (1.2.643.100.1), SNILS (1.2.643.100.3) et TIN (1.2.643.3.131.1.1). Et c'est ainsi qu'un citoyen ordinaire doit s'assurer qu'il s'agit bien de ses données. Ne pensez pas que ce soit uniquement sous Linux, c'est une caractéristique courante de tout visualiseur de certificats. Et si vous regardez plus loin, alors tout devient incompréhensible:

Certaines extensions, identificateurs et valeurs apparaissent. Dans ce cas, oid om 1.2.643.100.111 cache le nom du CIPF, qui a été utilisé par l'utilisateur pour générer la paire de clés, la clé privée à partir de laquelle a été utilisée pour signer la demande de certificat et la clé publique à partir de laquelle se trouve le certificat:

Et ici, le détenteur du certificat ne comprend pas grand-chose. Il ne comprend même pas quel algorithme a été utilisé pour générer la clé, GOST R 34.10-2001 ou GOST R 34.10-2012 et avec quelle longueur de clé.
Vous pouvez continuer à donner des exemples. Par exemple, si la validité du certificat est claire, alors où est la clé?
Il y a deux autres questions auxquelles les détenteurs de certificats aimeraient avoir une réponse: où puis-je obtenir une chaîne de certificats racine (ou mieux encore en obtenir un) et une question similaire sur la liste des certificats révoqués.
Et enfin, je voudrais avoir un utilitaire universel qui prend en compte les caractéristiques de la PKI / PKI russe, qui est vraiment multiplateforme et fonctionne sur
des OS nationaux et non
domestiques . Que développer? Bien sûr, dans un langage de script, ne serait-ce qu'en raison de leur nature multiplateforme.
Puis je me suis souvenu que le magnifique langage de script Tcl (Tool Command Language) avait récemment fêté ses
30 ans . C'est un plaisir de le programmer. Il a un grand nombre d'extensions (package) qui permettent presque tout. Donc, pour travailler avec les structures ASN, il y a un paquet asn. De plus, pour travailler avec des certificats (nous sommes intéressés à les analyser dans ce cas), il y a un paquet pki. Et pour développer une interface graphique, il existe un package Tk.
On peut en dire autant de Pyton avec Tkinter, de perl et de rubis. Chacun peut choisir selon son goût. Nous nous arrêtons à un tas de Tcl / Tk.
Nous emprunterons la conception graphique de l'utilitaire à l'utilitaire gcr-viewer. Et encore une exigence.
Comme Habr avait une
version anglaise , je voulais que l'utilitaire ait différentes interfaces (russe / anglais). Mais ce n'est pas la raison principale. Plus important encore, de plus en plus de citoyens du monde occidental deviennent des citoyens de la Fédération de Russie, par exemple le célèbre acteur Depardieu. Ils peuvent objecter: c'est un Français. Mais je suis aussi traducteur militaire du français:

Il est donc facile d'ajouter une interface en français. Mais je pense que Depardieu n'a aucun problème avec la langue anglaise. D'un autre côté, notre pays est multinational et ce serait bien que le logiciel domestique et l'OS domestique aient au moins plusieurs interfaces nationales.
Avançant un peu, voici ce qui en est ressorti:

Nous invitons un traducteur
Commençons donc par le «traducteur». Dans la capture d'écran, il se cache sous le drapeau national. La principale exigence pour un traducteur est la synchronisation de la traduction, c'est-à-dire la possibilité de passer à une autre langue à tout moment. Les fonctions du traducteur en Tcl / Tk sont exécutées par le package msgcat:
package require msgcat
Pour définir la langue actuelle, utilisez la commande suivante:
msgcat::mclocale ru
Le vocabulaire du «traducteur» est stocké dans le fichier ru.msg comme suit:
# msgcat::mcset # mcset namespace import -force msgcat::mcset # mcset ru "Language" "" …
Ce qui suit est le texte du test
script avec traducteur: #!/usr/bin/wish -f # msgcat package require msgcat # ru msgcat::mclocale ru # ::msgcat::mc # mc namespace import msgcat::mc # [msgcat::mclocale].msg. # . msgcat::mcload [file join [file dirname [info script]]] # image create photo rf_32x21_f -file rf_32x21.png image create photo gb_32x21_f -file gb_32x21.png #, label .lab -text "[mc Language]: " -relief flat -bd 0 –bg snow -anchor sw -width 10 button .but_lang -image rf_32x21_f -command ::changelang -relief flat -bd 0 pack .lab -side left -pady {2 0} pack .but_lang -side left # proc ::changelang {} { # # if {[msgcat::mclocale] == "ru"} { msgcat::mclocale en .but_lang configure -image gb_32x21_f } else { msgcat::mclocale ru .but_lang configure -image rf_32x21_f } # .lab configure -text "[mc Language]: " }
Dans ce script, la procédure :: changelang agit en tant que traducteur, qui est appelée lorsque le bouton .but_lang avec l'indicateur est enfoncé.
Si vous exécutez ce script, vous verrez clairement comment fonctionne le traducteur:

Obtenez la clé publique
Maintenant que nous avons choisi un traducteur, procédons à l'analyse du certificat. Pour ce faire, nous avons besoin du package pki:
package require pki).
Le package pki est conçu pour fonctionner avec les clés et certificats d'algorithme RSA. Si le certificat (proc :: pki :: x509 :: parse_cert) a été créé avec un type de clé différent, nous ne recevrons pas d'informations sur cette clé:
# Handle RSA public keys by extracting N and E 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 } }
Étonnamment, l'algorithme de clé publique renvoie toujours (ret (pubkey_algo))
La situation est la même avec l'analyse de la demande de certificat (proc :: pki :: pkcs :: parse_csr):
# Parse public key, based on type switch -- $pubkey_type { "rsaEncryption" { set pubkey [binary format B* $pubkey] ::asn::asnGetSequence pubkey pubkey_parts ::asn::asnGetBigInteger pubkey_parts key(n) ::asn::asnGetBigInteger pubkey_parts key(e) set key(n) [::math::bignum::tostr $key(n)] set key(e) [::math::bignum::tostr $key(e)] set key(l) [expr {2**int(ceil(log([::pki::_bits $key(n)])/log(2)))}] set key(type) rsa } default { return -code error "Unsupported key type: $pubkey_type" } }
Mais ici, il retourne même des informations sur l'erreur. Mais aujourd'hui, en plus de RSA, par exemple, des touches sur les courbes elliptiques de l'UE, y compris GOST R 34.10-2012 (GOST R 34.10-2001 existe également pour l'instant), sont utilisées.
Mais il suffit par défaut de renvoyer la structure ASN de la clé publique qui se trouve dans le certificat ou la demande, et l'utilisateur lui-même analysera la clé publique en fonction du type de clé. Pour ce faire, ajoutez simplement la structure de clé publique ASN en valeurs hexadécimales aux valeurs retournées:
proc ::pki::x509::parse_cert {cert} { . . . ::asn::asnGetSequence cert subject ::asn::asnGetSequence cert pubkeyinfo # ASN- . binary scan $pubkeyinfo H* ret(pubkey_pubkeyinfo) . . . }
Tout, rien d'autre à faire. De cette façon, la procédure :: pki :: x509 :: parse_cert renvoie la plupart des extensions de certificat pour la simple raison qu'elle ne sait pas comment les analyser (par exemple, subjectSignTool avec nos certificats qualifiés), c'est-à-dire donne à la discrétion de l'utilisateur.
En revanche, la procédure :: pki :: x509 :: parse_cert renvoie l'un des résultats d'un
certificat tbs qui contient toutes les informations du certificat, à l'exception de sa signature (signature) et de son type (signature_algo):
# set fd [open «cert.pem» r] chan configure –translation binary set datacert [read $fd] close $fd # array set cert_parse [::pki::x509::parse_cert $datacert] # tbs- set cert_tbs_hex $cert_parse(cert)
Nous écrivons la procédure pour extraire les informations de clé publique d'un certificat tbs:
proc ::pki::x509::parse_cert_pubkeyinfo {cert_tbs_hex} { array set ret [list] set wholething [binary format H* $cert_tbs_hex] ::asn::asnGetSequence wholething 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) } ::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 # hex asn- binary scan $pubkeyinfo H* ret(pubkeyinfo) return $ret(pubkeyinfo) }
Et comme nous nous intéressons à la cryptographie russe, nous écrirons immédiatement la procédure d'analyse de la clé publique GOST:
proc parse_key_gost {pubkeyinfo_hex} { array set ret [list] set pubkeyinfo [binary format H* $pubkeyinfo_hex] ::asn::asnGetSequence pubkeyinfo pubkey_algoid ::asn::asnGetObjectIdentifier pubkey_algoid ret(pubkey_algo) #, - if {[string first "1 2 643 " $ret(pubkey_algo)] == -1} { return [array get ret] } ::asn::asnGetBitString pubkeyinfo pubkey set pubkey [binary format B* $pubkey] # binary scan $pubkey H* ret(pubkey) ::asn::asnGetSequence pubkey_algoid pubalgost #OID - ::asn::asnGetObjectIdentifier pubalgost ret(paramkey) #OID - ::asn::asnGetObjectIdentifier pubalgost ret(hashkey) #puts "ret(paramkey)=$ret(paramkey)\n" #puts "ret(hashkey)=$ret(hashkey)\n" #parray ret # : , return [array get ret] }
Oui, je l'ai presque manqué: après avoir téléchargé le paquet pki, vous devez ajouter au tableau :: pki :: oids des oids qui caractérisent le certificat GOST et qualifié ou qui ne sont tout simplement pas dans ce tableau:
package require pki # 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.19) "GOST R 34.10-2001" set ::pki::oids(1.2.643.7.1.1.1.1) "GOST R 34.10-2012-256" set ::pki::oids(1.2.643.7.1.1.1.2) "GOST R 34.10-2012-512" set ::pki::oids(1.2.643.2.2.3) "GOST R 34.10-2001 with GOST R 34.11-94" set ::pki::oids(1.2.643.7.1.1.3.2) "GOST R 34.10-2012-256 with GOSTR 34.11-2012-256" set ::pki::oids(1.2.643.7.1.1.3.3) "GOST R 34.10-2012-512 with GOSTR 34.11-2012-512" set ::pki::oids(1.2.643.100.113.1) "KC1 Class Sign Tool" set ::pki::oids(1.2.643.100.113.2) "KC2 Class Sign Tool" . . .
Vous pouvez également reconstituer le vocabulaire du traducteur en ajoutant au fichier ru.msg:
mcset ru "GOST R 34.10-2001" " 34.10-2001" mcset ru "GOST R 34.10-2012-256" " 34.10-2012-256" mcset ru "GOST R 34.10-2012-512" " 34.10-2012-512" mcset ru "GOST R 34.10-2001 with GOST R 34.11-94" " 34.10-2001 34.11-94" mcset ru "GOST R 34.10-2012-256 with GOSTR 34.11-2012-256" " 34.10-2012-256 34.11-2012-256" mcset ru "GOST R 34.10-2012-512 with GOSTR 34.11-2012-512" " 34.10-2012-512 34.11-2012-512" . . .
:

Chaîne de certificats racine et liste de révocation de certificats
Comment obtenir une chaîne de certificats racine a déjà été discuté précédemment. Par analogie, une procédure est écrite pour obtenir une liste des certificats COS / CRL révoqués. Le code source de l'utilitaire et ses distributions pour Linux, OS X (macOS) et MS Windows peuvent être trouvés
Dans le code source, vous pouvez trouver toutes les procédures d'analyse des extensions de certificat.
Aux opposants à Tk (Tcl / Tk, Python / Tkinter, etc.), je propose de trouver, comme on dit, 10 (dix) différences entre les deux utilitaires: l'utilitaire gcr-viewer écrit en gtk et l'utilitaire certViewer développé en Tk:

Certificats PKCS # 11 Token / Smartcard
Ci-dessus, nous avons parlé de travailler avec des certificats (parcourir, obtenir la chaîne de certificats racine, listes de certificats révoqués, empreintes digitales par sha1 et sha256, etc.) stockés dans des fichiers. Mais il reste des certificats stockés sur les jetons / cartes à puce PKCS # 11. Et le désir naturel n'est pas seulement de les voir, puis de les exporter dans un fichier. Comment faire cela, nous décrirons dans l'article suivant:
