
Dalam komentarnya pada
artikel "Utilitas lintas platform berbahasa Inggris untuk melihat sertifikat x509 yang memenuhi syarat Rusia",
Pas dengan sangat tepat mencatat token PKCS # 11 bahwa mereka "semua dapat menghitung sendiri." Ya, token sebenarnya adalah komputer kriptografis. Dan wajar jika ingin menggunakan komputer ini dalam bahasa scripting, baik itu Python, Perl atau Ruby. Kami telah entah bagaimana mempertimbangkan
penggunaan token PKCS # 11 dengan dukungan untuk kriptografi Rusia di Python untuk menandatangani dan mengenkripsi dokumen, untuk membuat permintaan sertifikat:

Di sini kami melanjutkan diskusi tentang bahasa Tcl. Pada
artikel sebelumnya, ketika kami melihat melihat dan memvalidasi sertifikat yang tersimpan di token / kartu pintar PKCS # 11, kami menggunakan paket
TclPKCS11 versi 0.9.9 untuk mengaksesnya (sertifikat). Seperti yang telah dicatat, sayangnya, paket ini dikembangkan untuk kriptografi RSA dan memperhitungkan standar PKCS # 11 v.2.20. Hari ini, standar PKCS # 11 v.2.40 sudah digunakan dan komite teknis kriptografi TK-26 yang dipandu olehnya, mengeluarkan rekomendasi untuk produsen token / kartu pintar domestik yang mendukung kriptografi Rusia. Dan dengan semua ini dikatakan, paket baru
TclPKCS11 versi 1.0.1 telah muncul . Kami akan segera membuat reservasi agar semua antarmuka kriptografis untuk RSA dalam versi baru paket TclPKCS11 v.10.1 disimpan. Pustaka paket ditulis dalam bahasa C.
Jadi, apa yang baru dalam paket itu? Pertama-tama, sebuah perintah telah ditambahkan yang memungkinkan Anda untuk mendapatkan daftar mekanisme kriptografi yang didukung oleh token yang terhubung:
::pki::pkcs11::listmechs <handl> <slotid>
Cara mendapatkan daftar slot dengan token yang terhubung ditampilkan di
sini (prosedur - proc :: slots_with_token):
proc ::slots_with_token {handle} { set slots [pki::pkcs11::listslots $handle] # puts "Slots: $slots" array set listtok [] foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { set listtok($slotid) $slotlabel } } # parray listtok return [array get listtok] }
Ambil skrip sederhana:
#!/usr/bin/tclsh lappend auto_path . package require pki::pkcs11 # RuToken set lib "/usr/local/lib64/librtpkcs11ecp_2.0.so" <source lang="bash">set handle [pki::pkcs11::loadmodule $lib] # # set labslot [::slots_with_token $handle] if {[llength $labslot] == 0} { puts " " exit } set slotid 0 set lmech [pki::pkcs11::listmechs $handle $slotid] set i 0 foreach mm $lmech { # if {[string first "GOSTR3410" $mm] != -1} { puts -nonewline "[lindex $mm 0] " if {$i == 2} {puts "";set i 0} else { incr i} } } puts "\n" exit
Skrip ini memungkinkan Anda untuk mendapatkan daftar mekanisme kriptografi GOSTR3410 yang didukung pada token keluarga RuToken. Sebagai permulaan, mari kita ambil, seperti yang ditulis
Pas dalam
artikel itu , "Cahaya Rutoken yang dicintai oleh semua jenis EDO":
$ tclsh TEST_for_HABR.tcl listtok(0) = ruToken Lite 0 {ruToken Lite } $
Dan tentu saja ternyata dia tidak mendukung mezanisme GOST, yang harus dibuktikan. Kami mengambil token lain Rutoken EDS:
$ tclsh TEST_for_HABR.tcl listtok(0) = ruToken ECP } 0 {ruToken ECP } CKM_GOSTR3410_KEY_PAIR_GEN CKM_GOSTR3410 CKM_GOSTR3410_DERIVE CKM_GOSTR3410_WITH_GOSTR3411 $
Ya, token ini mendukung kriptografi Rusia, tetapi hanya tanda tangan GOST R 34.10-2001, yang hampir
tidak digunakan . Tetapi jika Anda mengambil token Rutoken EDS-2.0, maka semuanya akan baik-baik saja, itu mendukung GOST R 34.10-2012 dengan panjang kunci 256 dan 512 bit:
$ tclsh TEST_for_HABR.tcl listtok(0) = RuTokenECP20 0 {RuTokenECP20 } CKM_GOSTR3410_KEY_PAIR_GEN CKM_GOSTR3410 CKM_GOSTR3410_DERIVE CKM_GOSTR3410_512_KEY_PAIR_GEN CKM_GOSTR3410_512 CKM_GOSTR3410_12_DERIVE CKM_GOSTR3410_WITH_GOSTR3411 CKM_GOSTR3410_WITH_GOSTR3411_12_256 CKM_GOS TR3410_WITH_GOSTR3411_12_512 $

