Einzelnen Beitrag anzeigen

dschiffler

Registriert seit: 25. Okt 2006
30 Beiträge
 
Delphi 10.4 Sydney
 
#28

AW: Indy & OpenSSL 1.1.1 & TLS 1.3

  Alt 28. Nov 2020, 12:36
Hallo mezen,

erst einmal auch von mir ein großes Danke schön und Respekt für deine Arbeit.
Hoffentlich fließt deine Arbeit irgenwann auch in die Indy-Version von Delphi ein.

Nun zu meinem Problem:
Ich habe eine Software die sich per SSL/TLS mit einem Webserver verbindet, den wir selbst in der Verwaltung haben. Auf dem Webserver ist ein Zertifikat installiert. Beim Verbindungsaufbau zum Webserver überprüft die Software den Fingerprint des Server-Zertifikats, um sicherzustellen, dass die Verbindung zum richtigen Webserver erfolgt.
Umgesetzt ist dies mit Indy 10 von Delphi 10.3 und funktioniert einwandfrei.

Hier der relevante Code-Ausschnitt dazu (FHttpClient ist eine Instanz von TIdHttp):
Delphi-Quellcode:
var
  lHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  FHttpClient.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(FHttpClient);

  lHandler := TIdSSLIOHandlerSocketOpenSSL(FHttpClient.IOHandler);
  lHandler.SSLOptions.Mode := sslmClient;
  lHandler.SSLOptions.VerifyMode := [sslvrfPeer];
  lHandler.OnVerifyPeer := DoVerifyPeer;
end;
Die Signatur von DoVerifyPeer ist folgende:
Code:
function DoVerifyPeer(ACertificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
Mit dem Parameter ACertificate bekommt man Zugriff auf die Zertifikat-Informationen/-Eigenschaften und ich kann in DoVerifyPeer dann folgendes machen:
Delphi-Quellcode:
var
  sFingerprint: string
begin
  sFingerprint := ACertificate.Fingerprints.SHA1AsString;
  Result := <Vergleich mit dem erwarteten Fingerprint>
end
Im Getter von SHA1AsString wird folgendes aufgerufen:
Code:
Result := MDAsString(SHA1);
In der Funktion MDAsString wird lediglich die Rückgabe von SHA1 als Hex-Zahlen mit Doppelpunkt getrennt formatiert (was nicht weiter wichtig ist), denn das Entscheidende passiert in der Funktion SHA1, nämlich der Aufuf von X509_digest, was ja eine Funktion aus den OpenSSL-DLLs ist:
Delphi-Quellcode:
begin
  X509_digest(FX509, EVP_sha1, PByte(@Result.MD), Result.Length);
end

Wenn ich jetzt deinen OpenSSL-Client benutze (damit ich auch TLS 1.3 unterstützen kann), sieht die Implementierung so aus:
Delphi-Quellcode:
var
  lHandler: TIdOpenSSLIOHandlerClient;
begin
  FHttpClient.IOHandler := TIdOpenSSLIOHandlerClient.Create(FHttpClient);

  lHandler := TIdOpenSSLIOHandlerClient(FHttpClient.IOHandler);
  lHandler.Options.CipherList := IdOpenSSLHeaders_ssl.SSL_DEFAULT_CIPHER_LIST;
  lHandler.Options.CipherSuites := IdOpenSSLHeaders_ssl.TLS_DEFAULT_CIPHERSUITES;
  lHandler.Options.UseServerCipherPreferences := False;
  lHandler.Options.VerifyServerCertificate := True;
  lHandler.Options.OnVerify := DoVerifyPeer;
end;
Die Signatur von DoVerifyPeer ist hier folgende:
Code:
procedure DoVerifyPeer(ASender: TObject; const ACertificate: TIdOpenSSLX509;
  const VerifyResult, Depth: Integer; var Accepted: Boolean);
Auch hier kann ich mit dem Parameter ACertificate auf die Zertifikat-Informationen/-Eigenschaften zugreifen und in DoVerifyPeer dann folgendes machen:
Delphi-Quellcode:
var
  sFingerprint: string
begin
  sFingerprint := ACertificate.ThumbprintAsSHA1;
  Accepted := <Vergleich mit dem erwarteten Fingerprint>
end
Im Getter von ThumbprintAsSHA1 wird folgndes aufgerufen:
Code:
Result := GetInternalThumbprint(EVP_sha1());
Das Entscheidende hier ist auch der Aufruf der Funktion X509_digest in der Methode GetInternalThumbprint:
Delphi-Quellcode:
var
  LBuffer: array[0 .. EVP_MAX_MD_SIZE -1] of Byte;
  LByteCount: TIdC_UINT;
  i: Integer;
begin
  Result := '';
  LByteCount := EVP_MAX_MD_SIZE;
  if X509_digest(FX509, md, @LBuffer, @LByteCount) = 1 then
  begin
    for i := 0 to LByteCount - 1 do
      Result := Result + ByteToHex(LBuffer[i]);
  end;
end;
Nach meinem Verständnis sollte doch hier die gleiche Bytefolge (also der Fingerprint des Server-Zertifikats) zurückkommen?
Das passiert aber nicht, denn die Bytefolge ist eine vollkommen andere.

Habe ich bei der Konfiguration des IO-Handlers etwas falsch gemacht oder vergessen oder wo liegt der Fehler?
Ich hoffe, dass der Fehler nicht in den OpenSSL-DLLs liegt?
Liebe Grüße
Dirk Schiffler
  Mit Zitat antworten Zitat