SysTLSClient, TLS client manager

List

Questa pagina fa parte del Manuale Programmazione IEC 61131-3. Vai all indice.

Transport Layer Security (TLS) ed il predecessore Secure Sockets Layer (SSL) sono protocolli crittografici che permettono una comunicazione sicura dalla sorgente al destinatario (end-to-end) su reti TCP/IP. L’uso di questi protocolli consente alle applicazioni di comunicare attraverso la rete prevenendo manomissione dei dati (tampering), falsificazione e intercettazione.

Utilizzando questo blocco funzione da eseguire in task Back, è possibile gestire la crittografia su qualsiasi comunicazione IP, occorre inserirlo tra il FB di gestione connessione (Collegando a IFile lo stream generato dal FB) ed il FB di gestione protocollo (Collegando OFile all’ingresso File del FB). In caso di errore esecuzione viene attivata per un loop di programma l’uscita Fault.

Su tutti i sistemi non Linux è supportata l’esecuzione di una sola istanza di questo FB.

Diagramma utilizzo FB SysTLSClient
Information Circle

Blocco funzione

CODESYS: Non disponibile

LogicLab: eLLabXUnified12Lib

Descrizione

SpyOn (BOOL) Se attivo permette di spiare il funzionamento del FB (Vedi articolo).
IFile (eFILEP) Stream di I/O in uscita dal FB di gestione comunicazione UDP/TCP.
Server (@STRING) Definizione server.
Mode (TLS_MODE) Definizione modo di connessione (Vedi definizione).
CipherSuites (@TLS_CHIPER_SUITE) Definizione array certificati supportati, specifiche iana (Vedi definizione).
Psk (@STRING) Definizione pre shared key, stringa di lunghezza massima 64 chars esadecimali.
PskIdentity (@STRING) Definizione identità della pre shared key.
CAVerify (TLS_CERT_MODE) Impostare il tipo di verifica da eseguire sui certificati (Vedi definizione).
CAFile (@STRING) Definizione cartella file certificati.
eNULL se non richiesta certificazione del server.
Fault (BOOL) Attivo per un loop di programma se errore gestione.
OFile (eFILEP) Stream di I/O da passare al FB di gestione protocollo. Verificando da debug se il parametro è valorizzato è possibile determinare se il server è stato autenticato.

Immagine FB SysTLSClient
CAFile, File certificati

Il protocollo TLS si compone di due parti, una per autenticazione del server ed una per cifratura connessione e dati scambiati, per l’autenticazione del server occorre disporre del certificato pubblico rilasciato ed autenticato da enti certificatori. Il certificato viene richiesto automaticamente dal browser quando ci connettiamo ad un sito.

Creazione file certificati da browser

Collegandoci ad un server HTTPS il browser ci permette di receperarne il suo certificato pubblico, per esempio in Chrome un click con il tasto destro del mouse sul lucchetto permette di visualizzare il certificato e salvarlo su disco.

Nella figura a lato si vede l’accesso al nostro sito, il certificato è rilasciato da DigiCert, per raggiungere il sito si passa da Cloudflare, quindi il browser ne ha scaricato il certificato. Con il tasto salva su disco è possibile salvare entrambi i certificati, scegliere formato Codificato Base 64 X.509 (.CER).

Per certificare il nostro server possiamo utilizzare uno dei due certificati, salvarlo in un file sul sistema il cui percorso và indicato nel parametro CAFile. Per la verifica dei certificati esistono diversi servizi on-line ad esempio Cert Logik.

Screenshot salvataggio file certificato

Come utilizzare i file certificato

Come abbiamo visto nel caso sia settato CAVerify:=TLS_CA_REQUIRED il FB per abilitare il passaggio dei dati deve validare il server, quindi è necessario che nel file indicato in CAFile vi siano tutti i certificati necessari. Dovendo connettersi a servers diversi dobbiamo indicare in CAFile di volta in volta il file contenente i relativi certificati. E’ possibile unire in un unico file tutti i certificati necessari, occorre però tenere presente che più sono i certificati, maggiore è il tempo necessario al FB per validare il server, sono possibili tempi dell’ordine di 5-10 secondi.

Esempi

Ogni 15 secondi viene eseguita la richiesta della pagina eLLabHTTPLib/HTTPClient/ST_HTTPClient.php dal sito demos.elsist.biz, passando in GET alla richiesta i parametri Dividend e Divisor. La pagina richiesta è uno script PHP che esegue la divisione tra i valori passati. E’ possibile testare il funzionamento dello script a questo indirizzo. Se lo script è attivo viene ritornata una pagina con: The result is: 50, nelle variabili Header e Page è possibile visualizzare in debug i valori ritornati dal server.

  • Viene definita la cipher suite da utilizzare. Lasciando a eNULL la definizione di TLSClient.CipherSuites sarà scelta automaticamente la suite migliore.
  • Non è attiva la certificazione del server. Per attivarla occorre salvare il file di certificato (Vedi indicazioni nel paragrafo precedente) sul disco e definire la cartella in TLSClient.CAFile , impostando TLSClient.CAVerify con il valore TLS_CA_REQUIRED.
LogicLab, (Ptp156 ST_HTTPSClient)
PROGRAM ST_HTTPSClient
VAR
    i : UDINT; (* Auxiliary variable *)
    CaseNr : USINT; (* Program case *)
    TimeBf : UDINT; (* Time buffer (mS) *)
    Errors : UDINT; (* Execution errors *)
    Header : STRING[ 256 ]; (* Header string *)
    Page : STRING[ 64 ]; (* Page string *)
    CSuites : ARRAY[0..2] OF TLS_CHIPER_SUITE;
    TCPClient : SysTCPClient; (* TCP client management *)
    TLSClient : SysTLSClient; (* TLS client *)
    HTTPRq : HTTPClient_v5; (* HTTP client *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_HTTPSClient"
// *****************************************************************************
// The program connects to a server om which is running a PHP script like:
//
// <?php echo "The result is: ".($_REQUEST["Dividend"]/$_REQUEST["Divisor"]); ?>
//
// The program sends the "Dividend" and "Divisor" values and receives back from
// the server the result. If the server answers correctly the following result
// is expected.
// Header: 'HTTP/1.1 200 OK...
// Page: 'The result is: 50'
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATIONS
    // -------------------------------------------------------------------------
    // Program initializations.

    IF (SysFirstLoop) THEN

        // Set TCPClient parameters.

        TCPClient.PeerAdd:=ADR('demos.elsist.biz'); //Peer address
        TCPClient.PeerPort:=443; //Peer port
        TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address
        TCPClient.LocalPort:=0; //Local port
        TCPClient.FlushTm:=50; //Flush time (mS)
        TCPClient.LifeTm:=20; //Life time (S)
        TCPClient.RxSize:=512; //Rx buffer size
        TCPClient.TxSize:=512; //Tx buffer size

        // Set TLSClient parameters.

        TLSClient.SpyOn:=TRUE; //Spy active
        TLSClient.Mode:=TLS_MODE_TCP; //TLS mode
        TLSClient.Server:=TCPClient.PeerAdd; //Server name

        //TLSClient.CipherSuites:=ADR(CSuites); //Cipher suites supported
        TLSClient.CipherSuites:=eNULL; //Cipher suites supported

        TLSClient.Psk:=eNULL; //Preshared key
        TLSClient.PskIdentity:=eNULL; //Preshared key identity

         // TLSClient.CAFile:=ADR('/home/pi/Certificates/elsist_biz.crt'); //CA file
        // TLSClient.CAFile:=ADR('D:/Certificates/elsist_biz.crt'); //CA file
        // TLSClient.CAVerify:=TLS_CA_REQUIRED; //Certificate verify type
        TLSClient.CAFile:=eNULL; //CA file
        TLSClient.CAVerify:=TLS_CA_NONE; //Certificate verify type

        // Set cipher suites.
        // Values array, must terminate with TLS_CS_NULL_WITH_NULL_NULL

        // CSuites[0]:=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
        // CSuites[1]:=TLS_CS_NULL_WITH_NULL_NULL;

        // Set HTTPClient parameters.

        HTTPRq.SpyOn:=TRUE; //Activate the spy
        HTTPRq.KeepAlive:=TRUE; //HTTP keep-alive
        HTTPRq.RMethod:=HTTP_REQUEST#HTTP_GET; //HTTP request method
        HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname
        HTTPRq.Page:=ADR('eLLabHTTPLib/HTTPClient/ST_HTTPClient.php'); //Web page
        HTTPRq.Request:=ADR('Dividend=500$26Divisor=10'); //Request string
        HTTPRq.Header:=eNULL; //HTTP header
        HTTPRq.DBSize:=512; //Data buffer size
        HTTPRq.Timeout:=T#10s; //Execution timeout

        // Set local variables.

        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
    END_IF;

    // -------------------------------------------------------------------------
    // MANAGE CONNECTION
    // -------------------------------------------------------------------------
    // Manage the connection.

    TCPClient(Connect:=HTTPRq.Connect); //TCP client management
    IF (TCPClient.Fault) THEN Errors:=Errors+1; CaseNr:=0; END_IF;

    TLSClient(IFile:=TCPClient.File); //TLS client management
    IF (TLSClient.Fault) THEN Errors:=Errors+1; CaseNr:=0; END_IF;

    HTTPRq(Enable:=TRUE, File:=TLSClient.OFile); //HTTP client
    IF (HTTPRq.Fault) THEN Errors:=Errors+1; CaseNr:=0; END_IF;

    // -------------------------------------------------------------------------
    // PROGRAM SEQUENCIES
    // -------------------------------------------------------------------------
    // Program sequencies.

    CASE (CaseNr) OF

        // ---------------------------------------------------------------------
        // Initialize the time to manage acquisition delay.

        0:
        HTTPRq.Send:=FALSE; //Send request
        TimeBf:=SysTimeGetMs(); //Time buffer (mS)
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // At every 5 seconds a query is executed.

        1:
        IF ((SysTimeGetMs()-TimeBf) < TO_UDINT(T#5s)) THEN RETURN; END_IF;
        i:=Sysmemset(ADR(Header), 0, SIZEOF(Header)); //Empty header string
        i:=Sysmemset(ADR(Page), 0, SIZEOF(Page)); //Empty page string
        HTTPRq.Send:=TRUE; //Send request
        CaseNr:=CaseNr+1; //Program case

        // ---------------------------------------------------------------------
        // Header/Page data are received from server by chunked. When a chunk
        // has been received DBChars returns its length.

        2:
        IF (HTTPRq.DBChars <> 0) THEN

            // Transfer the received chunk on the proper buffer.

            IF NOT(HTTPRq.HPSelector) THEN
                IF ((Sysstrlen(ADR(Header))+HTTPRq.DBChars) < SIZEOF(Header)) THEN
                    i:=Sysmemmove(ADR(Header)+Sysstrlen(ADR(Header)), HTTPRq.DBAddress, HTTPRq.DBChars);
                END_IF;
            ELSE
                IF ((Sysstrlen(ADR(Page))+HTTPRq.DBChars) < SIZEOF(Page)) THEN
                    i:=Sysmemmove(ADR(Page)+Sysstrlen(ADR(Page)), HTTPRq.DBAddress, HTTPRq.DBChars);
                END_IF;
            END_IF;
        END_IF;

        // On Done it's possible to test if page has been loaded.

        IF (HTTPRq.Done) THEN
            IF NOT(HTTPRq.PLoad) THEN Errors:=Errors+1; END_IF;
            CaseNr:=0; //Program case
        END_IF;
    END_CASE;

// [End of file]
Was this article helpful?