imageПришло время продемонстрировать как криптографический АРМ на базе стандартов с открытым ключом cryptoarmpkcs работает на одной из мобильных платформ, а именно Android.

Концепция, которая закладывалась при разработке утилиты cryptoarmpkcs, состоит в том, что пользователь должен испытывать минимум неудобств при создании и проверке электронной подписи. Именно поэтому мы предлагаем в качестве ключевого носителя для личного сертификата использовать криптографические токены PKCS#11 и/или защищенные контейнеры PKCS#12. Надо сказать, что использование PKCS#12 во многих случаях оказывается чуть ли не единственно возможным, например, когда у тех же токенов PKCS#11 не оказывается поддержки для тех или иных платформах. Вот и мы решили начать портирование утилиты cryptoarmpkcs на платформу Android с поддержки защищенных контейнеров PKCS#12.

Сразу отметим, что поскольку проект делался на платформе C и Tcl/Tk, то портирование не вызвало каких-либо принципиальных трудностей. Это стало возможным еще благодаря технологии Androwish. С разворачиванием и настройкой среды никаких проблем не возникло (моя рабочая среда это Linux — Mageia-7.0). Приведу только один скрипт, для разворачивания Android Command Line Tools (sdk-tools-linux-xxxx.zip):

#!/bin/sh
if [ $# -ne 1   ]
    then 
	echo "./InstallAndroidSDK.sh <sdk-tools-linux-xxxxxxxx.zip>"
	echo "Не указан архив или число параметров больше 1"
	exit 1
fi
if [ ! -f $1 ]
    then 
	echo "./InstallAndroidSDK.sh <sdk-tools-linux-xxxxxxxx.zip>"
	echo "архив $1 отсутствует"
	exit 1
fi
#Распаковываем SDK-TOOLS в папку tools
#unzip sdk-tools-linux-4333796.zip
unzip $1
#Создаем папку android-sdk-linux
mkdir android-sdk-linux
#Перемещвем папку tools в android-sdk-linux
mv tools android-sdk-linux
cd android-sdk-linux/tools/bin
./sdkmanager "platform-tools" "platforms;android-29"  

Самое замечательное то, что в среду Androwish входит два интерпретатора undrowish-xxx и vanillawish-xxx полностью идентичных по составу «балалаек» (пакетов), входящих собственно в состав Androwish. Различие между undrowish и vanillawish состоит в том, что в vanillawish бэкэнд на основе SDL / AGG / freetype:



Наличие этих двух утилит позволяет разрабатывать приложение без использования самого Android-а и его эмулятора с максимальным приближением к реальному устройству. В первую очередь это, конечно, undroidwish-xxx.

Собственно пользовательский пакет для Android собирается в среде AWSDK. Дерево вашего проекта должно перенесено а папку ~/AWSDK/assets/app. При этом главный модуль вашего проекта должен быть переименован в main.tcl. Если вы используете дополнительные балалайки с динамическими библиотеками, то библиотеки необходимо положить в папки ~/AWSDK/libs/x86 для эмулятора и ~/AWSDK/libs/armeabi для реального устройства.

После этого достаточно выполнить команду:

$wish ~/AWSDK/tools/bones

и следовать ее указаниям:



Собранный apk-пакет будет лежать в папке ~/AWSDK/build/outputs/apk.

Если у вас подключено реальное устройство или эмулятор, то пакет можно сразу установить.
При этом реальное устройство должно находиться в режиме отладки.

Но вернемся собственно к приложению. Что же пришлось в нем изменить.

Естественно, в первую очередь изменения связаны с размерами экрана. Пришлось перепроектировать главное (стартовое окно). В итоге, вместо одного окна на обычном компьютере:



Появилось три окна на Android:



Первое окно является информационным. Условно оно разбивается на несколько частей. Первая часть, находящаяся вверху, содержит название утилиты и ее логотип.

Далее идет логотип производителя, информационная справка и завершают страницу три кнопки.
Страница написана с использованием компонента canvas (холста). На странице используются кнопки двух видов: одна в виде полупрозрачного прямоугольника (средняя), а две другие в виде прямоугольника с закругленными углами. Для создания кнопок с закругленными углами была задействована «балалайка» tkpath. Лично на меня эта балалайка произвела очень хорошее впечатление. Естественно при работе с холстом (canvas) львиную долю кода занимает геометрия. Ниже приводится скрипт create_titul_page.tcl для создания первой страницы приложения. Каждый желающий может его подредактировать под свое видение.

Скрипт create_titul_page.tcl
package require Tk
package require tkpath 0.3.0

global mydir
set mydir [file dirname [info script]]
#Грузим картинки
#Логотип продукта
image create photo logo_product -file [file join $mydir "imageme" "validcertkey_51x24.png"] 
#Логотип производителя
image create photo logo_orel -file [file join $mydir "imageme" "я_орел_425x200.png"] -format "png -alpha 1.0"
#Андроида с tcl/tk
image create photo logo_and -file [file join $mydir "imageme" "AndTk_inv_147x173.png"] -format "png -alpha 1.0"
#Свиток опечатанный
image create photo svitok -file [file join $mydir "imageme" "blue_svitok.png"] -format "png -alpha 1.0"
#Плитка
image create photo tileand -file [file join $mydir "imageme" "tile_green_and_32x32.png"] -format "png -alpha 1.0"
#Увеличить/уменьшить (отрицательное значение - уменьшение)
proc scaleImage {im xfactor {yfactor 0}} {
   set mode -subsample
   if {$xfactor>=0 && $yfactor>=0} {
       set mode -zoom
   } else {
	set xfactor [expr $xfactor * -1]
   }
   if {$yfactor == 0} {set yfactor $xfactor}
   set t [image create photo]
   $t copy $im
   $im blank
   $im copy $t -shrink $mode $xfactor $yfactor
   image delete $t
}

proc createtile {w  backg} {
    image create photo tiled
    tiled copy $backg -to 0 0 $::scrwidth $::scrheight -shrink
    $backg copy tiled
    image delete tiled
# Мостим холст
    $w create image 0 0        -image $backg        -anchor nw
}

proc create_rectangle  {canv img x1 y1 x2 y2 color alfa {wbd 0} {colorline black} } {
    image create photo $img -format "default -colorformat  rgb"
    set rgb1 [winfo rgb $canv $color]
    set cr  [lindex $rgb1 0]
    set cg  [lindex $rgb1 1]
    set cb  [lindex $rgb1 2]
    set fill [format "#%04x%04x%04x" $cr $cg $cb ]
#Создаем цветной праямоугольник
    $img put $fill -to 0 0 [expr {$x2 - $x1}] [expr {$y2 -$y1}]
#Сохраняем картинку
    set dimg [$img data -format png]
#Создаем image с учетом alpha канала
    image create photo $img -data $dimg -format "png -alpha $alfa"
#    $img put [list $rgb1] -to 0 0 [expr {$x2 - $x1}] [expr {$y2 -$y1}]
#Отображаем цветной прямоугольник
    set imgr [$canv create image $x1 $y1 -image $img -anchor nw] 
    set cc [subst {butImg $img}]
    $canv bind $imgr <ButtonPress-1> $cc
#Оконтовка вокруг цветного прямоугольника
    if {$wbd > 0 } {
	set item [$canv create rect $x1 $y1 $x2 $y2 -outline $colorline -width $wbd ]
	$canv bind $item <ButtonPress-1> $cc
    }
   return $imgr
}

proc butImg {img} {
    tk_messageBox -title "Кнопка" -icon info -message "Нажали кнопку=$img" -detail "::screenwidth=$::scrwidth\n::screenheight=$::scrheight" -parent .
    if {$img == "exit"} {
	set answer [tk_dialog .dialog2 "Конец работы" "Вы действительно\nхотите выйти?" question 0 "Да" "Нет" ]
	if {$answer == 0} {
    	    exit
	}	
    }
}

proc page_titul {fr  logo_manufacturer} {
    global mydir
#Создаем холст на весь экран
    tkp::canvas $fr.can -borderwidth 0 -height [winfo screenheight .] -width [winfo screenwidth .] -relief flat
#Мостим холст плиткой 
    createtile "$fr.can"  "tileand"
    pack $fr.can  -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0
#Вычисляем координаты для логотипа производителя
#update чтобы обновилась информация в БД об окнах
    update
#    set aa [winfo height $fr.labtitul]
    set aa $::padly
#Центрируем логотип разработчика
    set ha [image width $logo_manufacturer]
    set xman [expr {($::scrwidth - $ha) / 2 }]
    $fr.can create image $xman $aa -image $logo_manufacturer -anchor nw -tag tag_logo

    set blogo [$fr.can bbox tag_logo]
    set wexit [lindex $blogo 3]
    if {$::typetlf } {
	set dlx [expr {$::padlx / 1}]
	$fr.can create text [expr $dlx + 6] [expr {$wexit + $::padly + 6}] -anchor nw -text "Электронная подпись" -fill black -font {{Roboto Condensed Medium} 15 }
	$fr.can create text $dlx [expr {$wexit + $::padly}] -anchor nw -text "Электронная подпись" -fill white -font {{Roboto Condensed Medium} 15 } -tag id_text0
	update
	set blogo [$fr.can bbox id_text0]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $dlx + 4] [expr {$wexit + $::padly + 4 - 80}] -anchor nw -text "для платформы Android" -fill black -font {{Roboto Condensed Medium} 13}
	$fr.can create text $dlx [expr {$wexit + $::padly - 80}] -anchor nw -text "для платформы Android" -fill white -font {{Roboto Condensed Medium} 13} -tag id_text1
	set blogo [$fr.can bbox id_text1]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $dlx + 3] [expr {$wexit + $::padly + 3 - 50}] -anchor nw -text "№ 63 ФЗ \"Об электронной\nподписи от 6 апреля 2011 года\"" -fill black -font {{Roboto} 10}
	$fr.can create text $dlx [expr {$wexit + $::padly - 50}] -anchor nw -text "№ 63 ФЗ \"Об электронной\nподписи от 6 апреля 2011 года\"" -fill white -font {{Roboto} 10} -tag id_text2
	set blogo [$fr.can bbox id_text2]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $dlx + 2] [expr {$wexit + $::padly + 2 - 40}] -text "Авторы: В.Н. Орлов\nhttp://soft.lissi.ru, http://www.lissi.ru\n+7(495)589-99-53\ne-mail: support@lissi.ru\n\n" 	-anchor nw -fill black  -font {{Roboto} 9}
	$fr.can create text $dlx [expr {$wexit + $::padly - 40}] -text "Авторы: В.Н. Орлов\nhttp://soft.lissi.ru, http://www.lissi.ru\n+7(495)589-99-53\ne-mail: support@lissi.ru\n\n" 	-anchor nw -fill white -tag id_text3  -font {{Roboto} 9}
    } else {
	$fr.can create text [expr $::padlx + 2] [expr {$wexit + $::padly + 2}] -anchor nw -text "Электронная подпись" -fill black -font {{Nimbus Sans Narrow} 20}
	$fr.can create text $::padlx [expr {$wexit + $::padly}] -anchor nw -text "Электронная подпись" -fill white -font {{Nimbus Sans Narrow} 20} -tag id_text0
	set blogo [$fr.can bbox id_text0]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $::padlx + 2] [expr {$wexit + $::padly + 2 - 20}] -anchor nw -text "для платформы Android" -fill black -font {{Nimbus Sans Narrow} 18}
	$fr.can create text $::padlx [expr {$wexit + $::padly - 20}] -anchor nw -text "для платформы Android" -fill white -font {{Nimbus Sans Narrow} 18} -tag id_text1
	set blogo [$fr.can bbox id_text1]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $::padlx + 2] [expr {$wexit + $::padly + 2}] -anchor nw -text "№ 63 ФЗ \"Об электронной\nподписи от 6 апреля 2011 года\"" -fill black -font {{Nimbus Sans Narrow} 14}
	$fr.can create text $::padlx [expr {$wexit + $::padly}] -anchor nw -text "№ 63 ФЗ \"Об электронной\nподписи от 6 апреля 2011 года\"" -fill white -font {{Nimbus Sans Narrow} 14} -tag id_text2
	set blogo [$fr.can bbox id_text2]
	set wexit [lindex $blogo 3]
	$fr.can create text [expr $::padlx + 1] [expr {$wexit + $::padly + 1}] -text "Авторы: В.Н. Орлов\nhttp://soft.lissi.ru, http://www.lissi.ru\n+7(495)589-99-53\ne-mail: support@lissi.ru\n\n" 	-anchor nw -fill black  -font {{Nimbus Sans Narrow} 12}
	$fr.can create text $::padlx [expr {$wexit + $::padly}] -text "Авторы: В.Н. Орлов\nhttp://soft.lissi.ru, http://www.lissi.ru\n+7(495)589-99-53\ne-mail: support@lissi.ru\n\n" 	-anchor nw -fill white -tag id_text3  -font {{Nimbus Sans Narrow} 12} -tag id_text3
	set blogo [$fr.can bbox id_text2]
    }
    set blogo [$fr.can bbox id_text3]
    set wland [lindex $blogo 3]
    $fr.can create image $::padlx $wland -image logo_and -anchor nw -tag tag_land
    set ha [image height logo_and]
    set wa [image width logo_and]
    set ha1 [expr {$ha - ($ha / 2 ) }]
    $fr.can create image [expr {$wa - 80 }] [expr {$wland + $ha1}] -image svitok -anchor nw -tag tag_land
    if {$::typetlf} {
	set x1 [expr {$::padlx / 2}]
	set y1 [expr {$wland + 120}]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 120}]
	set wd 8
	set rr 18
    } else {
	set x1 [expr {$::padlx / 2}]
	set y1 [expr {$wland + 40}]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 40}]
	set wd 4
	set rr 6
    }

    set g5 [$fr.can gradient create linear -stops {{0 lightgreen} {1 green}}] 
    set S3 [$fr.can style create -stroke "skyblue" -fill  $g5 -strokewidth $wd  -fillopacity 0.6]
    set im1 [$fr.can create prect $x1 $y1 $x2 $y2 -rx $rr -style $S3]
    $fr.can bind $im1 <ButtonPress-1> {butImg "img1"}
#Печатаем техт
    set blogo [$fr.can bbox $im1]
    set by2 [lindex $blogo 3]
    set by1 [lindex $blogo 1]
    set bb [expr {($by2 - $by1) / 2}]
    set bx2 [lindex $blogo 2]
    set bx1 [lindex $blogo 0]
    set bbx [expr {($bx2 - $bx1) / 2}]
    set txt1 [$fr.can create text [expr {$x1 + $::padlx * 2}] [expr {$y1 + 1 }] -anchor nw -text "Сайт разработчика" -fill black -font {{Arial} 10 normal}] 
#Центрируем техт
    set btxt1 [$fr.can bbox $txt1]
#Смещение по оси Y
    set ty2 [lindex $btxt1 3]
    set ty1 [lindex $btxt1 1]
    set tt [expr {$ty2 - $ty1}]
    set tt [expr {$tt / 2}]
    set offsy [expr {($by1 + $bb) - ($ty1 + $tt)}]
#Смещение по оси X
    set tx2 [lindex $btxt1 2]
    set tx1 [lindex $btxt1 0]
    set ttx [expr {$tx2 - $tx1}]
    set ttx [expr {$ttx / 2}]
    set offsx [expr {($bx1 + $bbx) - ($tx1 + $ttx)}]
    $fr.can move $txt1 $offsx $offsy
    $fr.can bind $txt1 <ButtonPress-1> {butImg "img1"}

    if {$::typetlf} {
	set y1 [expr $y2 + 40]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 120}]
    } else {
	set y1 [expr {$y1 + 60}]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 40}]
    }
    set im1 [create_rectangle $fr.can "but2" $x1 $y1 $x2 $y2 "#2b972d" 0.6 $wd "skyblue"]
