Подписи OpenSSL ECDSA длиннее, чем ожидалось

Я пытаюсь сгенерировать "сырые", незакодированные подписи ECDSA для использования с криптографическим чипом. Цель состоит в том, чтобы что-то подписать на главном компьютере, а затем отправить это на чип для проверки. Однако у меня возникла небольшая проблема. Насколько я понимаю, подпись ECDSA должна быть 64 байта (для secp256v1). И когда я использую чип для генерации подписи, он действительно имеет длину 64 байта. Однако, когда я использую openssl, длина подписи составляет 71 байт. Начало подписи похоже на какой-то префикс, но я не могу найти никаких данных о том, что это такое.

Вот как я все пытаюсь:

Сгенерируйте ключ:

openssl ecparam -genkey -name secp256r1 -noout -out privkeyv1.pem

Сгенерируйте «сообщение» для подписи:

echo -n "Hello World" > test.txt

Я пробовал два способа подписать сообщение. Оба приводят к одному и тому же неожиданному результату.

Первый способ - сгенерировать хеш тестового файла sha256, затем подписать его:

sha256sum test.txt | cut -f 1 -d " " > hash

Подпишите с помощью pkutil

openssl pkeyutl -sign -in hash -inkey privkeyv1.pem -out test_sig_meth1

Метод 2: подпишите с помощью openssl dgst

openssl dgst -sha256 -binary -sign privkeyv1.pem -out test_sig_meth2 test.txt Проблема: вот результат xxd -p -c 256 test_sig_meth1: 3045022000a86fb146d5f8f6c15b962640bc2d1d928f5e0f96a5924e4db2853ec8b66fb002210085431613d0a235db1adabc090cc1062a246a78941972e298423f4b3d081b48c8

И вывод xxd -p -c 256 test_sig_meth2: 30450220693732cd53d9f2ba3deae213d74cdf69a00e7325a10ddc6a4445ff2b33f95e62022100b6d2561e3afba10f95247ed05f0c59620dc0913f0d798b4148e05c4116b6384e

Как видите, оба этих метода генерируют несколько байтов в начале, которые выглядят как байты заголовка (30450220, может быть, длиннее), но я не уверен, для чего они нужны и как их удалить. Для справки, вот подпись того же метода, сгенерированная на криптографическом чипе. Если вы удалите заполнение нулевым байтом в конце, это будет 64 байта. 4677AD09F2AF49D7445ED5D6AC7253ADC863EC6D5DB6D3CFBF9C6D3E221D0A7BA2561942524F46B590AEE749D827FBF80A961E884E3A7D85EC75FE48ADBC0BD00000000000000000000000

Вопрос: как я могу использовать openssl для генерации 64-байтовой необработанной (незашифрованной, без заголовка) подписи ECDSA, которую я могу использовать с этой схемой?


person f41lurizer    schedule 28.06.2017    source источник
comment
Я обнаружил, что вывод обеих команд зашифрован. Используя asn1parse -inform der, я смог найти целые числа, составляющие подпись (r и s)   -  person f41lurizer    schedule 28.06.2017
comment
Как это связано с C, пожалуйста?   -  person alk    schedule 28.06.2017
comment
Stack Overflow - это сайт для вопросов по программированию и разработке. Этот вопрос кажется не по теме, потому что он не о программировании или разработке. См. Какие темы можно задать здесь в Справочном центре. Возможно, суперпользователь или Unix и Linux Stack Exchange лучше спросить.   -  person jww    schedule 29.06.2017


Ответы (1)


Большинство микросхем по соображениям эффективности просто выводят r и s как массив байтов или строку октетов, где каждый r и s совпадает с размером поля (т. Е. Размером ключа) в октетах. Другой подход состоит в том, чтобы вывести r и s как последовательность чисел, потому что в конечном итоге это то, что есть r и s. При использовании ASN.1 это становится ПОСЛЕДОВАТЕЛЬНОСТЬЮ значений INTEGER.

Чтобы преобразовать из такой последовательности, вы можете сначала декодировать BER, используя синтаксический анализатор BER, чтобы получить целое число. Затем реализуйте алгоритм I2OSP (примитив потока целого числа в октет), который требует в качестве аргументов значение и размер ключа в байтах / октетах. Число должно быть в форме большого целого числа, но это нормально, поскольку целые числа в кодировке ASN.1 BER также являются большими целыми числами. По сути, вы должны оставить в поле нулевые байты, если число слишком мало. Затем вы объединяете число.

Я не буду вдаваться в OS2IP, которая преобразует массив байтов в целое число. Обратите внимание, что если вы кодируете его в форме BER, тогда целые числа не должны не заполняться нулевыми байтами. Так что некоторые хитрости все же требуются.

Таким образом, хотя подпись меняет форму, подпись остается действительной; вы можете просто преобразовать одну форму в другую, и подпись все равно будет проверяться - очевидно, до тех пор, пока вы используете правильную библиотеку для работы.

person Maarten Bodewes    schedule 28.06.2017
comment
Мне удалось преобразовать формат ключей с помощью openssl asn1parse -inform der -in test_sig_meth2, который выводит значения r и s. Спасибо! - person f41lurizer; 29.06.2017
comment
@ f41lurizer Хорошо, но не забывайте преобразование в строку октетов. Обычно это будет просто конкатенация ... до тех пор, пока вас не укусит более короткое значение r или s. - person Maarten Bodewes; 29.06.2017
comment
Я не понимаю, что вы имеете в виду, говоря о преобразовании в строку октетов. Это в основном шестнадцатеричное кодирование строки, или я что-то упустил? - person f41lurizer; 29.06.2017
comment
Строка октетов - это просто массив байтов. Октет = байт, строка = массив. Шестнадцатеричное кодирование позволяет сделать эти байты удобочитаемыми. Не путайте представление байтов с самими байтами. Итак, если у вас есть 256-битная кривая, которую вы должны кодировать до 32 байтов, и если у вас есть 521 (не опечатка), вы должны кодировать до 66 байтов, заполняя левым отступом число, закодированное с прямым порядком байтов. - person Maarten Bodewes; 30.06.2017
comment
Таким образом, хотя подпись меняет форму, подпись остается действительной; вы можете просто преобразовать одну форму в другую, и подпись все равно будет проверяться - пока вы используете правильную библиотеку для задания - См. также ECDSA подписать с помощью OpenSSL, подтвердить с помощью Crypto ++. Он конвертирует между форматами ASN.1 / DER (OpenSSL, Java и некоторые другие) и P1363 (Crypto ++ и некоторые другие). .Net - странный человек; он использует формат кодирования XML. - person jww; 16.02.2018