ECDSA подписать с OpenSSL, проверить с помощью Crypto ++

Я создаю пару ключей ECDSA (secp128r1) в своем приложении с помощью Crypto ++ от Wei Dai. Подписание и проверка работ, как ожидается. Я не добавляю само сообщение в подпись, чтобы минимизировать длину подписи (которая составляет ровно 32 байта).

Однако, когда я создаю подпись с помощью openssl:

$ cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > act.bin

OpenSSL, очевидно, помещает само сообщение в подпись, что приводит к большей подписи (например, 39 байт). Я могу проверить подпись с Crypto ++, если я установил CryptoPP::SignatureVerificationFilter::PUT_MESSAGE,

Могу ли я сказать OpenSSL подписать сообщение, не помещая сообщение в подпись так, чтобы результирующая подпись была точно 32 байта?

2

Решение

CodesInChaos это правильно. Дополнительные байты в подписи взяты из кодировки ASN.1, а не из подписываемого исходного сообщения. Например, вот 39-байтовая подпись, сгенерированная с помощью ключа ECDSA с кривой secp128r1:

30 25 02 10 4E 32 32 90 CA D9 BD D2 5F 8B BE 3B
F2 BF E9 7F 02 11 00 A7 83 A6 68 AD 74 7E 1A 0E
8F 73 BD DF 7A E8 B5

30 указывает, что последовательность следует. 25 говорит вам, что последовательность имеет длину 0x25 байт. 02 указывает, что первый элемент в последовательности является целым числом. 10 говорит вам, что первое целое число имеет длину 0x10 байт. Следующие 0x10 (16) байтов являются значением «r» подписи ECDSA. За первым целым числом следует байт 02. Это говорит о том, что 2-е целое число последовательности должно начаться. 11 говорит вам, что следующие 0x11 (17) байтов составляют 2-е целое число, которое является значением «s» сигнатуры ECDSA. Это 11 байтов, потому что первый байт целого числа равен 00. «00» вставляется всякий раз, когда первый байт целого числа равен> = 0x80. Это сделано для того, чтобы избежать старшего значащего бита, равного 1, что указывало бы на отрицательное целое число.

Итак, после всего этого настоящие значения подписи:

r: 4E 32 32 90 CA D9 BD D2 5F 8B BE 3B F2 BF E9 7F
s: A7 83 A6 68 AD 74 7E 1A 0E 8F 73 BD DF 7A E8 B5

«Дополнительные» байты предназначены для форматирования ASN.1.

7

Другие решения

divb> Могу ли я сказать openssl подписать сообщение, не помещая сообщение в подпись так, чтобы результирующая подпись была точно 32 байта?

А также

sandeep> есть ли в cryptopp функции, которые делают это преобразование?

Как заявил @CodesInChaos, проблема заключается в кодировании подписи. Также см Подписи OpenSSL ECDSA длиннее, чем ожидалось.

Как предложил @Sandeep в комментарии, другой вариант — использовать Crypto ++ для использования подписи OpenSSL.

Вот тестовая программа Crypto ++ для преобразования из вывода ASN.1 / DER, используемого OpenSSL и Java, и преобразования его в IEEE P1363, используемый Crypto ++. Это в основном берется из Алгоритм цифровой подписи эллиптической кривой на Crypto ++ вики.

#include "cryptlib.h"#include "eccrypto.h"#include "files.h"#include "oids.h"#include "dsa.h"#include "sha.h"#include "hex.h"
#include <iostream>

using namespace CryptoPP;

int main(int argc, char* argv[])
{
// Load DER encoded public key
FileSource pubKey("secp256k1-pub.der", true /*binary*/);
ECDSA<ECP, SHA1>::Verifier verifier(pubKey);

// Java or OpenSSL created signature. Is is ANS.1
//   SEQUENCE ::= { r INTEGER, s INTEGER }.
const byte derSignature[] = {
0x30, 0x44, 0x02, 0x20, 0x08, 0x66, 0xc8, 0xf1,
0x6f, 0x15, 0x00, 0x40, 0x8a, 0xe2, 0x1b, 0x40,
0x56, 0x28, 0x9c, 0x17, 0x8b, 0xca, 0x64, 0x99,
0x37, 0xdc, 0x35, 0xad, 0xad, 0x60, 0x18, 0x4d,
0x63, 0xcf, 0x4a, 0x06, 0x02, 0x20, 0x78, 0x4c,
0xb7, 0x0b, 0xa3, 0xff, 0x4f, 0xce, 0xd3, 0x01,
0x27, 0x5c, 0x6c, 0xed, 0x06, 0xf0, 0xd7, 0x63,
0x6d, 0xc6, 0xbe, 0x06, 0x59, 0xe8, 0xc3, 0xa5,
0xce, 0x8a, 0xf1, 0xde, 0x01, 0xd5
};

// P1363 'r || s' concatenation. The size is 32+32 due to field
// size for r and s in secp-256. It is not 20+20 due to SHA-1.
byte signature[0x40];
DSAConvertSignatureFormat(signature, sizeof(signature), DSA_P1363,
derSignature, sizeof(derSignature), DSA_DER);

// Cross check
std::cout << "Signature:\n";
ArraySource(signature, sizeof(signature), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;

// Message "Attack at dawn!"const byte message[] = {
0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x61,
0x74, 0x20, 0x64, 0x61, 0x77, 0x6e, 0x21, 0x0a
};

// Standard signature checking in Crypto++
// https://www.cryptopp.com/wiki/Elliptic_Curve_Digital_Signature_Algorithm
bool result = verifier.VerifyMessage(message, sizeof(message), signature, sizeof(signature));
if (result)
std::cout << "Verified message" << std::endl;
else
std::cout << "Failed to verify message" << std::endl;

return 0;
}