#Печатаем техт
    set blogo [$fr.can bbox $im1]
    set by2 [lindex $blogo 3]
    set by1 [lindex $blogo 1]
    set bb [expr {($by2 - $by1) / 2}]
    set bx2 [lindex $blogo 2]
    set bx1 [lindex $blogo 0]
    set bbx [expr {($bx2 - $bx1) / 2}]
    set txt1 [$fr.can create text [expr {$x1 + $::padlx * 2}] [expr {$y1 + 1 }] -anchor nw -text "Переход в основное меню" -fill black -font {{Roboto Condensed Medium} 12}] 
#Центрируем текст
    set btxt1 [$fr.can bbox $txt1]
#Смещение по оси Y
    set ty2 [lindex $btxt1 3]
    set ty1 [lindex $btxt1 1]
    set tt [expr {$ty2 - $ty1}]
    set tt [expr {$tt / 2}]
    set offsy [expr {($by1 + $bb) - ($ty1 + $tt)}]
#Смещение по оси X
    set tx2 [lindex $btxt1 2]
    set tx1 [lindex $btxt1 0]
    set ttx [expr {$tx2 - $tx1}]
    set ttx [expr {$ttx / 2}]
    set offsx [expr {($bx1 + $bbx) - ($tx1 + $ttx)}]
    $fr.can move $txt1 $offsx $offsy
    $fr.can bind $txt1 <ButtonPress-1> {butImg "but2"}
    if {$::typetlf} {
	set x1 [expr {$::padlx / 2}]
	set y1 [expr $y2 + 40]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 120}]
    } else {
	set x1 [expr {$::padlx / 2}]
	set y1 [expr {$y1 + 60}]
	set x2 [expr {$::::scrwidth - $x1}]
	set y2 [expr {$y1 + 40}]
    }
    set S3 [$fr.can style create -stroke skyblue -fill  $g5 -strokewidth $wd  -fillopacity 0.6]
    set im1 [$fr.can create prect $x1 $y1 $x2 $y2 -rx $rr -style $S3]
    set blogo [$fr.can bbox $im1]
    $fr.can bind $im1 <ButtonPress-1> {butImg "exit"}
    set by2 [lindex $blogo 3]
    set by1 [lindex $blogo 1]
    set bb [expr {($by2 - $by1) / 2}]
    set bx2 [lindex $blogo 2]
    set bx1 [lindex $blogo 0]
    set bbx [expr {($bx2 - $bx1) / 2}]
    set txt1 [$fr.can create text [expr {$x1 + $::padlx * 2}] [expr {$y1 + 1 }] -anchor nw -text "Конец работы" -fill black  -font {Arial 10 normal}]
    $fr.can bind $txt1 <ButtonPress-1> {butImg "exit"}
    set btxt1 [$fr.can bbox $txt1]
