简体   繁体   English

ECDSA使用OpenSSL签名,使用Crypto ++进行验证

[英]ECDSA sign with OpenSSL, verify with Crypto++

I create an ECDSA keypair (secp128r1) in my application with Wei Dai's Crypto++. 我在Wei Dai的Crypto ++应用程序中创建了一个ECDSA密钥对(secp128r1)。 Signing and verifying works as expected. 签名和验证按预期工作。 I do not add the message itself to the signature to minimize the signature length (which is exactly 32 Bytes). 我没有将消息本身添加到签名中以最小化签名长度(正好是32字节)。

However, when I create the signature with openssl: 但是,当我使用openssl创建签名时:

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

OpenSSL obviously puts the message itself to the signature resulting in a larger signature (eg 39 Bytes). OpenSSL显然将消息本身放到签名上,从而产生更大的签名(例如39字节)。 I can verify the signature with Crypto++ if I set CryptoPP::SignatureVerificationFilter::PUT_MESSAGE . 如果我设置CryptoPP::SignatureVerificationFilter::PUT_MESSAGE我可以使用Crypto ++验证签名。

Can I tell OpenSSL to sign a message with NOT putting the message to the signature such that the resulting signature is 32 Byte exactly? 我是否可以告诉OpenSSL签署一条消息而不将消息放入签名,以便生成的签名完全是32字节?

CodesInChaos is correct. CodesInChaos是正确的。 The extra bytes in the signature are from the ASN.1 encoding, and not the original message being signed. 签名中的额外字节来自ASN.1编码,而不是签名的原始消息。 For example, here is a 39 byte signature generated with an ECDSA key with curve secp128r1: 例如,这是一个39字节的签名,使用带有曲线secp128r1的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

The 30 indicates that a Sequence follows. 30表示序列如下。 The 25 tells you that the Sequence is 0x25 bytes long. 25告诉你序列长度为0x25字节。 The 02 indicates that the first item in the Sequence is an Integer. 02表示序列中的第一项是整数。 The 10 tells you that the first Integer is 0x10 bytes long. 10告诉你第一个Integer是0x10字节长。 The following 0x10 (16) bytes are the "r" value of the ECDSA signature. 以下0x10(16)字节是ECDSA签名的“r”值。 Following the first integer is the byte 02. This tells you that the 2nd Integer of the Sequence is about to begin. 在第一个整数之后是字节02.这告诉你序列的第二个整数即将开始。 11 tells you that the next 0x11 (17) bytes make up the 2nd Integer, which is the "s" value of the ECDSA signature. 11告诉您下一个0x11(17)字节构成第二个整数,这是ECDSA签名的“s”值。 It's 11 bytes because the first byte of the Integer is 00. "00" is inserted whenever the first byte of an integer is >= 0x80. 它是11个字节,因为Integer的第一个字节是00.只要整数的第一个字节> = 0x80,就会插入“00”。 This is to avoid the most significant bit being a 1, which would indicate a negative integer. 这是为了避免最高有效位为1,这表示负整数。

So after all that, the real signature values are: 毕竟,真正的签名值是:

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

The "extra" bytes are for ASN.1 formatting. “额外”字节用于ASN.1格式化。

divb> Can I tell openssl to sign a message with NOT putting the message to the signature such that the resulting signature is 32 Byte exactly? divb>我可以告诉openssl签署一条消息,不将消息放入签名,以便生成的签名完全是32字节吗?

And

sandeep> is there any function in cryptopp which do this conversion? sandeep> cryptopp中有任何功能可以进行这种转换吗?

As @CodesInChaos stated, the issue is signature encoding. 正如@CodesInChaos所述,问题是签名编码。 Also see OpenSSL ECDSA signatures longer than expected . 另请参阅OpenSSL ECDSA签名超过预期

As @Sandeep suggested in a comment, another option is to have Crypto++ consume the OpenSSL signature. 正如@Sandeep在评论中建议的那样,另一种选择是让Crypto ++使用OpenSSL签名。

Here is a Crypto++ test program to convert from the ASN.1/DER output used by OpenSSL and Java, and convert it to IEEE P1363 used by Crypto++. 这是一个Crypto ++测试程序,用于转换OpenSSL和Java使用的ASN.1 / DER输出,并将其转换为Crypto ++使用的IEEE P1363。 It is mostly taken from Elliptic Curve Digital Signature Algorithm on the Crypto++ wiki. 它主要取自Crypto ++ wiki上的椭圆曲线数字签名算法

#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;
}

And here is the result of running the test program. 这是运行测试程序的结果。

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

Here is the setup I used to reproduce cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > test.sig 这是我用来重现cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > test.sig cat test.txt | openssl dgst -ecdsa-with-SHA1 -sign sample.key -keyform DER > test.sig . 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
       :   }

It is possible to use the PEM encoded key rather than the ASN.1/DER encoded key in Crypto++. 可以在Crypto ++中使用PEM编码密钥而不是ASN.1 / DER编码密钥。 To do it you will need the PEM Pack . 要做到这一点,你需要PEM包 It is a community add-on, and not part of the library proper. 它是一个社区附加组件,而不是图书馆的一部分。

If you add the PEM Pack to the library, then you need to rebuild the library. 如果将PEM包添加到库中,则需要重建库。 Or, you can build it as part of your program. 或者,您可以将其构建为程序的一部分。

First of all you should be aware that EC of 128bit provides about 64bit of security. 首先,您应该知道128位的EC提供大约64位的安全性。 Second I don't think that this is the message openssl adds, as 5 bytes are not enough for that. 其次我不认为这是openssl添加的消息,因为5个字节不足以满足要求。 Anyway you can use head or tail to remove the extra bytes. 无论如何,您可以使用head或tail来删除额外的字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM