Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I create an ECDSA keypair (secp128r1) in my application with Wei Dai's Crypto++. 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).

However, when I create the signature with 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 (e.g. 39 Bytes). I can verify the signature with Crypto++ if I set CryptoPP::SignatureVerificationFilter::PUT_MESSAGE.

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?

Also see DSAConvertSignatureFormat on the Crypto++ wiki. It is a relatively new page. It was written after this question. – jww Sep 26, 2019 at 14:31

CodesInChaos is correct. The extra bytes in the signature are from the ASN.1 encoding, and not the original message being signed. For example, here is a 39 byte signature generated with an ECDSA key with curve 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

The 30 indicates that a Sequence follows. The 25 tells you that the Sequence is 0x25 bytes long. The 02 indicates that the first item in the Sequence is an Integer. The 10 tells you that the first Integer is 0x10 bytes long. The following 0x10 (16) bytes are the "r" value of the ECDSA signature. Following the first integer is the byte 02. This tells you that the 2nd Integer of the Sequence is about to begin. 11 tells you that the next 0x11 (17) bytes make up the 2nd Integer, which is the "s" value of the ECDSA signature. 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. This is to avoid the most significant bit being a 1, which would indicate a negative integer.

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.

@SandeepAggarwal - Yes, you use Crypto++ DSAConvertSignatureFormat to convert between ASN.1/DER, P1363 and OpenPGP formats. ASN.1/DER is kind of popular, and it is used by Java, OpenSSL and a few others. – jww Feb 16, 2018 at 23:02

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?

sandeep> is there any function in cryptopp which do this conversion?

As @CodesInChaos stated, the issue is signature encoding. Also see OpenSSL ECDSA signatures longer than expected.

As @Sandeep suggested in a comment, another option is to have Crypto++ consume the OpenSSL signature.

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++. It is mostly taken from Elliptic Curve Digital Signature Algorithm on the 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.
    SecByteBlock signature(verifier.SignatureLength());
    DSAConvertSignatureFormat(signature, signature.size(), DSA_P1363,
                          derSignature, sizeof(derSignature), DSA_DER);
    // Message "Attack at dawn!"
    const byte message[] = {
        0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x61,
        0x74, 0x20, 0x64, 0x61, 0x77, 0x6e, 0x21, 0x0a
    // https://www.cryptopp.com/wiki/Elliptic_Curve_Digital_Signature_Algorithm
    bool result = verifier.VerifyMessage(message, sizeof(message), signature, signature.size());
    if (result)
        std::cout << "Verified message" << std::endl;
        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
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++. To do it you will need the PEM Pack. 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. 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. Second I don't think that this is the message openssl adds, as 5 bytes are not enough for that. Anyway you can use head or tail to remove the extra bytes.

Security is not the big issue, I use it for activation/hardware locking of my software. Which "message openssl adds" are you referring to? When I create a signature I can choose to put the message or not (cryptopp.com/wiki/SignerFilter). If I choose to not do it, the signature gets smaller but I need to concatenate the signature and the message to verify it. openssl seems to put the message which results in a longer signature and incompatibility with my signature verification. Can I tell openssl dgst to NOT put the message? – divB Jun 26, 2013 at 21:11 "... you can use head or tail to remove the extra bytes..." - two of the bytes are in the middle of the stream. The two bytes are the ASN.1 type and length for the INTEGER of parameter s. – jww Feb 18, 2018 at 4:15

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.