#Смещение по оси Y
    set ty2 [lindex $btxt1 3]
    set ty1 [lindex $btxt1 1]
    set tt [expr {$ty2 - $ty1}]
    set tt [expr {$tt / 2}]
    set offsy [expr {($by1 + $bb) - ($ty1 + $tt)}]
#Смещение по оси X
    set tx2 [lindex $btxt1 2]
    set tx1 [lindex $btxt1 0]
    set ttx [expr {$tx2 - $tx1}]
    set ttx [expr {$ttx / 2}]
    set offsx [expr {($bx1 + $bbx) - ($tx1 + $ttx)}]
    $fr.can move $txt1 $offsx $offsy
}
#Собственно скрипт
#Считываем размеры экрана
set ::scrwidth [winfo screenwidth .]
set ::scrheight [winfo screenheight .]
set ::typetlf 0
#Проверяем, что это телефон
if {$::scrwidth < $::scrheight} {
    set ::typetlf 1
}
set ::padls 20
set ::padlx 15
set ::padly 15
if {$::typetlf} {
	wm attributes . -fullscreen 1
	scaleImage icon_openfile_18x16 3
	scaleImage ::img::view_18x16 3
#Логотип производителя
	scaleImage logo_orel 2
#Логотип продуктв
	scaleImage logo_product 2
#Андроида tcl/tk
	scaleImage logo_and 3
#Свиток опечатанный
	scaleImage svitok 4
	set ::padls 50
	set ::padlx 75
	set ::padly 50
} else {
#Конфигурирование виджета под смартфон
    scaleImage logo_orel -2
    set ::scrwidth 370
    set ::scrheight 700
    wm minsize . $::scrwidth $::scrheight
    set geometr $::scrwidth
    append geometr "x"
    append geometr $::scrheight
    append geometr "+0+0"
    wm geometry . $geometr
}
#Создаем название продукта
set name_product "CryptoArmPKCS-A" 
label .labtitul -image logo_product -compound left -fg snow -text $name_product -font {Arial 10 bold} -anchor w  -width [winfo screenwidth .] -pady $::padls -padx 10 -bg #222222 
pack .labtitul -anchor nw -expand 0 -fill x -side top  -padx 1 -pady 0
#Создаем стартовую страницу
set i 0
ttk::frame .fr$i -pad 0 -padding 0
page_titul ".fr$i"  "logo_orel"
pack .fr$i -side top -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0 
update


Для выполнения данного скрипта используем одну из утилит undroidwish или vanillawish:

$ /usr/local/bin64/undroidwish-e5dc71ed9d-linux64   create_titul_page.tcl 

или

$/usr/local/bin64/vanillawish-e5dc71ed9d-linux64  create_titul_page.tcl 

Результат представлен на первом скриншоте.

На второй странице перечисляется поддерживаемый утилитой cryptoarmpkcs-A функционал. Каждая строчка является кнопкой, при нажатии на которую будет отображена функциональная страница. Геометрия размещение кнопок на этой странице определяется шрифтом, который используется. Ниже приводится скрипт create_page_functions.tcl для создания второй/функциональной страницы приложения. Каждый желающий также может его подредактировать под свои функции.

Скрипт create_page_functions.tcl
package require Tk
package require tkpath 0.3.0
global mydir
set mydir [file dirname [info script]]
#Увеличить/уменьшить (отрицательное значение - уменьшение)
proc scaleImage {im xfactor {yfactor 0}} {
   set mode -subsample
   if {$xfactor>=0 && $yfactor>=0} {
       set mode -zoom
   } else {
	set xfactor [expr $xfactor * -1]
   }

   if {$yfactor == 0} {set yfactor $xfactor}
   set t [image create photo]
   $t copy $im
   $im blank
   $im copy $t -shrink $mode $xfactor $yfactor
   image delete $t
}

proc createtile {w  backg} {
    image create photo tiled
    tiled copy $backg -to 0 0 $::scrwidth $::scrheight -shrink
    $backg copy tiled
    image delete tiled
# Мостим холст
    $w create image 0 0        -image $backg        -anchor nw
}

