Неумолимо приближается час «Ч»: «использование схемы подписи ГОСТ Р 34.10-2001 для формирования подписи после 31 декабря 2018 года не допускается!».Однако потом что-то пошло не так, кто-то оказался не готов, и использование ГОСТ Р 34.10-2001 продлили на 2019 год. Но вдруг все бросились переводить УЦ на ГОСТ Р 34.10-2012, а простых граждан переводить на новые сертификаты. У людей на руках стало по нескольку сертификатов. При проверки сертификатов или электронной подписи стали возникать вопросы, а где взять корневые сертификаты, чтобы установить в хранилища доверенных корневых сертификатов.
* охренительной вики, где есть даже игрушки (там можно подсмотреть интересное :)К этому бы я добавил наличии утилиты freewrap, которая нам и поможет собрать автономные (standalone) утилиты для Linux и MS Windows. В результате мы будем иметь утилиту chainfromcert:
* шпаргалки
* нормальные сборки tclkit (1.5 — 2 Мб как плата за реальную кросс-платформенность)
* и моя любимая сборка eTcl от Evolane (бережно сохранённая с умершего сайта :(
сохраняют высокий рейтинг Tcl/Tk в моём личном списке инструментария
и, да, wiki.tcl.tk/16867 (мелкий web-сервер с cgi на Tcl, периодически используется с завидным постоянством под tclkit)
а ещё — это просто красиво и красиво :)
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]
которая собственно и загружает корневые сертификаты: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 $f w]
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 Запрос успешно выполненОсталось не рассмотренной одна процедура, а именно cert_to_der:
206 Запрос успешно выполнен, но удалось скачать только часть файла
301 Файл перемещен в другое место
302 Файл временно перемещен в другое место
401 Требуется аутентификация на сервере
403 Доступ к этому ресурсу запрещен
404 Указанный ресурс не может быть найден
500 Внутренняя ошибка
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 $f w]
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. Утилита freewrapTCLSH для платформы linux_x86_64;Итак, собираемый выполняемый файл chainfromcerty_linuxx86 для платформы Linux x86:
2. Файл freewrapTCLSH с этой утилитой для каждой платформы:
— freewrapTCLSH_linux32
— freewrapTCLSH_linux64
— freewrapTCLSH_win32
— freewrapTCLSH_win64
3. Исходный файл нашей утилиты: chainfromcert.tcl
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_linux32 –o chainfromcerty_linuxx86
$
$freewrapTCLSH chainfromcert.tcl –w freewrapTCLSH_win64 –o chainfromcerty_win64.exe
$
К сожалению, не доступен сервер mySQL