Signature ECDSA avec OpenSSL, vérification avec Crypto ++

Je crée une paire de clés ECDSA (secp128r1) dans mon application avec Crypto ++ de Wei Dai. La signature et la vérification fonctionnent comme prévu. Je n’ajoute pas le message lui-même à la signature pour minimiser la longueur de la signature (qui est exactement 32 octets).

Cependant, lorsque je crée la signature avec openssl:

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

OpenSSL place évidemment le message lui-même dans la signature, ce qui donne une signature plus grande (par exemple, 39 octets). Je peux vérifier la signature avec Crypto ++ si je définis CryptoPP::SignatureVerificationFilter::PUT_MESSAGE .

Puis-je dire à OpenSSL de signer un message en n’en mettant PAS le message à la signature, de sorte que la signature obtenue soit exactement 32 octets?

CodesInChaos est correct. Les octets supplémentaires dans la signature proviennent du codage ASN.1 et non du message original en cours de signature. Par exemple, voici une signature de 39 octets générée avec une clé ECDSA avec la courbe 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 

Le 30 indique qu’une séquence suit. Le 25 vous indique que la séquence a une longueur de 0x25 octets. Le 02 indique que le premier élément de la séquence est un entier. Le 10 vous indique que le premier entier est long de 0x10 octets. Les octets 0x10 (16) suivants sont la valeur “r” de la signature ECDSA. L’octet 02 suit le premier entier. Il vous indique que le deuxième entier de la séquence est sur le sharepoint commencer. 11 vous indique que les octets 0x11 (17) suivants constituent le deuxième entier, qui est la valeur “s” de la signature ECDSA. Il s’agit de 11 octets, car le premier octet de l’entier est 00. “00” est inséré chaque fois que le premier octet d’un entier est> = 0x80. Cela évite que le bit le plus significatif soit un 1, ce qui indiquerait un entier négatif.

Donc après tout cela, les vraies valeurs de signature sont:

 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 

Les “extra” octets sont pour le formatage ASN.1.

divb> Puis-je dire à openssl de signer un message en n’en mettant PAS le message à la signature de sorte que la signature résultante soit exactement 32 octets?

Et

sandeep> y at-il une fonction dans cryptopp qui effectue cette conversion?

Comme @CodesInChaos l’a déclaré, le problème est le codage de signature. Consultez également les signatures OpenSSL ECDSA plus longtemps que prévu .

Comme @Sandeep l’a suggéré dans un commentaire, une autre option consiste à faire en sorte que Crypto ++ utilise la signature OpenSSL.

Voici un programme de test Crypto ++ permettant de convertir la sortie ASN.1 / DER utilisée par OpenSSL et Java et de la convertir au format IEEE P1363 utilisé par Crypto ++. Il provient principalement de l’ algorithme de signature numérique à courbe elliptique du wiki Crypto ++.

 #include "cryptlib.h" #include "eccrypto.h" #include "files.h" #include "oids.h" #include "dsa.h" #include "sha.h" #include "hex.h" #include  using namespace CryptoPP; int main(int argc, char* argv[]) { // Load DER encoded public key FileSource pubKey("secp256k1-pub.der", true /*binary*/); ECDSA::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; } 

Et voici le résultat de l'exécution du programme de test.

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

Voici la configuration utilisée pour reproduire 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 |cJ. 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 : } 

Il est possible d’utiliser la clé codée PEM plutôt que la clé codée ASN.1 / DER en Crypto ++. Pour ce faire, vous aurez besoin du pack PEM . Il s’agit d’une extension de la communauté et ne fait pas partie de la bibliothèque proprement dite.

Si vous ajoutez le pack PEM à la bibliothèque, vous devez reconstruire la bibliothèque. Ou, vous pouvez le construire dans le cadre de votre programme.

Tout d’abord, sachez qu’EC de 128 bits fournit environ 64 bits de sécurité. Deuxièmement, je ne pense pas que ce soit le message ajouté par openssl, car 5 octets ne suffisent pas pour cela. Quoi qu’il en soit, vous pouvez utiliser head ou tail pour supprimer les octets supplémentaires.