Indy TLS Handshake



  • Hallo Leute,

    ich habe mal eine Frage zu den Indy Komponenten.
    Vielleicht kann mir einer sagen warum das TIdTCPClient SendCmd nicht funktioniert.
    Hier kommt folgende Fehlermeldung "no matching Member function for call SendCmd "
    Obwohl in der indy Doku der Member vorhanden?

    Mein Source:

    // Funktion für den TLS-Handshake mit Indy-Komponenten und Sec-WebSocket-Key
    void TForm11::performTLSHandshakeWithWebSocketKey(const UnicodeString& hostname, int port, const UnicodeString& webSocketKey)
    {
        TIdSSLIOHandlerSocketOpenSSL *sslIOHandler = new TIdSSLIOHandlerSocketOpenSSL(NULL);
        sslIOHandler->SSLOptions->Method = sslvTLSv1_2;
        sslIOHandler->SSLOptions->Mode = sslmClient;
    	sslIOHandler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
    
    	TIdTCPClient *tcpClient = new TIdTCPClient(NULL);
    	tcpClient->IOHandler = sslIOHandler;
    
    	// Setze die gewünschten Verbindungsparameter
    	tcpClient->Host = hostname;
    	tcpClient->Port = port;
    
    	try {
    		tcpClient->Connect();
    
    		// Erstelle die HTTP-Anfrage
    		TIdHTTPRequest *request = new TIdHTTPRequest(NULL);
    		request->Method = "GET";
    //		request->ProtocolVersion = "HTTP/1.1";
    		request->Host = hostname;
    		request->ContentType = "application/json";
    		request->CustomHeaders->Values["Sec-WebSocket-Key"] = webSocketKey;
    
    		// Sende die HTTP-Anfrage
    		TIdHTTPResponse *response = tcpClient->SendCmd(request); //..<=== Kein Member von TIdTCPClient ??
    
    		// Überprüfe die HTTP-Antwort
    //		if (response->ResponseCode != 101 || response->Headers->Values["Upgrade"] != "websocket") {
    		if (response->ResponseCode != 101) {
    			// Fehler beim Handshake
    			ShowMessage("Fehler beim Handshake");
    			delete response;
    			delete request;
    			tcpClient->Disconnect();
    			delete tcpClient;
    			delete sslIOHandler;
    			return;
    		}
    
    
            // TLS-Handshake erfolgreich abgeschlossen und WebSocket-Verbindung hergestellt
            // Hier können Daten über die gesicherte WebSocket-Verbindung gesendet und empfangen werden
    
            tcpClient->Disconnect();
    
    		//delete response;
    		delete request;
        } catch (const Exception &e) {
            // Fehler beim Handshake oder bei der Verbindung
            ShowMessage("Fehler: " + e.Message);
        }
    
        delete tcpClient;
        delete sslIOHandler;
    }
    


  • Soweit ich sehe, will TIdTCPClient::SendCmd einen String haben, gibt's für TIdHTTPRequest vielleicht eine ToString() Funktion oder was Ähnliches?



  • @DocShoe Ja, einen request->ToString scheint es zu geben.

    Wenn ich das ändere in:

    		// Sende die HTTP-Anfrage
    		UnicodeString req = request->ToString();
    		TIdHTTPResponse *response = tcpClient->SendCmd(req);
    

    bekomme ich einen anderen Fehler:
    [bcc32c Fehler] F_main.cpp(55): cannot initialize a variable of type 'Idhttp::TIdHTTPResponse *' with an rvalue of type 'short'

    OK kann nicht funktionieren da bei SendCmd ein Short zurück gegeben wird. Aber request->ToString()
    funktioniert auch nicht. 😞



  • Willst du nicht eher mit nem TIdHTTP (statt eines rohen tcp clients) arbeiten und den IOHandler auf ein TIdSSLIOHandlerSocketOpenSSL setzen?
    Ich glaube das ist zielführender. Genauer kann ich nicht direkt werden, weil ich die seit 10 Jahren nicht verwendet habe, ich würde das jetzt genauso zusammengoogeln.

    Grundsätzlich zu SendCmd
    tcpClient->LastCmdResult() gibt dir das Resultat als text, der Rückgabewert von SendCmd ist nur ein success/failure value.



  • @5cript Habe ich auch schon versucht.

    void TForm11::performTLSHandshakeWithWebSocketKey_idhttp(const UnicodeString& url, const UnicodeString& webSocketKey, UnicodeString& data)
    {
    	TIdSSLIOHandlerSocketOpenSSL* sslIOHandler = new TIdSSLIOHandlerSocketOpenSSL(NULL);
    	sslIOHandler->SSLOptions->Method = sslvTLSv1_2;
    	sslIOHandler->SSLOptions->Mode = sslmClient;
    	sslIOHandler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
    
    	TIdHTTP* http = new TIdHTTP(NULL);
    	http->IOHandler = sslIOHandler;
    
    	try {
    		// Setze den Sec-WebSocket-Key Header
    		http->Request->CustomHeaders->Values["Sec-WebSocket-Key"] = webSocketKey;
    		http->Request->CustomHeaders->Values["Port"] = 80;
    		http->Request->CustomHeaders->Values["Protocol"] = "HTTP/1.1";
    
    		// Führe den Handshake durch
    		http->Get(url);
    
    		// Der TLS-Handshake wurde erfolgreich abgeschlossen
    		// Hier können Daten über die gesicherte Verbindung gesendet und empfangen werden
    //		http->Post(url, data);
    
        } catch (const Exception &e) {
            // Fehler beim Handshake oder bei der Verbindung
            ShowMessage("Fehler: " + e.Message);
    	}
    
    	delete http;
    	delete sslIOHandler;
    }
    

    Wenn ich das sende kommt der Fehler "Unbekanntes Protokoll", warum weiß ich nicht, da ich das doch im Header mitgebe. Standard müsste die Komponente doch das HTTP Protokoll benutzen, wie ich gelesen habe.



  • Das Get müsste unnötig sein, ziemlich sicher würde der dort eine echte GET Anfrage machen.
    Der Handshake würde auch im POST gemacht werden.

    "Unbekanntes Protokoll" -> entweder hat

    • der Server deinen TLS optionen abgelehnt (Der Server kann natürlich sagen: "Diese Verschlüsselungsart will ich nicht benutzen oder kann ich gar nicht" oder
    • die exception übersetzt schon die HTTP antwort auf deinen "http->Request->CustomHeaders->Values["Protocol"] = "HTTP/1.1";" header? Den kann ich nicht kommentieren, weil der ist nicht standardisiert. Wäre unüblich die erste Zeile nochmal im Header zu wiederholen.
    • EDIT: Oder Indy sendet in der ersten Zeile ein HTTP/2 request oder macht was seltsames? das wäre aber schon seltsam. Kann ich aber nur raten.

    Der Request sieht ja so aus zb:

    POST /path HTTP/1.1
    Host: bla.com
    Content-Type: text/text
    Content-Length: 9
    
    BODY_HERE
    

    Es wäre vermutlich am besten, wenn du Wireshark verwenden würdest um dir anzuschaun was wirklich kommuniziert wird.
    (EDIT: ggf einmal unverschlüsselt, damit man den header sehen kann auch wenn das der server ablehnt)



  • @5cript Vielen Dank nochmal für deinen Tip.
    Ich habe es wie folgt gelöst:

    // Funktion für den TLS-Handshake mit Indy idhttp Komponenten und Sec-WebSocket-Key
    void TForm11::performTLSHandshakeWithWebSocketKey_idhttp(const UnicodeString &url, const UnicodeString &webSocketKey, UnicodeString &data)
    {
    	mem_status->Lines->Clear();
    	lbl_seckey->Caption = webSocketKey;
    
    	TIdSSLIOHandlerSocketOpenSSL *sslIOHandler = new TIdSSLIOHandlerSocketOpenSSL(NULL);
    	TIdHTTP *http = new TIdHTTP(NULL);
    	__try
    	{
    		sslIOHandler->SSLOptions->Method = sslvTLSv1_2;
    		sslIOHandler->SSLOptions->Mode = sslmClient;
    		sslIOHandler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
    
    		http->IOHandler = sslIOHandler;
    
    		try
    		{
    			//..Setze den Sec-WebSocket-Key Header
                http->Request->CustomHeaders->Values["Sec-WebSocket-Key"] = webSocketKey;
    
                http->Request->Host = url;
                http->Request->Username = "";
                http->Request->Password = "";
    
    			//..Führe den Handshake durch
    			http->Get(url);
    
    			UnicodeString s = http->ResponseText;
    			mem_status->Lines->Add("Handshake: " + s + "\n" + http->LastCmdResult->Text->Text);
    
    			//..Der TLS-Handshake wurde erfolgreich abgeschlossen
    			//..Hier können Daten über die gesicherte Verbindung gesendet und empfangen werden
    
    			TStringStream *str = new TStringStream();
    			__try
    			{
    				str->WriteString(data); //..<=== Hier JSON für Post rein, wird übergeben!
    				UnicodeString reststr = http->Post(url, str);
    				mem_status->Lines->Add(reststr + "\n" + http->LastCmdResult->Text->Text);
    			}
    			__finally
    			{
    				str->Free();
    			}
    		}
    		catch (const Exception &e)
    		{
    			//..Fehler beim Handshake oder bei der Verbindung
    			mem_status->Lines->Add("Fehler: " + e.Message);
    		}
    	}
        __finally
        {
            http->Free();
            sslIOHandler->Free();
        }
    }
    
    
    
    //..Funktion zum Erzeugen eines Sec-WebSocket-Key
    UnicodeString TForm11::generateSecWebSocketKey()
    {
    	TByteDynArray keyBytes;
    	keyBytes.set_length(16);
    	for (int i = 0; i < 16; ++i)
    	{
    		keyBytes[i] = Random(256); //..Zufällige Byte-Werte zwischen 0 und 255
    	}
    	UnicodeString base64Key = TNetEncoding::Base64->EncodeBytesToString(&keyBytes[0], keyBytes.High);
    
    	return base64Key;
    }
    

    So scheint es zu funktionieren. 😀 👍🏼



  • Danke für eure Hilfe. 👍🏼


Anmelden zum Antworten