И вот результат запуска тестовой программы.

$ ./test.exe
Signature (64):
0866C8F16F1500408AE21B4056289C178BCA649937DC35ADAD60184D63CF4A06784CB70BA3FF4FCE
D301275C6CED06F0D7636DC6BE0659E8C3A5CE8AF1DE01D5
Verified message

Вот настройки, которые я использовал для воспроизведения cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > test.sig,

$ cat test.txt
Attack at dawn!

$ hexdump -C test.txt
00000000  41 74 74 61 63 6b 20 61  74 20 64 61 77 6e 21 0a  |Attack at dawn!.|
00000010

# Create private key in PEM format
$ openssl ecparam -name secp256k1 -genkey -noout -out secp256k1-key.pem

$ cat secp256k1-key.pem
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIO0D5Rjmes/91Nb3dHY9dxmbM7gVfxmB2+OVuLmWMbGXoAcGBSuBBAAK
oUQDQgAEgVNEuirUNCEVdf7nLSBUgU1GXLrtIBeglIbK54s91HlWKOKjk4CkJ3/B
wGAfcYKa+DgJ2IUQSD15K1T/ghM9eQ==
-----END EC PRIVATE KEY-----

# Convert private key to ASN.1/DER format
$ openssl ec -in secp256k1-key.pem -inform PEM -out secp256k1-key.der -outform DER

$ dumpasn1 secp256k1-key.der
0 116: SEQUENCE {
2   1:   INTEGER 1
5  32:   OCTET STRING
:     ED 03 E5 18 E6 7A CF FD D4 D6 F7 74 76 3D 77 19
:     9B 33 B8 15 7F 19 81 DB E3 95 B8 B9 96 31 B1 97
39   7:   [0] {
41   5:     OBJECT IDENTIFIER secp256k1 (1 3 132 0 10)
:     }
48  68:   [1] {
50  66:     BIT STRING
:       04 81 53 44 BA 2A D4 34 21 15 75 FE E7 2D 20 54
:       81 4D 46 5C BA ED 20 17 A0 94 86 CA E7 8B 3D D4
:       79 56 28 E2 A3 93 80 A4 27 7F C1 C0 60 1F 71 82
:       9A F8 38 09 D8 85 10 48 3D 79 2B 54 FF 82 13 3D
:       79
:     }
:   }

# Create public key from private key
$ openssl ec -in secp256k1-key.der -inform DER -pubout -out secp256k1-pub.der -outform DER

$ dumpasn1 secp256k1-pub.der
0  86: SEQUENCE {
2  16:   SEQUENCE {
4   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
13   5:     OBJECT IDENTIFIER secp256k1 (1 3 132 0 10)
:     }
20  66:   BIT STRING
:     04 81 53 44 BA 2A D4 34 21 15 75 FE E7 2D 20 54
:     81 4D 46 5C BA ED 20 17 A0 94 86 CA E7 8B 3D D4
:     79 56 28 E2 A3 93 80 A4 27 7F C1 C0 60 1F 71 82
:     9A F8 38 09 D8 85 10 48 3D 79 2B 54 FF 82 13 3D
:     79
:   }

# Sign the message using the private key
$ cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign secp256k1-key.der -keyform DER > test.sig

# Dump the signature as hex
$ hexdump -C test.sig
00000000  30 44 02 20 08 66 c8 f1  6f 15 00 40 8a e2 1b 40  |0D. .f..o..@...@|
00000010  56 28 9c 17 8b ca 64 99  37 dc 35 ad ad 60 18 4d  |V(....d.7.5..`.M|
00000020  63 cf 4a 06 02 20 78 4c  b7 0b a3 ff 4f ce d3 01  |c.J.. xL....O...|
00000030  27 5c 6c ed 06 f0 d7 63  6d c6 be 06 59 e8 c3 a5  |'\l....cm...Y...|
00000040  ce 8a f1 de 01 d5                                 |......|
00000046

# Dump the signature as ASN.1/DER
$ dumpasn1 test.sig
0  68: SEQUENCE {
2  32:   INTEGER
:     08 66 C8 F1 6F 15 00 40 8A E2 1B 40 56 28 9C 17
:     8B CA 64 99 37 DC 35 AD AD 60 18 4D 63 CF 4A 06
36  32:   INTEGER
:     78 4C B7 0B A3 FF 4F CE D3 01 27 5C 6C ED 06 F0
:     D7 63 6D C6 BE 06 59 E8 C3 A5 CE 8A F1 DE 01 D5
:   }

В Crypto ++ можно использовать кодированный ключ PEM, а не кодированный ключ ASN.1 / DER. Для этого вам понадобится PEM Pack. Это дополнение сообщества, а не часть самой библиотеки.

Если вы добавите PEM Pack в библиотеку, то вам нужно перестроить библиотеку. Или вы можете построить его как часть вашей программы.

1

Прежде всего вы должны знать, что EC 128 бит обеспечивает безопасность около 64 бит.
Во-вторых, я не думаю, что это сообщение openssl добавляет, так как для этого недостаточно 5 байт.
В любом случае вы можете использовать голову или хвост, чтобы удалить лишние байты.

0
По вопросам рекламы [email protected]