English-language cross-platform utility for viewing Russian qualified x509 certificates

imageToday, the use of digital certificates X509 v.3 has become commonplace. More and more people use them to access the State Services website, the Federal Tax Service, electronic trading, etc. And more and more people want to know what is in this "chest" called a certificate. And if the certificate is an analogue of a passport , then how can it be read / viewed. Yes, in operating systems there are various utilities for viewing. But they will give little to the ordinary citizen. Take, for example, the gcr-viewer utility, which is essentially the standard tool for viewing on Linux systems, and therefore on domestic operating systems :

Standard viewer

The utility is well done, convenient. And generally conceived as a universal utility for viewing files containing data in various cryptographic formats (certificates, requests, electronic signatures / PKCS # 7, secure PKCS # 12 containers, etc.). But, unfortunately, it is designed for Western cryptography and does not take into account the OIDs that are entered in your country. And if you look at the screenshot, already when displaying information about the certificate holder there are incomprehensible characters. On the left, these are the oids themselves, and on the right in the 16th form, the asn1 structure with their values. In this case, it is OGRN (1.2.643.100.1), SNILS (1.2.643.100.3) and TIN (1.2.643. And that's how an ordinary citizen should make sure that this is his data. Do not think that this is only in Linux, it is a common feature of any certificate viewer. And if you look further, it becomes generally incomprehensible:

Some extensions, identifiers and values ​​appear. In this case, under oid-ohm 1.2.643.100.111 hides the name of the SKZI, which was used by the user to generate a key pair, the private key of which was used to sign the certificate request, and the public key of which lies in the certificate:

And here the owner does not understand much certificate. He does not even understand what algorithm was used when generating the key, or GOST R 34.10-2001, or GOST R 34.10-2012, and with what key length.

You can continue to give examples. For example, if the validity of the certificate is clear, then where is the key validity period?

There are two more questions to which certificate holders would like to have an answer: where to get the chain of root certificates (or better still just get) and a similar question on the list of revoked certificates.

And lastly, I would like to have a universal utility that takes into account the peculiarities of the Russian PKI / PKI, which is truly cross-platform and works on domestic and non- domestic operating systems . What to develop? Of course, in the scripting language, if only because of their cross-platform nature.

Then I remembered that I recently celebrated my 30th birthdayexcellent scripting language Tcl (Tool Command Language). It is a pleasure to program on it. He has a huge number of extensions (package), which allow almost everything. So for work with ASN-structures there is a package asn. Moreover, to work with certificates (we are interested in their analysis in this case) there is a pki package. And for the development of the graphical interface there is a package Tk.

All the same can be said about Pyton with Tkinter, and about perl, and about ruby. Everyone can choose according to their taste. We stop at a bunch of Tcl / Tk.

We will borrow graphic design for the utility from the gcr-viewer utility. And one more requirement.

Since Habr has an English version, I wanted the utility to have various interfaces (Russian / English). But this is not the main reason. More importantly, more and more citizens of the Western world are becoming citizens of the Russian Federation, for example, the world-famous actor Depardieu. It may be objected - he is French. But I am also a military translator from French:

So it’s easy to add a French interface. But I think Depardieu has no problems with English. On the other hand, our country is multinational and it would be quite good if domestic software, domestic operating systems had at least several national interfaces.
A little ahead, here's what came of it:

We invite the translator

So let's start with the "translator". In the screenshot, he is hiding under the national flag. The main requirement for the translator is the simultaneous translation, i.e. the ability to switch to another language at any time. The functions of the translator in Tcl / Tk are performed by the msgcat package:

package require msgcat

To set the current language, use the following command:

msgcat::mclocale ru

The vocabulary of the “translator” is stored in the ru.msg file as follows:

#Импортируем процедуру msgcat::mcset и в дальнейшем к ней 
# обращаемся по короткому имени mcset 
namespace import -force msgcat::mcset 
#         На английском  Русский перевод
mcset ru "Language" 	"Язык"

Below is the text of the test
script with translator:
#!/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]: " 

In this script, the procedure :: changelang acts as a translator, called when the .but_lang button is pressed with a flag.

If you run this script, it will be clearly seen how the translator works:

We get the public key

Now that we have decided on a translator, let's proceed to the analysis of the certificate. For this we need the pki package:

package require pki). 

The pki package is sharpened to work with the keys and certificates of the RSA algorithm. If the certificate (proc :: pki :: x509 :: parse_cert) is created with a different key type, then we will not receive information about this key:

# 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

Surprisingly, the public key algorithm is still returned (ret (pubkey_algo)). The
same is true for parsing a certificate request (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"

But here at least it returns information about the error. But today, in addition to RSA, for example, the keys on the elliptic curves of the EU, including GOST R 34.10-2012, are in use (GOST R 34.10-2001 is also running for now).

But it is enough by default (default) to return the ASN-structure of the public key contained in the certificate or request, and the user himself, depending on the type of key, will parse the public key. For this, it is enough to add to the returned values ​​the ASN structure of the public key in hexadecimal form:

proc ::pki::x509::parse_cert {cert} {
	. . . 
	::asn::asnGetSequence cert subject
	::asn::asnGetSequence cert pubkeyinfo
#Добавляем в возвращаемый массив ASN-структуру публичного ключа.
       binary scan $pubkeyinfo H* ret(pubkey_pubkeyinfo)
	. . .

Anything more is not necessary. In this way, the procedure (proc :: pki :: x509 :: parse_cert returns the majority of certificate extensions for the simple reason that he knows how to parse (eg, subjectSignTool our Qualified Certificates), that gives to the user's discretion.

With On the other hand, the procedure :: pki :: x509 :: parse_cert returns with one of the results a tbs-certificate that contains all the information from the certificate except for its signature (signature) and signature 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)

We write the procedure for extracting information about the public key from the tbs-certificate:

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)

And since we are interested in Russian cryptography, we will immediately write the procedure for parsing a GOST public key:

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]

Yes, I almost missed it, after downloading the pki package, you need to add to the array :: pki :: oids oids, which characterize GOST and a qualified certificate or are simply missing in this array:

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. "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. "GOST R 34.10-2012-256"
 set ::pki::oids(1.2.643. "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. "GOST R 34.10-2012-256 with GOSTR 34.11-2012-256"
 set ::pki::oids(1.2.643. "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"
. . . 

You can also fill up the vocabulary of the translator by adding to the file 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"
. . .

Root certificate chain and certificate revocation list

How to get a chain of root certificates has already been considered . By analogy, we write the procedure for obtaining a list of revoked COC / CRL certificates. The source code of the utility and its distributions for Linux, OS X (macOS) and MS Windows platforms can be found

In the source code, you can find all the procedures for parsing certificate extensions.
Opponents of Tk (Tcl / Tk, Python / Tkinter, etc.) suggest finding, as they say, 10 (to lessen) differences between the two utilities, the gcr-viewer utility written in gtk and the certViewer utility developed on Tk:

Certificates on tokens / smartcards PKCS # 11

You talked about working with certificates (view, get a chain of root certificates, lists of revoked certificates, prints by sha1 and sha256, etc.) stored in files. But there are more certificates stored on PKCS # 11 tokens / smartcards . And the natural desire is not only to see them, then export to a file. And how to do this, we will tell in the next article:

Also popular now: