我使用Wei Dai的Crypto ++在我的应用程序中创建了一个ECDSA密钥对(secp128r1)。签名和验证按预期工作。我没有将消息本身添加到签名中以最小化签名长度(正好是32字节)。
但是,当我使用openssl创建签名时:
$ cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > act.bin
OpenSSL显然将消息本身放在签名上,从而产生更大的签名(例如39字节)。如果我设置CryptoPP::SignatureVerificationFilter::PUT_MESSAGE
,我可以使用Crypto ++验证签名。
我是否可以告诉OpenSSL签署一条消息,而不是将消息放入签名中,以便生成的签名完全是32字节?
答案 0 :(得分:7)
CodesInChaos是正确的。签名中的额外字节来自ASN.1编码,而不是签名的原始消息。例如,这是一个39字节的签名,使用带有曲线secc128r1的ECDSA密钥生成:
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告诉你第一个Integer是0x10字节长。以下0x10(16)字节是ECDSA签名的“r”值。在第一个整数之后是字节02.这告诉你序列的第二个整数即将开始。 11告诉您下一个0x11(17)字节构成第二个整数,这是ECDSA签名的“s”值。它是11个字节,因为整数的第一个字节是00.只要整数的第一个字节是> = 0x80,就会插入“00”。这是为了避免最高有效位为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格式化。
答案 1 :(得分:1)
DIVB>我是否可以告诉openssl签署一条消息而不将消息放入签名,以便生成的签名完全是32字节?
和
和Sandeep>在cryptopp中是否有任何功能可以进行这种转换?
正如@CodesInChaos所述,问题是签名编码。另请参阅OpenSSL ECDSA signatures longer than expected。
正如@Sandeep在评论中建议的那样,另一个选择是让Crypto ++使用OpenSSL签名。
这是一个Crypto ++测试程序,用于转换OpenSSL和Java使用的ASN.1 / DER输出,并将其转换为Crypto ++使用的IEEE P1363。它主要取自Crypto ++ wiki上的Elliptic Curve Digital Signature Algorithm。
#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添加到库中,则需要重建库。或者,您可以将其构建为程序的一部分。
答案 2 :(得分:0)
首先,您应该知道128位的EC提供大约64位的安全性。 其次我不认为这是openssl添加的消息,因为5个字节不足以满足要求。 无论如何,您可以使用head或tail来删除额外的字节。