proc butCliked {num fr} {
    pack forget  .fr1
    set ::tekFrfunc $fr
    pack $fr -side top -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0 
    tk_dialog .dialog1 "Dear user:" "Button $num was clicked\nFr=$fr" info 0 OK 
}

proc butImg {img} {
    tk_messageBox -title "Кнопка" -icon info -message "Нажали кнопку=$img" -detail "::screenwidth=$::scrwidth\n::screenheight=$::scrheight" -parent .
    if {$img == "exit"} {
	set answer [tk_dialog .dialog2 "Конец работы" "Вы действительно\nхотите выйти?" question 0 "Да" "Нет" ]
	if {$answer == 0} {
    	    exit
	}	
    }
}

proc butReturn {} {
    pack forget  $::tekFrfunc
    pack .fr1 -side top -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0 
#    tk_dialog .dialog1 "Dear user:" "Button $num was clicked\nFr=$fr" info 0 OK 
}

proc page_func {fr tile titul functions} {
#Кнопки  меню
    upvar $functions but
#Создаем шрифт для кнопок
    if {$::typetlf} {
	set feFONT_button "-family {Roboto} -size 9 -weight bold -slant roman"
	set widl 10
    } else {
	set feFONT_button "-family {Arial} -size 12 -weight bold -slant roman"
	set widl 5
    }
    catch {font delete fontTEMP_drawer}
    eval font create fontTEMP_drawer  $feFONT_button
#Вычисляем максимальныю длину текста
    set drawerCNT 0
    set strMaxWidthPx 15
    set Ndrawers [expr {[array size but] - 1}]
    while { $drawerCNT <= $Ndrawers } {
	set strWidthPx [font measure fontTEMP_drawer "$but($drawerCNT)"]
	if { $strWidthPx > $strMaxWidthPx } {
    	    set strMaxWidthPx $strWidthPx
	}
	incr drawerCNT
    }
    set drawerWidthPx [expr $strMaxWidthPx + 10]
    set xxx [expr {($::::scrwidth - $drawerWidthPx) / 2}]

    if {$fr != ".fr1"} {
	set hret [expr $::scrheight / 4]
    } else {
	set hret $::scrheight
    }
	set hret [expr $::scrheight / 4]
    tkp::canvas $fr.can -borderwidth 0 -height $hret -width $::scrwidth -relief flat
#Мостим холст плиткой 
    createtile "$fr.can"  $tile
    pack $fr.can  -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0
    if {$titul != "" } {
	set allfunc $titul
	catch {font delete fontTEMP_titul}
	set font_titul "-family {Roboto Condensed Medium} -size 15"
        eval font create fontTEMP_titul  $font_titul
	set funcWidthPx [font measure fontTEMP_titul "$allfunc"]
	set dlx [expr {($::::scrwidth - $funcWidthPx) / 2}]

	$fr.can create text [expr $dlx + 6] [expr {6 + 6}] -anchor nw -text "$allfunc" -fill black -font fontTEMP_titul
	$fr.can create text $dlx 6 -anchor nw -text "$allfunc" -fill white -font fontTEMP_titul -tag id_text0
	set blogo [$fr.can bbox id_text0]
	set boxbut [expr ([lindex $blogo 3] + 6 + 6)]
    } else {
	set boxbut [expr 6 + 6]
    }
#Вычисляем самый широкий текст у кнопок
#См. выше
#Размещаем кнопки
    set BDwidth_canvas 0

    set maxTextHeightPx [font metrics fontTEMP_drawer -linespace] 

    set maxTextHeightPx [expr {$maxTextHeightPx + ( $maxTextHeightPx / 2)}]

    set drawerHeightPx $maxTextHeightPx

    set xLocTextPx [expr {($::::scrwidth - $drawerWidthPx) / 2}]

    set yLocTextPx [expr $BDwidth_canvas + ($drawerHeightPx / 2) + $boxbut]
    set canvasHeightPx [expr $Ndrawers * $drawerHeightPx]

    set drawerCNT 0
    set Ndrawers [expr {[array size but] - 1}]
    while { $drawerCNT <= $Ndrawers } {
      set yLineLocPx [ expr (( $drawerCNT ) * $drawerHeightPx + $boxbut)]
#Линия перед текстом
      $fr.can create line          $xLocTextPx $yLineLocPx          [expr $drawerWidthPx + $xLocTextPx] $yLineLocPx          -fill "#a0a0a0" -width $widl

      $fr.can create text [expr $xLocTextPx + 5] $yLocTextPx 	-anchor w         -font fontTEMP_drawer         -text "$but($drawerCNT)"         -tag textlineTag($drawerCNT)

      if {$drawerCNT == 0} {
	    if {$fr == ".fr1"} {
		$fr.can bind textlineTag($drawerCNT)  <ButtonRelease-1>   {butImg "but1"}
	    } else {
		$fr.can bind textlineTag($drawerCNT)  <ButtonRelease-1>   {butReturn}
	    }
	} else {
	    frame .fn$drawerCNT -background white -relief flat -pady 0 -padx 0
	    set titul $but($drawerCNT)
	    set cmd "$fr.can bind textlineTag($drawerCNT)  <ButtonRelease-1>   {butCliked $drawerCNT .fn$drawerCNT}"
	    set cmd [subst "$cmd"]
	    eval $cmd 
	    set but1(0) "Возврат в основное меню"
	    page_func ".fn$drawerCNT" voda "$titul" "but1"
	}

	incr drawerCNT

	set yLocTextPx [ expr $yLocTextPx + $drawerHeightPx]
#Завершаюшая линия
	if { $drawerCNT > $Ndrawers } {
    	    set yLineLocPx [ expr (( $drawerCNT ) * $drawerHeightPx + $boxbut)]
    	    $fr.can create line $xLocTextPx $yLineLocPx             [expr $drawerWidthPx + $xLocTextPx] $yLineLocPx             -fill "#a0a0a0" -width $widl
	}
    }
}
#Собственно скрипт
#Считываем размеры экрана
set ::scrwidth [winfo screenwidth .]
set ::scrheight [winfo screenheight .]
set ::typetlf 0
#Проверяем, что это телефон
if {$::scrwidth < $::scrheight} {
    set ::typetlf 1
}
set ::padls 20
set ::padlx 15
set ::padly 15
if {$::typetlf} {
	wm attributes . -fullscreen 1
#Логотип продуктв
	scaleImage logo_product 2
	set ::padls 50
	set ::padlx 75
	set ::padly 50
} else {
#Конфигурирование виджета под смартфон
    set ::scrwidth 370
    set ::scrheight 700
    wm minsize . $::scrwidth $::scrheight
    set geometr $::scrwidth
    append geometr "x"
    append geometr $::scrheight
    append geometr "+0+0"
    wm geometry . $geometr
}

