关于GOST R 34.11-2012哈希函数的开源实现及其对GOST R 34.10-2012电子签名的影响

有一次,在libgcrypt库中实现国内加密算法给我很大的启发。 通常,可以在Kleopatra中以及在Kmail和GnuPg中使用这些算法 ,以将libgcrypt库考虑为具有GOST引擎的openssl替代方法 。 直到上周五,一切都很棒。

我被要求验证在MS Windows上的Microsoft Office中创建的文档的GOST R 34.10-2012-256的电子签名。 我决定在Kleopatra(我有Linux)中进行检查。 您怎么看,签名证明是错误的。 怀疑逐渐蔓延。 我决定使用GOST引擎检查openssl 。 签名已成功验证。 紧急在Kleopatra中重新签名了文件,但该文件未通过MS Windows上的检查。 我们尝试签名并验证其他文件,一切都很好。 问题是出了什么问题? 由于文档哈希涉及签名,因此决定由其他程序检查哈希的计算。 首先, 涉及stribog的开源实现


然后发生了震惊! “著名的Degtyarev实现”计算出的哈希值与带有GOST端点的openssl中计算出的哈希值一致,但与使用libgcrypt和libressl计算出的哈希值不匹配。

ru_crypt文章开头写的内容是多么正确:
我立即警告您,我没有检查实现的正确性。

顺便说一句,关于GOST R 34.10-2012的标准还规定,控制示例仅供参考。 必须清楚地理解,测试用例并不能保证在所有情况下不同的实现都给出相同的结果。

要计算哈希值,使用了以下实用程序:

1)openssl

$ openssl dgst [–md_gost12_256|-md_gost12_512] <file> 

2)libressl

 $libressl dgst [–streebog256|streebog512] <file> 

3)libgcrypt

 $gchash [stribog256|stribog512] <file> 

4)Degtyarev的著名实现

 $gost3411-2012 [-2|-5] <file> 

这也是一件很有趣的事情:在拉丁语转录中,频闪笔写的是stribog或streebog。 达到统一会很好。 因此,这些似乎是不同的功能。 就个人而言,我更喜欢第一个选项-stribog。

我需要一名仲裁员。

作为仲裁员,决定使用PKCS#11令牌RUTOKEN EDS-2.0,该令牌支持俄罗斯密码标准GOST R 34.10-2012,GOST R 34.11-2012,VKO GOST R 34.10-2012(RFC 7836),密钥长度分别为256位和512位已获得俄罗斯FSB的认证,可作为加密信息保护(CPSI)和电子签名的手段。

此外,RUTOKEN EDS-2.0令牌已广泛分发,并且在其上存储了许多证书 ,以访问国家服务和其他门户。
要计算令牌上的哈希值,我们将在Tcl中使用test_digest.tcl脚本:

test_digest.tcl
 #! /usr/bin/env tclsh package require pki lappend auto_path . package require pki::pkcs11 #     PKCS#11 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 } } proc usage {use error} { puts "Copyright(C) Orlov Vladimir (http://soft.lissi.ru) 2019" if {$use == 1} { puts $error puts "Usage:\ndigest <stribog256|stribog512> <file for digest>\n" } } set countcert [llength $argv] if { $countcert != 2 } { usage 1 "Bad usage!" exit } set digest_algo [lindex $argv 0] if {$digest_algo != "stribog256" && $digest_algo != "stribog512"} { usage 1 "Bad usage!" exit } set file [lindex $argv 1] if {![file exists $file]} { usage 1 "File $file not exist" exit } puts "Loading file for digest: $file" set fd [open $file] chan configure $fd -translation binary set cert_user [read $fd] close $fd if {$cert_user == "" } { usage 1 "Bad file: $file" exit } set aa [dict create pkcs11_handle $handle pkcs11_slotid $token_slotid] set digest_hex [pki::pkcs11::digest $digest_algo $cert_user $aa] puts "digest_hex=\n$digest_hex" exit 


这种差异何时出现? 到目前为止,在计算在MS Office中创建的doc文件的哈希值时,可以确定出现这种差异。 此外,前143个字节的哈希被认为是相同的,并且从144个字节计算哈希时,值是不同的。

十六进制的前143个字节如下所示:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

将它们保存在文件Doc1_143_hex.txt中。

十六进制的前144个字节如下所示:

 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 

