AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke CETP over TLS - TI-Konnektor

CETP over TLS - TI-Konnektor

Ein Thema von WladiD · begonnen am 22. Apr 2021 · letzter Beitrag vom 12. Jul 2022
Antwort Antwort
WladiD

Registriert seit: 27. Jan 2006
Ort: Celle
135 Beiträge
 
Delphi 10.4 Sydney
 
#1

CETP over TLS - TI-Konnektor

  Alt 22. Apr 2021, 11:29
Hallo zusammen,

ich komme hier schon seit Tagen einfach nicht weiter. Das Thema ist sehr speziell, aber vielleicht gibt es ja doch einen Leidensgenossen hier, der im TI-Medizinwesen unterwegs ist.

CETP ist die Abkürzung für Connector Event Transport Protocol und wird vom TI-Konnektor bereitgestellt. Die Funktionsweise ist eigentlich ganz simpel, das PVS (Praxisverwaltungssystem) abonniert über einen SOAP-Call die gewünschten Ereignisse und teilt in diesem mit auf welchem Port es einen TCP-Server bereitstellt. Der Konnektor sendet dann die Nachrichten wenn das entsprechende Ereignis eintritt (z.B. wenn eine Karte ins Kartenterminal gesteckt wird).

Die unverschlüsselte CETP-Variante in unserem PVS funktioniert von Anfang an problemlos. Ihr ahnt es schon…die TLS-Variante kriege ich einfach nicht. Als Netzwerkkomponente kommt hier die aktuellste Indy-Version unter Delphi 10.1 zum Einsatz.