#Грузим картинки
image create photo voda -file [file join $mydir "imageme" "voda_400x800.png"]
#Логотип продукта
image create photo logo_product -file [file join $mydir "imageme" "validcertkey_51x24.png"] 
#Создаем название продукта
set name_product "CryptoArmPKCS-A" 
label .labtitul -image logo_product -compound left -fg snow -text $name_product -font {Arial 10 bold} -anchor w  -width [winfo screenwidth .] -pady $::padls -padx 10 -bg #222222 
pack .labtitul -anchor nw -expand 0 -fill x -side top  -padx 1 -pady 0
#Создаем страницы с функционалом
set i 1
ttk::frame .fr$i -pad 0 -padding 0
#Кнопки основного меню
set but(0) "Стартовая страница" 
set but(1) "Подписать документ"
set but(2) "Работаем с ЭП (PKCS7)" 
set but(3) "Запрос на сертификат" 
set but(4) "Просмотр запроса/сертификата" 
set but(5) "Список криптомеханизмов"
set but(6) "Просмотр ASN1-структуры" 
set but(7) "Объекты токена" 
set but(8) "Работаем с PKCS12/PFX" 
set but(9) "Самоподписанный сертификат"
set but(10) "Об Утилите/Дистрибутивы" 
set but(11) "Создать Токены"
set but(12) "Конфигурировние токена"
if {$::typetlf} {
    scaleImage voda 3 2
}
#Создаем страницу с функционалом
page_func ".fr$i" voda "Функционал" "but"
#Отображаем страницу с функционалом
pack .fr$i -side top -anchor center -expand 1 -fill both -side top  -padx 0 -pady 0 


Этот скрипт также готовит болванки для каждой функциональной кнопки:



Наполнение болванок ведется классическими и тематическими виджетами (labelframe, button и т.д.). Одну из таких заполненных балванок можно видеть на первом скриншоте справа. Посколько на первом этапе мы сосредоточились на работе с контейнером PKCS#12, то код практически без изменений был использован и для cryptoarmpkcs-A. На данном этапе реализованы следующие функции:

  • подписать документ (Cades-BES, CAdes-T, CAdes-XLT1);
  • работаем с ЭП (PKCS7), включая добавление подписанта;
  • просмотр сертификатов/запросов на сертификаты:

  • работаем с PKCS12/PFX;
  • об утилите/Дистрибутивы:


Остальные функции больше связаны с токенами PKCS#11. Их портирование отложено на Новый Год. Планируется подключение программного токена и подключение к облаку токенов.

