The “H” time is inexorably approaching: “the use of the signature scheme GOST R 34.10-2001 for the formation of a signature after December 31, 2018 is not allowed!”.However, then something went wrong, someone was not ready, and the use of GOST R 34.10-2001 was extended to 2019. But suddenly everyone rushed to transfer the TC to GOST R 34.10-2012, and to transfer ordinary citizens to new certificates. There were several certificates in the hands of people. When checking certificates or an electronic signature, questions began to arise, and where to get the root certificates to install in the trusted root certificate stores.
* Awesome wiki , where there are even toys (there you can peek at interesting :)To this, I would add the presence of the freewrap utility, which will help us build standalone utilities for Linux and MS Windows. As a result, we will have the chainfromcert utility:
* cheat sheets
* normal tclkit builds (1.5 - 2 MB as payment for real cross-platform)
* and my favorite eTcl build from Evolane (carefully saved from the deceased site :(
keep a high Tcl / Tk rating on my personal tool list
and yes, wiki.tcl.tk/16867 (a small web server from cgi to Tcl, periodically used with an enviable constancy under tclkit)
and more - it's just beautiful and beautiful :)
bash-4.3$ ./chainfromcert_linux64 Copyright(C)2019 Usage: chainfromcert <file with certificate> <directory for chain certificate> Bad usage! bash-4.3$
bash-4.3$ ./chainfromcert_linux64 ./cert_test.der /tmp Loading file: cert_test.der Directory for chain: . cert 1 from http://ca.ekey.ru/cdp/ekeyUC2012.cer cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt Goodby! Length chain=2 Copyright(C) 2019 bash-4.3$
#!/usr/bin/tclsh -f package require pki
package require base64
proc usage {use } { puts "Copyright(C) LISSI-Soft Ltd (http://soft.lissi.ru) 2011-2019" if {$use == 1} { puts "Usage:\nchainfromcert <file with certificate> <directory for chain certificate>\n" } } if {[llength $argv] != 2 } { usage 1 puts "Bad usage!" exit } set file [lindex $argv 0] if {![file exists $file]} { puts "File $file not exist" usage 1 exit } puts "Loading file: $file" set dir [lindex $argv 1] if {![file exists $dir]} { puts "Dir $dir not exist" usage 1 exit } puts "Directory for chain: $dir" set fd [open $file] chan configure $fd -translation binary set data [read $fd] close $fd if {$data == "" } { puts "Bad file with certificate=$file" usage 1 exit }
chan configure $fd -translation binary
set depth [chainfromcert $data $dir]
which actually loads root certificates: proc chainfromcert {cert dir} { if {$cert == "" } { exit } set asndata [cert_to_der $cert] if {$asndata == "" } { #Файл содержит все что угодно, только не сертификат return -1 } array set cert_parse [::pki::x509::parse_cert $asndata] array set extcert $cert_parse(extensions) if {![info exists extcert(1.3.6.1.5.5.7.1.1)]} { #В сертификате нет расширений return 0 } set a [lindex $extcert(1.3.6.1.5.5.7.1.1) 0] # if {$a == "false"} { # puts $a # } #Читаем ASN1-последовательность расширения в Hex-кодировке set b [lindex $extcert(1.3.6.1.5.5.7.1.1) 1] #Переводим в двоичную кодировку set c [binary format H* $b] #Sequence 1.3.6.1.5.5.7.1.1 ::asn::asnGetSequence c c_par_first #Цикл перебора значений в засширении 1.3.6.1.5.5.7.1.1 while {[string length $c_par_first] > 0 } { #Выбираем очередную последовательность (sequence) ::asn::asnGetSequence c_par_first c_par #Выбираем oid из последовательности ::asn::asnGetObjectIdentifier c_par c_type set tas1 [::pki::_oid_number_to_name $c_type] #Выбираем установленное значение ::asn::asnGetContext c_par c_par_two #Ищем oid с адресом корневого сертификата if {$tas1 == "1.3.6.1.5.5.7.48.2" } { #Читаем очередной корневой сертификат set certca [readca $c_par $dir] if {$certca == ""} { #Прочитать сертификат не удалось. Ищем следующую точку с сертификатом continue } else { global count #Сохраняем корневой сертификат в указанном каталоге set f [file join $dir [file tail $c_par]] set fd [open $fw] chan configure $fd -translation binary puts -nonewline $fd $certca close $fd incr count puts "cert $count from $c_par" #ПОДЫМАЕМСЯ по ЦЕПОЧКЕ СЕРТИФИКАТОВ ВВЕРХ chainfromcert $certca $dir continue } } elseif {$tas1 == "1.3.6.1.5.5.7.48.1" } { # puts "OCSP server (oid=$tas1)=$c_par" } } # Цепочка закончилась return $count }
proc readca {url dir} { set cer "" #Читаем сертификат в бинарном виде if {[catch {set token [http::geturl $url -binary 1] #получаем статус выполнения функции set ere [http::status $token] if {$ere == "ok"} { #Получаем код возврата с которым был прочитан сертификат set code [http::ncode $token] if {$code == 200} { #Сертификат успешно прочитан и будет созвращен set cer [http::data $token] } elseif {$code == 301 || $code == 302} { #Сертификат перемещен в другое место, получаем его set newURL [dict get [http::meta $token] Location] #Читаем сертификат с другого сервера set cer [readca $newURL $dir] } else { #Сертификат не удалось прочитать set cer "" } } } error]} { #Сертификат не удалось прочитать, нет узла в сети set cer "" } return $cer }
package require http
set token [http::geturl $url -binary 1]
200 Request successfully completedThere is one procedure not considered, namely cert_to_der:
206 The request was successful, but only a part of the file could be downloaded.
301 File moved to another location.
302 File is temporarily moved to another location.
401 Server authentication required
403 Access to this resource is denied.
404 The specified resource could not be found.
500 Internal error
proc cert_to_der {data} { set lines [split $data \n] set hlines 0 set total 0 set first 0 #Ищем PEM-сертификат в файле foreach line $lines { incr total if {[regexp {^-----BEGIN CERTIFICATE-----$} $line]} { if {$first} { incr total -1 break } else { set first 1 incr hlines } } if {[regexp {^(.*):(.*)$} $line ]} { incr hlines } } if { $first == 0 && [string range $data 0 0 ] == "0" } { #Очень похоже на DER-кодировку "0" == 0x30 return $data } if {$first == 0} {return ""} set block [join [lrange $lines $hlines [expr {$total-1}]]] #from PEM to DER set asnblock [base64::decode $block] return $asnblock }
set asnblock [base64::decode $block]
if {$depth == -1} { puts "Bad file with certificate=$file" usage 1 exit } puts "Goodby!\nLength chain=$depth" usage 0 exit
#!/usr/bin/tclsh encoding system utf-8 package require pki package require base64 #package require asn package require http global count set count 0 proc chainfromcert {cert dir} { if {$cert == "" } { exit } set asndata [cert_to_der $cert] if {$asndata == "" } { #Файл содержит все что угодно, только не сертификат return -1 } array set cert_parse [::pki::x509::parse_cert $asndata] array set extcert $cert_parse(extensions) if {![info exists extcert(1.3.6.1.5.5.7.1.1)]} { #В сертификате нет расширений return 0 } set a [lindex $extcert(1.3.6.1.5.5.7.1.1) 0] # if {$a == "false"} { # puts $a # } #Читаем ASN1-последовательность расширения в Hex-кодировке set b [lindex $extcert(1.3.6.1.5.5.7.1.1) 1] #Переводим в двоичную кодировку set c [binary format H* $b] #Sequence 1.3.6.1.5.5.7.1.1 ::asn::asnGetSequence c c_par_first #Цикл перебора значений в засширении 1.3.6.1.5.5.7.1.1 while {[string length $c_par_first] > 0 } { #Выбираем очередную последовательность (sequence) ::asn::asnGetSequence c_par_first c_par #Выбираем oid из последовательности ::asn::asnGetObjectIdentifier c_par c_type set tas1 [::pki::_oid_number_to_name $c_type] #Выбираем установленное значение ::asn::asnGetContext c_par c_par_two #Ищем oid с адресом корневого сертификата if {$tas1 == "1.3.6.1.5.5.7.48.2" } { #Читаем очередной корневой сертификат set certca [readca $c_par $dir] if {$certca == ""} { #Прочитать сертификат не удалось. Ищем следующую точку с сертификатом continue } else { global count #Сохраняем корневой сертификат в указанном каталоге set f [file join $dir [file tail $c_par]] set fd [open $fw] chan configure $fd -translation binary puts -nonewline $fd $certca close $fd incr count puts "cert $count from $c_par" #ПОДЫМАЕМСЯ по ЦЕПОЧКЕ СЕРТИФИКАТОВ ВВЕРХ chainfromcert $certca $dir continue } } elseif {$tas1 == "1.3.6.1.5.5.7.48.1" } { # puts "OCSP server (oid=$tas1)=$c_par" } } # Цепочка закончилась return $count } proc readca {url dir} { set cer "" #Читаем сертификат в бинарном виде if {[catch {set token [http::geturl $url -binary 1] #получаем статус выполнения функции set ere [http::status $token] if {$ere == "ok"} { #Получаем код возврата с которым был прочитан сертификат set code [http::ncode $token] if {$code == 200} { #Сертификат успешно прочитан и будет созвращен set cer [http::data $token] } elseif {$code == 301 || $code == 302} { #Сертификат перемещен в другое место, получаем его set newURL [dict get [http::meta $token] Location] #Читаем сертификат с другого сервера set cer [readca $newURL $dir] } else { #Сертификат не удалось прочитать set cer "" } } } error]} { #Сертификат не удалось прочитать, нет узла в сети set cer "" } return $cer } proc cert_to_der {data} { set lines [split $data \n] set hlines 0 set total 0 set first 0 #Ищем PEM-сертификат в файле foreach line $lines { incr total # if {[regexp {^-----(.*?)-----$} $line]} {} if {[regexp {^-----BEGIN CERTIFICATE-----$} $line]} { if {$first} { incr total -1 break } else { set first 1 incr hlines } } if {[regexp {^(.*):(.*)$} $line ]} { incr hlines } } if { $first == 0 && [string range $data 0 0 ] == "0" } { #Очень похоже на DER-кодировку "0" == 0x30 return $data } if {$first == 0} {return ""} set block [join [lrange $lines $hlines [expr {$total-1}]]] #from PEM to DER set asnblock [base64::decode $block] return $asnblock } proc usage {use } { puts "Copyright(C) LISSI-Soft Ltd (http://soft.lissi.ru) 2011-2019" if {$use == 1} { puts "Usage:\nchainfromcert <file with certificate> <directory for chain certificate>\n" } } if {[llength $argv] != 2 } { usage 1 puts "Bad usage!" exit } set file [lindex $argv 0] if {![file exists $file]} { puts "File $file not exist" usage 1 exit } puts "Loading file: $file" set dir [lindex $argv 1] if {![file exists $dir]} { puts "Dir $dir not exist" usage 1 exit } puts "Directory for chain: $dir" set fd [open $file] chan configure $fd -translation binary set data [read $fd] close $fd if {$data == "" } { puts "Bad file with certificate=$file" usage 1 exit } set depth [chainfromcert $data $dir] if {$depth == -1} { puts "Bad file with certificate=$file" usage 1 exit } puts "Goodby!\nLength chain=$depth" usage 0 exit
$ tclsh ./chainfromcert.tcl cert_orlov.der /tmp Loading file: cert_test.der Directory for chain: /tmp cert 1 from http://ca.ekey.ru/cdp/ekeyUC2012.cer cert 2 from http://reestr-pki.ru/cdp/guc_gost12.crt Goodby! Length chain=2 Copyright(C) 2019 $
1. The freewrapTCLSH utility for the linux_x86_64 platform;So, the executable chainfromcerty_linuxx86 executable for the Linux x86 platform:
2. File freewrapTCLSH with this utility for each platform:
- freewrapTCLSH_linux32
- freewrapTCLSH_linux64
- freewrapTCLSH_win32
- freewrapTCLSH_win64
3. The source file of our utility: chainfromcert.tcl
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_linux32 –o chainfromcerty_linuxx86 $
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_win64 –o chainfromcerty_win64.exe $
Source: https://habr.com/ru/post/436370/