将它们保存在文件Doc1_144_hex.txt中。

使用hex2bin.tcl脚本将十六进制转换为二进制形式很方便:

 #!/usr/bin/tclsh proc usage {use error} { if {$use == 1} { puts $error puts "Usage:\nhex2bin <file with hex> <file for bin>\n" } } set countcert [llength $argv] if { $countcert != 2 } { usage 1 "Bad usage!" exit } set file [lindex $argv 0] if {![file exists $file]} { usage 1 "File $file not exist" exit } set fd [open $file] chan configure $fd -translation binary set cert_user [read $fd] close $fd if {$cert_user == "" } { usage 1 "Bad file with hex: $file" exit } set cert_user [binary format H* $cert_user] set fd [open [lindex $argv 1] w] chan configure $fd -translation binary puts -nonewline $fd $cert_user close $fd 

将十六进制代码转换为二进制:
 $./hex2bin Doc1_143_hex.txt Doc1_143.bin $./hex2bin Doc1_144_hex.txt Doc1_144.bin $ 

现在,您可以检查各种实现如何计算哈希值:
首先,考虑文件Doc1_143 bin的哈希:

 $ ./openssl dgst -md_gost12_256 Doc1_143.bin md_gost12_256(Doc1_143.bin)= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ ./libressl dgst -streebog256 Doc1_143.bin streebog256(Doc1_143.bin)= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ ./gchash stribog256 Doc1_143.bin e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 Doc1_143.bin $ ./gost3411-2012 -2 Doc1_143.bin GOST R 34.11-2012 (Doc1_143.bin) = e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ 

最重要的时刻已经到来,即在经过认证的密码信息保护系统上进行验证的时刻:

 $ ./test_digest.tcl stribog256 Doc1_143.bin Connect the Token and press Enter Loading file for digest: Doc1_143.bin digest_hex= e63bd3edc44f9a03fece4198b690a8ae291b973ae61b2a0f512a9a7479431a63 $ 

如您所见,一切都结束了。

让我们看看Doc1_144.bin文件会发生什么:

 $ ./openssl dgst -md_gost12_256 Doc1_144.bin md_gost12_256(Doc1_144.bin)= c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ ./libressl dgst -streebog256 Doc1_144.bin streebog256(Doc1_144.bin)= 3965c99777eb1b64c783496fe950aa6540bc7baa399a3889995145afbdd76250 $ 

就是这样,哈希值不匹配。 为了实验的纯洁,我们检查其余的实现:

 $ ./gchash_1.7.10 stribog256 Doc1_144.bin 3965c99777eb1b64c783496fe950aa6540bc7baa399a3889995145afbdd76250 Doc1_144.bin $ ./gost3411-2012 -2 Doc1_144.bin GOST R 34.11-2012 (Doc1_144.bin) = c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ ./test_digest.tcl stribog256 Doc1_144.bin Connect the Token and press Enter Loading file for digest: Doc1_144.bin digest_hex= c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0 $ 

“著名的Degtyarev实现”计算出的哈希值与使用GOST引擎在openssl中计算出的哈希值匹配,但与使用libgcrypt和libressl计算出的哈希值不匹配。

如果考虑散列stribog512,我们将得到类似的结果。

有一个结论。 如果您希望由libressl和libgcrypt(或其他厂商)生成的GOST R 34.10-2012电子签名与openssl实现兼容,并且最重要的是,与俄罗斯FSB认证系统中CIPF认证的实现兼容,请使用经过验证的计算哈希的实现。 我希望该出版物能够避免许多误解,并且在libressl,libgrypt和其他可能的版本中实现stribog的作者将有助于消除这些差异。 今天,我必须承认,在上述产品中,实现的实际上不是GOST R 34.10-2012,而是其他一些东西。 这是一种不同的算法。 给定的测试示例可能会很好地包含在GOST R 34.10-2012的测试示例中。 我将为Kleopatra和KMail编辑libgcrypt。 Cleopart和俄罗斯加密术的传说尚未完成。

PS当我的同事说当遇到足够长的0xFF序列时,实现之间的差异就出现了,这篇文章已经准备好了。 顺便说一下,她的顺序是出现在MS Office的doc文件的开头。 我按原样检查。 该文件包含189个字节。

Source: https://habr.com/ru/post/zh-CN450024/


All Articles