С точки зрения функционала практически все аналочично утилите cryptoarmpkcs. Но есть отдельные отличия. Например, после подписания документа, утилита спрашивает будет ли подпись проверяться на сайте Госуслуг:


При нажатии кнопки «Да» будет загружен браузер со страницей проверки подписи документов и сертификатов. Сразу оговоримся, что эта страница не очень рассчитана на экран смартфона. Это будет заметно при выборе подписи и, если подпись отсоединенная, файла с документом. Но если все хорошо, то мы получим положительный результат:



Следует иметь ввиду, что проверка как подписи, так и сертификата на сайте Госуслуг имеет смыл только если сертификат был получен в аккредитованном удостоверяющем центре (УЦ). В противном случае подпись всегда будет недействительной.

Для вызова браузера пришлось добавить пару строк в процедуру openUrl:

proc openURL {url} {
  global typesys
  global macos
  #  global windowsOS
#проверка, что утилита выполняется на смартфоне Android
  if {$::typetlf} {
#Запуск браузера
 	borg activity android.intent.action.VIEW $url text/html
 	return
  }
. . .
}

Браузер на Android вызывается следующим образом:

borg activity android.intent.action.VIEW <URL> text/html

Небольшая особенность есть при добавлении нового подписанта к ранее подписанному документу. Сертификат нового подписанта (вернее даже контейнер PKCS#12 с сертификатом и закрытым ключом) должен быть заранее выбран на странице «Подписать документ» или «Работаем с PKCS12/PFX», о чем утилита напомнит:


При длительных операциях как и прежде будут идти часы:


Осталось сказать откуда скачать дистрибутивы и Поздравить с наступающим Новым Годом и пожелать всем всего наилучшего в 2020 Году!

imageИтак, дистрибутивы для Linux, OS X, Windows и Android:

Комментарии (8)


  1. u_235
    31.12.2019 11:21

    Вы предлагаете для работы с чувствительными данными установить программное обеспечение из сомнительного источника? По http и без контрольных сумм? Извините, но это как минимум непрофессионально.


    1. saipr Автор
      31.12.2019 13:46

      Ваше замечание учтено. Теперь дистрибутивы качаются по https с github.
      С наступающим Говым Годом. Счастья и Удачи!


  1. ne_kotin
    31.12.2019 12:53

    А работать в Android с его стандартными криптомеханизмами что мешает? Реализовать нормальное нативное приложение при помощи SDK? использовать локскрин и биометрию для аутентификации и доступа к ключам?


    1. saipr Автор
      31.12.2019 13:50

      В Android ничто не мешает работать с его стандартными криптомеханизмами. Только эти стандартные криптомеханизмы нельзя использовать при создании электронной подписи для различных госуслуг и не только. С 1 января 2020 года (т.е. уже завтра) электронная подпись должна быть ГОСТ Р 34.10-2012. Для внутренних нужд вы можете использовать что пожелаете.
      С наступающим Говым Годом. Счастья и Удачи!


      1. ne_kotin
        31.12.2019 14:59

        Берем BouncyCastle криптопровайдер, там вроде ГОСТ был


        1. saipr Автор
          31.12.2019 16:16
          -1

          Да, вот здесь написано как получить ГОСТ Р 34.10-2012 в BouncyCastle.
          Но помимо реализации собственно математического алгоритма, есть много особенностей с так называемыми квалифицированными сертификатами, типами подписей и т.д. Наконец особенности национальной криптографии (помните особенности национальной охоты?), например, как хранить то или иное значение. Так что криптопровайдер может и не помочь.


      1. Ark_V
        01.01.2020 13:04

        Только эти стандартные криптомеханизмы нельзя использовать при создании электронной подписи для различных госуслуг и не только.

        а как в вашем случае быть с тем, что средства создания ЭП для подобного использования должны быть сертифицированы специфическими государственными организациями?


        1. saipr Автор
          01.01.2020 13:17
          -1

          Первое, когда мы говорили про "стандартные криптомеханизмы", то имелось в виду то, что там нет поддержки ГОСТ-ых механизмов.
          Со вторым постулатом могу согласиться, хотя очень и очень многие сегодня используют, например, openssl с ГОСТ-ами.
          Ну и третье поддержка ГОСТ-ов в данной утилите базируется на сертифицированном СКЗИ.
          И еще, если Госуслуги принимают, то значит все нормально.
          Во всем мире используют OPENSSL, NSS, PGP и т.д. и никто не говорит, что они должны быть сертифицированы госорганизациями. Главное здесь иметь легитимный сертификат закрытым ключом. В наших условиях сертификат должен получаться в аккредитованном УЦ!!!