Jika kita berbicara tentang mendukung kriptografi Rusia, termasuk algoritma enkripsi belalang dan magma, dengan satu atau lain token, maka itu paling didukung sepenuhnya oleh perangkat lunak dan token
cloud , dan ini wajar:
$ tclsh TEST_for_HABR.tcl listtok(0) = LS11SW2016_LIN_64 0 {LS11SW2016_LIN_64 }
Daftar mekanismeCKM_GOSTR3410_KEY_PAIR_GEN
CKM_GOSTR3410_512_KEY_PAIR_GEN
CKM_GOSTR3410
CKM_GOSTR3410_512
CKM_GOSTR3410_WITH_GOSTR3411
CKM_GOSTR3410_WITH_GOSTR3411_12_256
CKM_GOSTR3410_WITH_GOSTR3411_12_512
CKM_GOSTR3410_DERIVE
CKM_GOSTR3410_12_DERIVE
CKM_GOSR3410_2012_VKO_256
CKM_GOSR3410_2012_VKO_512
CKM_KDF_4357
CKM_KDF_GOSTR3411_2012_256
CKM_KDF_TREE_GOSTR3411_2012_256
CKM_GOSTR3410_KEY_WRAP
CKM_GOSTR3410_PUBLIC_KEY_DERIVE
CKM_LISSI_GOSTR3410_PUBLIC_KEY_DERIVE
CKM_GOST_GENERIC_SECRET_KEY_GEN
CKM_GOST_CIPHER_KEY_GEN
CKM_GOST_CIPHER_ECB
CKM_GOST_CIPHER_CBC
CKM_GOST_CIPHER_CTR
CKM_GOST_CIPHER_OFB
CKM_GOST_CIPHER_CFB
CKM_GOST_CIPHER_OMAC
CKM_GOST_CIPHER_KEY_WRAP
CKM_GOST_CIPHER_ACPKM_CTR
CKM_GOST_CIPHER_ACPKM_OMAC
CKM_GOST28147_KEY_GEN
CKM_GOST28147
CKM_GOST28147_KEY_WRAP
CKM_GOST28147_PKCS8_KEY_WRAP
CKM_GOST_CIPHER_PKCS8_KEY_WRAP
CKM_GOST28147_ECB
CKM_GOST28147_CNT
CKM_GOST28147_MAC
CKM_KUZNYECHIK_KEY_GEN
CKM_KUZNYECHIK_ECB
CKM_KUZNYECHIK_CBC
CKM_KUZNYECHIK_CTR
CKM_KUZNYECHIK_OFB
CKM_KUZNYECHIK_CFB
CKM_KUZNYECHIK_OMAC
CKM_KUZNYECHIK_KEY_WRAP
CKM_KUZNYECHIK_ACPKM_CTR
CKM_KUZNYECHIK_ACPKM_OMAC
CKM_MAGMA_KEY_GEN
CKM_MAGMA_ECB
CKM_MAGMA_CBC
CKM_MAGMA_CTR
CKM_MAGMA_OFB
CKM_MAGMA_CFB
CKM_MAGMA_OMAC
CKM_MAGMA_KEY_WRAP
CKM_MAGMA_ACPKM_CTR
CKM_MAGMA_ACPKM_OMAC
CKM_GOSTR3411
CKM_GOSTR3411_12_256
CKM_GOSTR3411_12_512
CKM_GOSTR3411_HMAC
CKM_GOSTR3411_12_256_HMAC
CKM_GOSTR3411_12_512_HMAC
CKM_PKCS5_PBKD2
CKM_PBA_GOSTR3411_WITH_GOSTR3411_HMAC
CKM_TLS_GOST_KEY_AND_MAC_DERIVE
CKM_TLS_GOST_PRE_MASTER_KEY_GEN
CKM_TLS_GOST_MASTER_KEY_DERIVE
CKM_TLS_GOST_PRF
CKM_TLS_GOST_PRF_2012_256
CKM_TLS_GOST_PRF_2012_512
CKM_TLS12_MASTER_KEY_DERIVE
CKM_TLS12_KEY_AND_MAC_DERIVE
CKM_TLS_MAC
CKM_TLS_KDF
CKM_TLS_TREE_GOSTR3411_2012_256
CKM_EXTRACT_KEY_FROM_KEY
CKM_SHA_1
CKM_MD5
$
Kami beralih ke fitur baru berikutnya yang ditambahkan ke paket:
set listcertsder [pki::pkcs11::listcertsder $handle $slotid]
Fungsi ini mengembalikan daftar sertifikat yang disimpan tanpa token. Pertanyaan secara alami muncul, tetapi bagaimana perbedaannya dari fungsi yang ada pki :: pkcs11 :: listcerts?
Pertama-tama, fungsi baru tidak menggunakan paket :: pki. Salah satu elemen yang dikembalikan adalah elemen cert_der, yang berisi sertifikat lengkap. Ini nyaman, misalnya, ketika mengekspor sertifikat, atau menerima sidik jarinya.
Sebelumnya, saya harus mengumpulkan sertifikat lengkap dari sertifikat tbs dan tanda tangannya. Daftar lengkap item yang dikembalikan untuk setiap sertifikat terlihat jelas saat mencetak konten dari satu sertifikat:
. . . array set derc [[pki::pkcs11::listcertsder $handle $slotid] 0] parray derc derc(cert_der) = 3082064a … derc(pkcs11_handle) = pkcsmod0 derc(pkcs11_id) = 5882d64386211cf3a8367d2f87659f9330e5605d derc(pkcs11_label) = Thenderbird-60 derc(pkcs11_slotid) = 0 derc(type) = pkcs11 . . .
Elemen pkcs11_id menyimpan atribut CKA_ID nilai hash SHA-1 dari kunci publik. Elemen cert_der adalah CKA_VALUE dari sertifikat, pkcs11_label adalah CKA_LABEL.
Elemen pkcs11_id (CKA_ID dalam terminologi standar PKCS # 11) adalah, bersama dengan pkcs11_handle library, dan pengidentifikasi slot dengan pkcs11_slotid token elemen kunci
untuk mengakses kunci dan sertifikat yang disimpan pada token.
Jadi, jika kita ingin mengubah label (pkcs11_label) dari sertifikat atau kunci, kita menjalankan perintah dari formulir:
pki::pkcs11::rname <cert|key|all> < >
Untuk menghapus sertifikat atau kunci dari token, perintah dari formulir
pki::pkcs11::delete <cert|key|all> < >
Daftar elemen-elemen kunci dapat dibentuk sebagai berikut:
set listparam {} lappend listparam pkcs11_handle lappend listparam $handle lappend listparam pkcs11_slotid lappend listparam $pkcs11_slotid lappend listparam pkcs11_id lappend listparam $pkcs11_id
dll.
Panggilan fungsi dalam hal ini terlihat seperti ini (kami akan menghapus sertifikat dan kunci yang terkait dengannya):
pki::pkcs11::delete all $listparam
Pembaca mungkin sudah menebak bahwa daftar ini dapat diatur sebagai kamus dict:
set listparam [dict create pkcs11_handle $pkcs11_handle] dict set listparam pkcs11_slotid $pkcs11_slotid) dict set listparam pkcs11_id $pkcs11_id
Ada cara lain, misalnya, melalui array.
Sekali lagi, kami mencatat bahwa elemen pkcs11_handle dan pkcs11_slotid harus selalu ada dalam daftar elemen kunci, yang secara unik mengidentifikasi token yang terhubung. Sisa komposisi ditentukan oleh fungsi tertentu.
Fungsi berikut digunakan untuk menginstal sertifikat pada token:
set pkcs11_id_cert [::pki::pkcs11::importcert <cert_der_hex> < >
Fungsi mengembalikan nilai CKA_ID dalam heksadesimal. Daftar parameter kunci menentukan token tempat sertifikat akan ditempatkan:
{pkcs11_handle <handle> pkcs11_slotid <slotid>}
Selanjutnya adalah perhitungan hash kami. Dalam kriptografi Rusia saat ini, tiga jenis fungsi hash digunakan:
- GOST R 34.11-94
- GOST R 34 .11-2012 dengan nilai hash 256 bit (stribog256)
- GOST R 34 .11-2012 dengan nilai hash 512 bit (stribog512)
Untuk menentukan hash mana yang mendukung token, kita memiliki fungsi pki :: pkcs11 :: listmechs.
Fungsi perhitungan hash memiliki bentuk berikut:
set <> [pki::pkcs11::digest <gostr3411|stribog256|stribog512|sha1> < > < >]
Perhatikan bahwa hasil perhitungan dari perhitungan disajikan dalam heksadesimal:
. . . set listparam [dict create pkcs11_handle $pkcs11_handle] dict set listparam pkcs11_slotid $pkcs11_slotid set res_hex [pki::pkcs11::digest stribog256 0123456789 $listparam] puts $res_hex 086f2776f33aae96b9a616416b9d1fe9a049951d766709dbe00888852c9cc021
Untuk verifikasi, mari kita
buka openssl dengan dukungan untuk kriptografi Rusia :
$ echo -n "0123456789"|/usr/local/lirssl_csp_64/bin/lirssl_s tatic dgst -md_gost12_256 (stdin)= 086f2776f33aae96b9a616416b9d1fe9a0499 51d766709dbe00888852c9 cc021 $
Seperti yang Anda lihat, hasilnya identik.
Untuk memverifikasi tanda tangan elektronik, apakah itu sertifikat atau daftar sertifikat yang dicabut atau dokumen yang ditandatangani dalam format, kita sekarang hanya memerlukan fungsi verifikasi tanda tangan:
set result [pki::pkcs11::verify < > < > < >]]
Jika tanda tangan lulus verifikasi, maka 1 dikembalikan, jika tidak, 0. Untuk memverifikasi tanda tangan elektronik, tanda tangan dokumen itu sendiri, hash dokumen, ditentukan oleh jenis tanda tangan, dan kunci publik yang dengannya tanda tangan itu dibuat, dengan semua parameter (nilai, jenis dan parameter), diperlukan. . Semua informasi tentang kunci dalam bentuk struktur ASN1 publickeyinfo harus dimasukkan dalam daftar elemen kunci:
lpkar (pkcs11_handle) = pkcsmod0
lpkar (pkcs11_slotid) = 0
lpkar (pubkeyinfo) = 301f06082a85030701010101301306072a85030202240
006082a8503070101020203430004407d9306687af5a8e63af4b09443ed2e03794be
10eba6627bf5fb3da1bb474a3507d2ce2cd24b63c727a02521897d1dd6edbdc7084d
8886a39289c3f81bdf2e179
Struktur kunci publik ASN1 diambil dari sertifikat penandatangan:
proc ::pki::x509::parse_cert_pubkeyinfo {cert_hex} { array set ret [list] set wholething [binary format H* $cert_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 binary scan $pubkeyinfo H* ret(pubkeyinfo) return $ret(pubkeyinfo) }
Teks skrip untuk memverifikasi tanda tangan elektronik sertifikat dari suatu file berada
disini #! /usr/bin/env tclsh package require pki lappend auto_path . package require pki::pkcs11 # PKCS#11 #set pkcs11_module "/usr/local/lib/libcackey.so" #set pkcs11_module "/usr/local/lib64/librtpkcs11ecp_2.0.so" set pkcs11_module "/usr/local/lib64/libls11sw2016.so" puts "Connect the Token and press Enter" gets stdin yes set handle [pki::pkcs11::loadmodule $pkcs11_module] set slots [pki::pkcs11::listslots $handle] foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { set token_slotlabel $slotlabel set token_slotid $slotid # break } } # PEM DER proc ::cert_to_der {data} { if {[string first "-----BEGIN CERTIFICATE-----" $data] != -1} { set data [string map {"\r\n" "\n"} $data] } array set parsed_cert [::pki::_parse_pem $data "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"] if {[string range $parsed_cert(data) 0 0 ] == "0" } { # DER- "0" == 0x30 set asnblock $parsed_cert(data) } else { set asnblock "" } return $asnblock } proc usage {use error} { puts "Copyright(C) Orlov Vladimir (http://soft.lissi.ru) 2019" if {$use == 1} { puts $error puts "Usage:\nverify_cert_with_pkcs11 <file with certificate> \[<file with CA certificate>\]\n" } } set countcert [llength $argv] if { $countcert < 1 || $countcert > 2 } { usage 1 "Bad usage!" exit } set file [lindex $argv 0] if {![file exists $file]} { usage 1 "File $file not exist" exit } # cert_user puts "Loading user certificate: $file" set fd [open $file] chan configure $fd -translation binary set cert_user [read $fd] close $fd if {$cert_user == "" } { usage 1 "Bad file with certificate user: $file" exit } set cert_user [cert_to_der $cert_user] if {$cert_user == ""} { puts "User certificate bad" exit } catch {array set cert_parse [::pki::x509::parse_cert $cert_user]} if {![info exists cert_parse]} { puts "User certificate bad" exit } #parray cert_parse if {$countcert == 1} { if {$cert_parse(issuer) != $cert_parse(subject)} { puts "Bad usage: not self signed certificate" } else { set cert_CA $cert_user } } else { set fileca [lindex $argv 1] if {![file exists $fileca]} { usage 1 "File $fileca not exist" exit } # cert_CA puts "Loading CA certificate: $fileca" set fd [open $fileca] chan configure $fd -translation binary set cert_CA [read $fd] close $fd if {$cert_CA == "" } { usage 1 "Bad file with certificate CA=$fileca" exit } set cert_CA [cert_to_der $cert_CA] if {$cert_CA == ""} { puts "CA certificate bad" exit } } foreach slotinfo $slots { set slotid [lindex $slotinfo 0] set slotlabel [lindex $slotinfo 1] set slotflags [lindex $slotinfo 2] if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} { set token_slotlabel $slotlabel set token_slotid $slotid } } # #array set cert_parse_CA [::pki::x509::parse_cert $cert_CA] catch {array set cert_parse_CA [::pki::x509::parse_cert $cert_CA]} #array set cert_parse_CA [::pki::x509::parse_cert $cert_CA_256] #array set cert_parse_CA [::pki::x509::parse_cert $CA_12_512] if {![info exists cert_parse_CA]} { puts "CA certificate bad" exit } ############################### set aa [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid] set tbs_cert [binary format H* $cert_parse(cert)] #puts "SIGN_ALGO1=$cert_parse(signature_algo)" catch {set signature_algo_number [::pki::_oid_name_to_number $cert_parse(signature_algo)]} if {![info exists signature_algo_number]} { set signature_algo_number $cert_parse(signature_algo) } #puts "SIGN_ALGO=$signature_algo_number" switch -- $signature_algo_number { "1.2.643.2.2.3" - "1 2 643 2 2 3" { # "GOST R 34.10-2001 with GOST R 34.11-94" set digest_algo "gostr3411" } "1.2.643.7.1.1.3.2" - "1 2 643 7 1 1 3 2" { # "GOST R 34.10-2012-256 with GOSTR 34.11-2012-256" set digest_algo "stribog256" } "1.2.643.7.1.1.3.3" - "1 2 643 7 1 1 3 3" { # "GOST R 34.10-2012-512 with GOSTR 34.11-2012-512" set digest_algo "stribog512" } default { puts " :$signature_algo_number" exit } } # tbs-!!!! set digest_hex [pki::pkcs11::digest $digest_algo $tbs_cert $aa] puts "digest_hex=$digest_hex" puts [string length $digest_hex] # asn- # binary scan $cert_CA H* cert_CA_hex array set infopk [pki::pkcs11::pubkeyinfo $cert_CA_hex [list pkcs11_handle $handle pkcs11_slotid $token_slotid]] parray infopk set lpk [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid] # pybkeyinfo lappend lpk "pubkeyinfo" #lappend lpk $pubinfo lappend lpk $infopk(pubkeyinfo) array set lpkar $lpk parray lpkar puts "Enter PIN user for you token \"$token_slotlabel\":" #set password "01234567" gets stdin password if { [pki::pkcs11::login $handle $token_slotid $password] == 0 } { puts "Bad password" exit } if {[catch {set verify [pki::pkcs11::verify $digest_hex $cert_parse(signature) $lpk]} res] } { puts $res exit } if {$verify != 1} { puts "BAD SIGNATURE=$verify" } else { puts "SIGNATURE OK=$verify" } puts "!" exit
Simpan skrip dalam file dan coba jalankan:
$./verify_cert_with_pkcs11.tcl Copyright(C) Orlov Vladimir (http://museum.lissi-crypto.ru/) Usage: verify_cert_with_pkcs11 <file with certificate> <file with CA certificate> $
Orang bertanya-tanya, bagaimana dengan sertifikat pada token? Pertama, kami memecahkan masalah menggunakan mesin kriptografi PKCS # 11. Kami menggunakannya. Dan untuk menayangkan sertifikat dengan token, ada fungsi paket pki :: pkcs11 :: listcertsder, yang memungkinkan Anda memilih sertifikat yang diinginkan dan memverifikasinya. Ini bisa dianggap sebagai pekerjaan rumah.
Munculnya versi baru dari paket TclPKCS11v.1.0.1 memungkinkan untuk memperbaiki
utilitas melihat sertifikat
dengan menambahkan fungsi mengimpor sertifikat untuk token, menghapus sertifikat dan kunci terkait dari token, mengubah label sertifikat dan kunci, dll .:

Fitur terpenting yang ditambahkan adalah verifikasi tanda tangan digital dari sertifikat:

Pembaca yang penuh perhatian dengan benar mencatat bahwa tidak ada yang dikatakan tentang generasi pasangan kunci. Fitur ini juga ditambahkan ke paket TclPKCS11:
array set genkey [pki::pkcs11::keypair < > <> < >]
Bagaimana fungsi dari paket TclPKCS11 digunakan, tentu saja, dapat ditemukan dalam kode sumber utilitas.
Fungsi menghasilkan pasangan kunci akan dibahas secara rinci dalam artikel berikutnya, ketika utilitas untuk membuat permintaan untuk sertifikat yang memenuhi syarat dengan generasi pasangan kunci pada token PKCS # 11, mekanisme untuk mendapatkan sertifikat di pusat sertifikasi (
CA ) dan mengimpornya ke token akan disajikan:

Dalam artikel yang sama, fungsi penandatanganan dokumen akan dipertimbangkan. Ini akan menjadi artikel terakhir dalam seri ini. Selanjutnya, serangkaian artikel direncanakan untuk mendukung kriptografi Rusia dalam bahasa scripting Ruby yang sekarang menjadi mode. Sampai ketemu lagi!