Ich versuche SHA256-Signatur und Verifikation in Delphi mit OpenSSL Libeay32.dll zu implementieren. Dazu in einem ersten Schritt habe ich ein RSA 2048-Bit-Schlüssel-Paar mit der die folgenden OpenSSL-Befehle:Überprüfung der SHA256-Signatur mit OpenSSL in Delphi schlägt fehl
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
So weit so einfach. Der nächste Schritt, den ich tat, war die Schaffung einer Funktion, die die öffentlichen und privaten Schlüssel von den PEM-Dateien lesen konnte:
function TSignSHA256.ReadKeyFile(aFileName : String; aType : TKeyFileType) : pEVP_PKEY;
var locFile : RawByteString;
locBIO : pBIO;
begin
locFile := UTF8Encode(aFileName);
locBIO := BIO_new(BIO_s_file());
try
BIO_read_filename(locBIO, PAnsiChar(locFile));
result := NIL;
case aType of
kfPrivate : result := PEM_read_bio_PrivateKey(locBIO, result, nil, nil);
kfPublic : result := PEM_read_bio_PUBKEY(locBIO, result, nil, nil);
end;
finally
BIO_free(locBIO);
end;
end;
Das schien so gut zu funktionieren. So implementiert ich ein Zeichen Verfahren:
procedure TSignSHA256.Sign;
var locData : RawByteString;
locKey : pEVP_PKEY;
locCtx : pEVP_MD_CTX;
locSHA256 : pEVP_MD;
locSize : Cardinal;
locStream : TBytesStream;
begin
locKey := ReadKeyFile('private.pem', kfPrivate);
locData := ReadMessage('message.txt');
locCtx := EVP_MD_CTX_create;
try
locSHA256 := EVP_sha256();
EVP_DigestSignInit(locCtx, NIL, locSHA256, NIL, locKey);
EVP_DigestSignUpdate(locCtx, PAnsiChar(locData), Length(locData));
EVP_DigestSignFinal(locCtx, NIL, locSize);
locStream := TBytesStream.Create;
try
locStream.SetSize(locSize);
EVP_DigestSignFinal(locCtx, PAnsiChar(locStream.Memory), locSize);
WriteSignature('message.sig', locStream.Bytes, locSize);
finally
FreeAndNIL(locStream);
end;
finally
EVP_MD_CTX_destroy(locCtx);
end;
end;
Wie die Prozedur sehen kann, wird eine Datei message.txt genannt Lesen, die Signatur zu berechnen und diese sig zu message.sig speichern.
openssl dgst -sha256 -verify public.pem -signature message.sig message.txt
So scheint es, wie meine Unterzeichnung Verfahren ist auch richtig funktioniert: Wenn ich die folgende OpenSSL-Befehl das Ergebnis ist Verifiziert OK laufen. So implementiert ich endlich ein Überprüfungsverfahren:
function TSignSHA256.Verify : Boolean;
var locData : RawByteString;
locSig : TArray<Byte>;
locKey : pEVP_PKEY;
locCtx : pEVP_MD_CTX;
locSHA256 : pEVP_MD;
locSize : Cardinal;
locStream : TBytesStream;
begin
locKey := ReadKeyFile('public.pem', kfPublic);
locData := ReadMessage('message.txt');
locSig := ReadSignature('message.sig');
locSize := Length(locSig);
locCtx := EVP_MD_CTX_create;
try
locSHA256 := EVP_sha256();
EVP_DigestVerifyInit(locCtx, NIL, EVP_sha256(), NIL, locKey); //Returns 1
EVP_DigestVerifyUpdate(locCtx, PAnsiChar(locData), Length(locData)); //Returns 1
locStream := TBytesStream.Create(locSig);
try
result := (EVP_DigestVerifyFinal(locCtx, PAnsiChar(locStream.Memory), locSize) = 1); //Returns false! WHY???
finally
FreeAndNIL(locStream);
end;
finally
EVP_MD_CTX_destroy(locCtx);
end;
end;
Wie man sehen kann ich diese Prozedur genau die gleiche Art und Weise umgesetzt, wie ich die Unterzeichnung Verfahren tat umzusetzen. Leider ist das Ergebnis false. Der Fehlercode von OpenSSL zurück ist
error04091077:lib(4):func(145):reason:(119)
, die in lib RSA zu einem Fehler übersetzt, funktionieren int_rsa_verify, Grund Falsche Signaturlänge. Ich habe Google gesucht, aber keine nützlichen Informationen zu diesem Fehler gefunden. Ich habe auch versucht, die OpenSSL-Quellen zu verstehen, aber ich bin nicht so tief in C und es scheint, es kann ewig dauern, bis ich es herausfinden kann.
Mein persönliches Gefühl ist, dass ich den öffentlichen Schlüssel falsch gelesen habe. Aber das ist nur ein Gefühl und ich habe keine Ahnung, wie ich es anders machen könnte. Meine zweite Vermutung wäre, dass ich etwas falsch gemacht habe, um den Kontext im Verifikationsverfahren zu initialisieren. Aber ich habe keine Ahnung, was das sein könnte.
Warum schlägt die Signaturprüfung fehl?
Sie vermissen Fehlerbehandlung, beginnt mit der Überprüfung, ob 'EVP_DigestVerifyInit' und' EVP_DigestVerifyUpdate' gelingt – Remko
See (Rückgabewerte überprüfen) [ EVP Signing and Verifying] (http://wiki.openssl.org/index.php/EVP_Signing_and_Verifying) im OpenSSL-Wiki. Es gibt Ihnen Beispiele, die out of the box funktionieren. – jww
@Remko: Ich habe nur die Fehlerbehandlung für die Lesbarkeit ausgelassen. EVP_DigestVerifyInit und EVP_DigistVerifyUpdate geben beide 1 zurück, was Erfolg bedeutet. Ich habe meinen Code bearbeitet, um das deutlicher zu machen. –