Die Anforderung von der Gematik ist wie folgt:
---
GS-A_4384 - TLS-Verbindungen
Alle Produkttypen, die Übertragungen mittels TLS durchführen, MÜSSEN die folgenden
Vorgaben erfüllen:
• Zur Authentifizierung MUSS eine X.509-Identität gemäß [gemSpec_Krypt#GS-A_4359] verwendet werden.
• Als Cipher Suite MUSS TLS_DHE_RSA_WITH_AES_128_CBC_SHA oder TLS_DHE_RSA_WITH_AES_256_CBC_SHA verwendet werden.
• Es MUSS für die Schlüsselaushandlung Gruppe 14 (definiert in [RFC-3526], verwendbar bis Ende 2023) verwendet werden.
• Der private DH-Exponent für den Schlüsselaustausch MUSS eine Länge von mindestens 256 Bit haben
---

Hier der aktuelle Code:
Delphi-Quellcode:

procedure TCommunicator.StartListener(Topics: TCETPEvents);

  function CreateIOHandler: TIdServerIOHandlerSSLOpenSSL;
  begin
    Result := TIdServerIOHandlerSSLOpenSSL.Create(FCETPServer);

    if FClientAuthMode = camCertificate then
    begin
      Result.SSLOptions.CertFile := 'C:\Dev-master\exe\kiwi.crt.pem';
      Result.SSLOptions.KeyFile := 'C:\Dev-master\exe\kiwi.key.pem';
      Result.OnGetPassword := CertificatePassEventHandler;
    end;

    Result.SSLOptions.Mode := sslmServer;
    Result.SSLOptions.Method := sslvTLSv1_2;
    Result.SSLOptions.SSLVersions := [sslvTLSv1_2];
    Result.SSLOptions.CipherList := 'DHE-RSA-AES256-SHA'; // TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    Result.SSLOptions.DHParamsFile := 'C:\Dev-master\exe\dhparam.pem';
  end;

begin
  // Ausführung in Thread leiten
  if not InCurrentThread then
  begin
    FMethods.Add(
      procedure
      begin
        StartListener(Topics);
      end);
    Exit;
  end;

  if Assigned(FCETPServer) or (FCETPPort <= 0) then
    Exit;

  FCETPServer := TIdTCPServer.Create(nil);

  // Wenn es eine TLS-Pflicht gibt, dann muss auch CETP over TLS laufen
  if FTLSMandatory then
  begin
    FCETPServer.IOHandler := CreateIOHandler;
    FCETPServer.OnConnect := CETPServerConnect;
  end;

  FCETPServer.DefaultPort := FCETPPort;
  FCETPServer.ReuseSocket := rsOSDependent;
  FCETPServer.ListenQueue := 15;
  FCETPServer.UseNagle := True;
  FCETPServer.OnExecute := CETPResponseExecute;
  FCETPServer.Active := True;

  if Assigned(FOnStartListener) then
    Synchronize(
      procedure
      begin
        FOnStartListener(Self);
      end);

  if Topics = [] then
    SubscribeDefault
  else if Topics <> [ceUnknown] then
    SubscribeForCETPEvents(Topics);
end;

procedure TCommunicator.CertificatePassEventHandler(var Password: string);
begin
  Password := FClientAuthCertFilePass
end;

procedure TCommunicator.CETPServerConnect(AContext: TIdContext);
begin
  if FTLSMandatory and (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False;
   
  // An dieser Stelle wird eine Exception mit der Meldung 'Error accepting connection with SSL.' ausgelöst
end;
Mein großes Problem an der Sache, dass der Fehler von der OpenSSL-Funktion SSL_accept geworfen wird und ich somit überhaupt nicht nachvollziehen kann, an welcher Stelle genau und weshalb der Fehler auftritt. Die Meldung 'Error accepting connection with SSL.' ist überhaupt nicht aussagekräftig.
Waldemar Derr
Profil bei GitHub

Geändert von WladiD (22. Apr 2021 um 11:32 Uhr) Grund: Code an sslvTLSv1_2 angepasst
  Mit Zitat antworten Zitat
TurboMagic

Registriert seit: 28. Feb 2016
Ort: Nordost Baden-Württemberg
2.933 Beiträge
 
Delphi 12 Athens
 
#2

AW: CETP over TLS - TI-Konnektor

  Alt 22. Apr 2021, 12:13
Hallo,

falls du mit Indy nicht weiter kommst, könntest du alternativ mal ICS probieren:
http://www.overbyte.eu

Grüße
TurboMagic
  Mit Zitat antworten Zitat
WladiD

Registriert seit: 27. Jan 2006
Ort: Celle
135 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: CETP over TLS - TI-Konnektor

  Alt 22. Apr 2021, 12:24
Hi TurboMagic,

vielen Dank für den Vorschlag!

Ich habe von ICS schon mal was gehört, ist jedenfalls einen Versuch wert.

Wobei ich mir nicht sicher bin, ob Indy etwas falsch macht oder eher ich.
Waldemar Derr
Profil bei GitHub
  Mit Zitat antworten Zitat
WladiD

Registriert seit: 27. Jan 2006
Ort: Celle
135 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: CETP over TLS - TI-Konnektor

  Alt 12. Jul 2022, 10:08
Falls jemand mal auf dieses Thema kommt, wollte ich auch eine Lösung liefern, die mit Indy und OpenSSL doch nun funktioniert hat...

Delphi-Quellcode:
procedure TCetpServer.StartServer(Port: Integer);

  function CreateIOHandler: TIdServerIOHandlerSSLOpenSSL;
  begin
    Result := TIdServerIOHandlerSSLOpenSSL.Create(FServer);
    Result.SSLOptions.SSLVersions := [sslvTLSv1_2];
    Result.SSLOptions.CertFile := '<Eure-P12-Datei>';
    Result.SSLOptions.KeyFile := '<Eure-P12-Datei>';
    Result.SSLOptions.Mode := sslmServer;
    Result.SSLOptions.Method := sslvTLSv1_2;
    Result.SSLOptions.SSLVersions := [sslvTLSv1_2];
    Result.SSLOptions.VerifyMode := [];
    Result.OnGetPassword := YourPassEventHandler; // procedure(var Password: string);
  end;

var
  IoHandler: TIdServerIOHandlerSSLOpenSSL;
begin
  FServer := TIdTCPServer.Create(nil);
  IoHandler := CreateIOHandler;
  if Assigned(IoHandler) then
    FServer.IOHandler := IoHandler;
  FServer.DefaultPort := Port;
  FServer.ReuseSocket := rsOSDependent;
  FServer.ListenQueue := 15;
  FServer.UseNagle := True;
  FServer.OnAfterBind := CetpServerAfterBind;
  FServer.OnConnect := CetpServerConnect;
  FServer.OnExecute := CetpResponseExecute;
  FServer.Active := True;
end;

type
  TIdSSLContextRobin = class(TIdSSLContext);

procedure TCetpServer.CetpServerAfterBind(Sender: TObject);
begin
  if FServer.IOHandler is TIdServerIOHandlerSSLOpenSSL then
  begin
    // Das ist die entscheidende Zeile...
    //
    // Gefunden: <https://stackoverflow.com/questions/40454338/no-shared-cipher-at-ssl-accept-why>
    // "Some older OpenSSL versions require an explicit call to SSL_CTX_set_ecdh_auto() at
    // initialization stage to enable negotiation of advanced algorithms."
    SSL_CTX_set_ecdh_auto(
      TIdSSLContextRobin(TIdServerIOHandlerSSLOpenSSL(FServer.IOHandler).SSLContext).fContext, 1);
  end;
end;

procedure TCetpServer.CetpServerConnect(AContext: TIdContext);
begin
  // "PassThrough := False" ist bei TLS-Verbindungen notwendig
  if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then
    TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False;
end;

procedure TCetpServer.CetpResponseExecute(AContext: TIdContext);
const
  MaxContentLength = 1024 * 1024; // Maximal 1 MB
var
  CETPLine: string;
  ContentLength: Cardinal;
  IOHandler: TIdIOHandler;
  Header: TIdBytes;
  Content: TBytesStream;
begin
  Content := TBytesStream.Create;
  try
    IOHandler := AContext.Connection.IOHandler;

    SetLength(Header, 4);
    IOHandler.ReadBytes(Header, 4, False);
    CETPLine := TEncoding.ANSI.GetString(TBytes(Header));
    if CETPLine <> 'CETPthen
      raise ETICommunicator.Create('No CETP Header found');

    ContentLength := IOHandler.ReadUInt32(True);

    if ContentLength > MaxContentLength then
      raise ETICommunicator.CreateFmt(
        'Max content length of a CETP message must not exceed %d bytes', [MaxContentLength]);

    IOHandler.ReadStream(Content, ContentLength, False);
    Content.Position := 0;

    // Jetzt könnt ihr den Inhalt der CETP-Nachricht in Content verarbeiten...
  finally
    Content.Free;
  end;
end;
Die Code-Fragmente sind aus dem Produktiv-Code herausgeschnippelt, alles Wichtige für die Lösung des eingangs geschilderten Problems ist hier aber dabei.
Waldemar Derr
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:12 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz