Ваш файл signature.sig представляется в кодировке base64. Расшифруйте его так:
$ base64 -d signature.sig >signature.bin
Давайте посмотрим, что у нас есть:
$ hexdump -C signature.bin
00000000 24 98 70 45 e1 de bf c7 31 3a c3 4a 09 1e 6d fc |$.pE....1:.J..m.|
00000010 47 b7 59 4f 5c ee d9 1f f5 1b 86 35 a9 97 76 95 |G.YO\......5..v.|
00000020 d0 bb d3 8b f1 92 a7 b2 b6 e5 08 ee ef 12 63 97 |..............c.|
00000030 18 a1 ab 93 a3 6c 80 0e 49 66 94 21 5c ed c0 d5 |.....l..If.!\...|
00000040
Для сравнения я создал новый закрытый ключ ECDSA на основе той же кривой, что и ваш ключ publi c использует (P-256):
$ openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
И затем подписывает некоторые данные, используя их:
$ echo "HELLO" > hello.txt
$ openssl dgst -sha256 -sign key.pem -out hello.sig hello.txt
$ openssl asn1parse -in hello.sig -inform DER
0:d=0 hl=2 l= 68 cons: SEQUENCE
2:d=1 hl=2 l= 32 prim: INTEGER :2C1599C7765B047A2E98E2265CF6DB91232200559909D7F97CA3E859A39AC02C
36:d=1 hl=2 l= 32 prim: INTEGER :14E748DF692A8A7A2E41F984497782FF03F970DDB6591CCC68C71704B959A480
Итак, вы заметите, что у нас есть два целых числа в последовательности где каждое целое число имеет длину ровно 32 байта. Это соответствует определению ECDSA_SIG ASN.1:
ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
Необработанная сигнатура ECDSA состоит из двух целых чисел «r» и «s». OpenSSL ожидает, что они будут заключены в кодированное представление DER. Однако, как вы уже обнаружили, что у вас есть для подписи, не является действительным DER. Он равен , но ровно 64 байта в длину - что предполагает, что он состоит из 2-х 32-байтовых целых чисел, соединенных вместе.
Для целей этого упражнения мы можем использовать шестнадцатеричный редактор для преобразования необработанного r и значения в формате DER. Давайте посмотрим на hexdump файла hello.sig, который я создал ранее:
$ hexdump -C hello.sig
00000000 30 44 02 20 2c 15 99 c7 76 5b 04 7a 2e 98 e2 26 |0D. ,...v[.z...&|
00000010 5c f6 db 91 23 22 00 55 99 09 d7 f9 7c a3 e8 59 |\...#".U....|..Y|
00000020 a3 9a c0 2c 02 20 14 e7 48 df 69 2a 8a 7a 2e 41 |...,. ..H.i*.z.A|
00000030 f9 84 49 77 82 ff 03 f9 70 dd b6 59 1c cc 68 c7 |..Iw....p..Y..h.|
00000040 17 04 b9 59 a4 80 |...Y..|
00000046
Мы начнем с 30
, который говорит нам, что у нас есть последовательность. Следующий байт 44
- это длина оставшихся данных. Далее следует 02
, который является тегом для целого числа, за которым следует 20
(который равен 32 в десятичном виде), который является длиной целого числа. Следующие 32 байта представляют собой целое число (значение r
). Затем у нас есть еще 02
байт (целое число) и 20
(длина 32), за которыми следуют 32 байта значения s
.
Так что, если мы добавим байты 30 44 02 20
вперед из ваших двоичных данных подписи, за которыми следуют первые 32 байта данных, затем 02 20
, за которыми следуют следующие 32 байта, мы должны получить то, что хотим ...
... за исключением, к сожалению, это не совсем так просто. В вашем s
значении есть осложнение. Вы заметите, что он начинается с байта d0
. Этот байт имеет наиболее значимый установленный бит, который в кодировании DER целого числа указывает, что целочисленное значение является отрицательным. Это не то, что мы хотим. Чтобы обойти это, мы должны добавить дополнительный 00
байт в начало значения s
.
Это изменяет общую длину, поэтому теперь мы должны добавить эти байты в начало 30 45 02 20
за ними следуют первые 32 байта данных подписи, затем 02 21 00
, за которыми следуют следующие 32 байта данных подписи. Я сделал это в шестнадцатеричном редакторе и придумал следующее:
$ hexdump -C signature2.bin
00000000 30 45 02 20 24 98 70 45 e1 de bf c7 31 3a c3 4a |0E. $.pE....1:.J|
00000010 09 1e 6d fc 47 b7 59 4f 5c ee d9 1f f5 1b 86 35 |..m.G.YO\......5|
00000020 a9 97 76 95 02 21 00 d0 bb d3 8b f1 92 a7 b2 b6 |..v..!..........|
00000030 e5 08 ee ef 12 63 97 18 a1 ab 93 a3 6c 80 0e 49 |.....c......l..I|
00000040 66 94 21 5c ed c0 d5 |f.!\...|
00000047
Давайте проверим, выглядит ли это вменяемым:
$ openssl asn1parse -in signature2.bin -inform DER
0:d=0 hl=2 l= 69 cons: SEQUENCE
2:d=1 hl=2 l= 32 prim: INTEGER :24987045E1DEBFC7313AC34A091E6DFC47B7594F5CEED91FF51B8635A9977695
36:d=1 hl=2 l= 33 prim: INTEGER :D0BBD38BF192A7B2B6E508EEEF12639718A1AB93A36C800E496694215CEDC0D5
Теперь давайте попробуем проверить подпись:
$ openssl dgst -sha256 -verify pubkey.pem -signature signature2.bin hello.txt
Verification Failure
Черт. Так близко и еще так далеко. Но по крайней мере мы избавились от ошибок ASN.1. Так почему это не работает? По догадке я сделал это:
echo -n "HELLO" > hello2.txt
Аргумент "-n" для эха подавляет переводы строк с выхода. Возможно, новую строку не следует включать в данные, которые будут перевариваться для подписи. Итак, попробуем это:
$ openssl dgst -sha256 -verify pubkey.pem -signature signature2.bin hello2.txt
Verified OK
Удачи!