Sto tentando di implementare la firma e la verifica SHA256 in Delphi utilizzando OpenSSL libeay32.dll. Perciò in una prima fase ho creato un RSA 2048 bit coppia di chiavi utilizzando il seguente OpenSSL comandi:Verifica della firma SHA256 con OpenSSL in Delphi non riuscita
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Così lontano così facile. Il prossimo passo che ho fatto è stata la creazione di una funzione che è stato in grado di leggere le chiavi pubbliche e private dai file PEM:
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;
che sembrava funzionare così. Così ho implementato qualche procedura segno:
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;
Come si può vedere la procedura è la lettura di un file chiamato message.txt, calcolando la firma e la conservazione che in ordine a al message.sig. Se si esegue il seguente comando OpenSSL il risultato è Verificati OK:
openssl dgst -sha256 -verify public.pem -signature message.sig message.txt
Così sembra che la mia procedura di firma sta lavorando anche corretto. Così ho finalmente implementato una procedura di verifica:
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;
Come potete vedere ho implementato questa procedura esattamente allo stesso modo come ho fatto applicare la procedura di firma. Purtroppo il risultato di questo è false. Il codice di errore restituito da OpenSSL è
error04091077:lib(4):func(145):reason:(119)
Ciò si traduce in un errore nel lib RSA, funziona int_rsa_verify, ragione errata lunghezza firma. Ho cercato su Google ma non ho trovato alcuna informazione utile su quell'errore. Ho anche cercato di capire i sorgenti di OpenSSL, ma non sono così approfondito in C e sembra che possano volerci anni prima che riesca a capirlo.
La mia sensazione personale è che ho fatto qualcosa di sbagliato leggendo la chiave pubblica. Ma questo è solo un sentimento e non ho idea di come potrei farlo in un modo diverso. La mia seconda ipotesi sarebbe che ho sbagliato qualcosa inizializzando il contesto nella procedura di verifica. Ma non ho idea di cosa possa essere.
Perché la verifica della firma non riesce?
Ti manca la gestione degli errori, iniziare con il controllo se '' EVP_DigestVerifyInit' e EVP_DigestVerifyUpdate' successo (controllare i valori di ritorno) – Remko
See [ Firma e verifica EVP] (http://wiki.openssl.org/index.php/EVP_Signing_and_Verifying) sul wiki OpenSSL. Ti dà esempi che funzionano fuori dalla scatola. – jww
@Remko: ho appena lasciato la gestione degli errori per la leggibilità. EVP_DigestVerifyInit e EVP_DigistVerifyUpdate restituiscono entrambi 1 che significa successo. Ho modificato il mio codice per renderlo più chiaro. –