JavaRush /Java Blog /Random-IT /Dall'HTTP all'HTTPS
Viacheslav
Livello 3

Dall'HTTP all'HTTPS

Pubblicato nel gruppo Random-IT
Dall'HTTP all'HTTPS - 1
Contenuto:

introduzione

Nel mondo moderno, non puoi vivere senza applicazioni web. E inizieremo con un piccolo esperimento. Da bambino, ricordo come tutte le bancarelle vendessero un giornale come "Argomenti e fatti". Li ricordavo perché, secondo la mia percezione personale fin dall'infanzia, questi giornali sembravano sempre strani. E ho deciso se dovremmo andare sul loro sito web:
Dall'HTTP all'HTTPS - 2
Se andiamo alla guida di Google Chrome, leggeremo che questo sito non utilizza una connessione sicura e le informazioni scambiate con il sito potrebbero essere accessibili a terzi. Diamo un'occhiata ad altre notizie, ad esempio quelle di San Pietroburgo da Fontanka, un media elettronico:
Da HTTP a HTTPS - 3
Come puoi vedere, il sito Fontanka non presenta problemi di sicurezza secondo questi dati. Si scopre che le risorse web possono essere sicure o meno. Vediamo anche che l'accesso alle risorse non protette avviene tramite il protocollo HTTP. E se la risorsa è protetta, lo scambio di dati viene effettuato utilizzando il protocollo HTTPS, dove la S alla fine significa "Sicuro". Il protocollo HTTPS è descritto nella specifica rfc2818: " HTTP Over TLS ". Proviamo a creare la nostra applicazione web e vediamo di persona come funziona. E lungo la strada ne capiremo i termini.
Da HTTP a HTTPS - 4

Applicazione Web in Java

Quindi, dobbiamo creare un'applicazione web molto semplice in Java. Innanzitutto, abbiamo bisogno dell'applicazione Java stessa. Per fare ciò, utilizzeremo il sistema di compilazione automatica del progetto Gradle. Questo ci consentirà di non creare manualmente la struttura di directory necessaria + Gradle gestirà per noi tutte le librerie necessarie per il progetto e garantirà che siano disponibili durante l'esecuzione del codice. Puoi leggere di più su Gradle in una breve recensione: " A Brief Introduction to Gradle ". Usiamo Gradle Init Plugin ed eseguiamo il comando:
gradle init --type java-application
Fatto questo apriamo lo script di build build.gradle, che descrive in quali librerie è composto il nostro progetto, che Gradle ci fornirà. Aggiungiamo lì una dipendenza dal server web su cui sperimenteremo:
dependencies {
    // Web server
    implementation 'io.undertow:undertow-core:2.0.20.Final'
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
}
Affinché un'applicazione Web funzioni, abbiamo sicuramente bisogno di un server Web su cui verrà ospitata la nostra applicazione. Esistono moltissimi server web, ma i principali sono: Tomcat, Jetty, Undertow. Questa volta sceglieremo Undertow. Per capire come possiamo lavorare con questo nostro server web, portiamoci sul sito ufficiale di Undertow e portiamoci nella sezione documentazione . Tu ed io abbiamo collegato una dipendenza da Undertow Core, quindi siamo interessati alla sezione su questo Core , cioè il core, la base del server web. Il modo più semplice è utilizzare l'API Builder per Undertow:
public static void main(String[] args) {
	Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
    server.start();
}
Se eseguiamo il codice, possiamo accedere alla seguente risorsa web:
Da HTTP a HTTPS - 5
Funziona semplicemente. Grazie all'API Undertow Builder, aggiungiamo un ascoltatore HTTP a localhost e alla porta 8080. Questo ascoltatore riceve richieste dal browser web e restituisce in risposta la stringa "Hello World". Ottima applicazione web. Ma come vediamo, utilizziamo il protocollo HTTP, ovvero Questo tipo di scambio di dati non è sicuro. Scopriamo come vengono effettuati gli scambi utilizzando il protocollo HTTPS.
Da HTTP a HTTPS - 6

Requisiti per HTTPS

Per capire come abilitare HTTPS, torniamo alla specifica HTTPS: " RFC-2818: HTTP Over TLS ". Secondo la specifica, i dati nel protocollo HTTPS vengono trasmessi tramite i protocolli crittografici SSL o TLS. Le persone vengono spesso fuorviate dal concetto di SSL e TLS. In effetti, SSL si è evoluto e ha cambiato le sue versioni. Successivamente, TLS è diventato il passo successivo nello sviluppo del protocollo SSL. Cioè, TLS è semplicemente una nuova versione di SSL. La specifica dice così: “SSL e il suo successore TLS”. Quindi, abbiamo appreso che esistono protocolli crittografici SSL/TLS. SSL è l'abbreviazione di Secure Sockets Layer e si traduce come "secure socket layer". Socket tradotto dall'inglese è un connettore. I partecipanti alla trasmissione dei dati su una rete utilizzano i socket come interfaccia di programmazione (ovvero un'API) per comunicare tra loro sulla rete. Il browser funge da client e utilizza un socket client, mentre il server che riceve una richiesta ed emette una risposta utilizza un socket server. Ed è tra queste prese che avviene lo scambio di dati. Ecco perché originariamente il protocollo si chiamava SSL. Ma il tempo è passato e il protocollo si è evoluto. E ad un certo punto, il protocollo SSL è diventato il protocollo TLS. TLS è l'abbreviazione di Transport Layer Security. Il protocollo TLS, a sua volta, si basa sulla specifica del protocollo SSL versione 3.0. Il protocollo TLS è oggetto di articoli e recensioni separati, quindi indicherò semplicemente i materiali che trovo interessanti: In breve, la base di HTTPS è l’handshake TLS e il controllo della “Server Identity” (cioè l’identificazione del server) tramite il suo certificato digitale. È importante. Ricordiamocelo perché... Torneremo su questo fatto più tardi. Quindi, in precedenza abbiamo utilizzato HttpListener per indicare al server come operare sul protocollo HTTP. Se nell'esempio sopra abbiamo aggiunto un HttpListener per funzionare su HTTP, per funzionare su HTTPS dobbiamo aggiungere un HttpsListener:
Da HTTP a HTTPS - 7
Ma per aggiungerlo abbiamo bisogno di SSLContext. È interessante notare che SSLContext non è una classe di Undertow, ma javax.net.ssl.SSLContext. La classe SSLContext fa parte della cosiddetta " Java Secure Socket Extension " (JSSE), un'estensione Java per garantire la sicurezza della connessione Internet. Questa estensione è descritta nella " Guida di riferimento Java Secure Socket Extension (JSSE) ". Come puoi vedere dalla parte introduttiva della documentazione, JSSE fornisce un framework e un'implementazione Java dei protocolli SSL e TLS. Come otteniamo SSLContext? Apri JavaDoc SSLContext e trova il metodo getInstance . Come puoi vedere, per ottenere SSLContext dobbiamo specificare il nome "Secure Socket Protocol". La descrizione dei parametri indica che questi nomi si trovano nella " Documentazione sui nomi degli algoritmi standard dell'architettura di crittografia Java ". Pertanto, seguiamo le istruzioni e andiamo alla documentazione. E vediamo che possiamo scegliere tra SSL e TLS:
Da HTTP a HTTPS - 8
Ora capiamo che dobbiamo creare SSLContext come segue:
public SSLContext getSSLContext() {
	// 1. Получаем контекст, в рамках которого будем работать по TLS протоколу
	SSLContext context = null;
	try {
		context = SSLContext.getInstance("TLS");
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	}
	return context;
}
Avendo creato un nuovo contesto, ricordiamo che SSLContext è stato descritto nella " Java Secure Socket Extension (JSSE) Reference Guide ". Leggiamo e vediamo che “Un SSLContext appena creato deve essere inizializzato chiamando il metodo init”. Cioè, creare un contesto non è sufficiente. È necessario inizializzarlo. E questo è logico, perché per quanto riguarda la sicurezza vi abbiamo solo detto che vogliamo utilizzare il protocollo TLS. Per inizializzare SSLContext dobbiamo fornire tre cose: KeyManager, TrustManager, SecureRandom.
Da HTTP a HTTPS - 9

Gestore delle chiavi

KeyManager è un gestore di chiavi. È responsabile di quali “credenziali di autenticazione” fornire a qualcuno che ci contatta. La credenziale può essere tradotta come identità. L'identità è necessaria affinché il client sia sicuro che il server sia chi dichiara di essere e che possa essere considerato attendibile. Cosa verrà utilizzato come identificazione? Come ricordiamo, l’identità del server viene verificata dal certificato digitale del server. Questo processo può essere rappresentato come segue:
Da HTTP a HTTPS - 10
Inoltre, la " Guida di riferimento JSSE: come funziona SSL " afferma che SSL utilizza la "crittografia asimmetrica", il che significa che abbiamo bisogno di una coppia di chiavi: una chiave pubblica e una chiave privata. Dato che parliamo di crittografia, entra in gioco la “Java Cryptography Architecture” (JCA). Oracle fornisce un eccellente documento su questa architettura: " Java Cryptography Architecture (JCA) Reference Guide ". Inoltre, puoi leggere una breve panoramica di JCA su JavaRush: " Architettura crittografica Java: prima conoscenza ". Quindi, per inizializzare il KeyManager, abbiamo bisogno di un KeyStore, che memorizzerà il certificato del nostro server. Il modo più comune per creare un archivio di chiavi e certificati è l'utilità keytool, inclusa con JDK. Un esempio può essere visto nella documentazione JSSE: " Creazione di un archivio di chiavi da utilizzare con JSSE ". Pertanto, dobbiamo utilizzare l'utilità KeyTool per creare un archivio chiavi e scrivere lì il certificato. È interessante notare che in precedenza la generazione delle chiavi veniva specificata utilizzando -genkey, ma ora si consiglia di utilizzare -genkeypair. Dovremo definire le seguenti cose:
  • alias : Alias ​​o semplicemente il nome con cui verrà salvata la voce nel Keystore
  • keyalg : algoritmo di crittografia della chiave. Scegliamo l'algoritmo RSA, che è essenzialmente una soluzione standard per il nostro scopo.
  • keysize : dimensione della chiave (in bit). La dimensione minima consigliata è 2048, perché... una dimensione più piccola è già stata crackata. Puoi leggere di più qui: " un certificato SSL a 2048 bit ".
  • dname : nome distinto, nome distinto.
È importante comprendere che la risorsa richiesta (ad esempio https://localhost) verrà confrontata con essa. Questo si chiama "corrispondenza soggetto cn".
  • validità : Durata in giorni durante i quali il certificato generato è valido, ovvero valido.
  • ext : estensione del certificato specificata in " Estensioni denominate ".
Per i certificati autofirmati (ovvero per i certificati creati in modo indipendente), è necessario specificare le seguenti estensioni:
  • -ext san:critical=dns:localhost,ip:127.0.0.1 > per eseguire la corrispondenza dell'oggetto in base a ObjectAlternativeName
  • -ext bc=ca:false > per indicare che questo certificato non viene utilizzato per firmare altri certificati
Eseguiamo il comando (esempio per sistema operativo Windows):
keytool -genkeypair -alias ssl -keyalg RSA -keysize 2048 -dname "CN=localhost,OU=IT,O=Javarush,L=SaintPetersburg,C=RU,email=contact@email.com" -validity 90 -keystore C:/keystore.jks -storepass passw0rd -keypass passw0rd -ext san:critical=dns:localhost,ip:127.0.0.1 -ext bc=ca:false
Perché il file verrà creato, assicurati di avere tutti i diritti per creare il file. Probabilmente vedrai anche consigli come questo:
Da HTTP a HTTPS - 11
Qui ci viene detto che JKS è un formato proprietario. Proprietario significa che è proprietà privata degli autori ed è destinato all'uso solo in Java. Quando si lavora con utilità di terze parti, potrebbe sorgere un conflitto, motivo per cui veniamo avvisati. Inoltre, potremmo ricevere l'errore: The destination pkcs12 keystore has different storepass and keypass. Questo errore si verifica perché le password per la voce nel keystore e per il keystore stesso sono diverse. Come dice la documentazione di keytool , "Ad esempio, la maggior parte degli strumenti di terze parti richiedono che storepass e keypass in un keystore PKCS #12 siano uguali." Possiamo specificare noi stessi la chiave (ad esempio, -destkeypass entrypassw). Ma è meglio non violare i requisiti e impostare la stessa password. Quindi l'importazione potrebbe assomigliare a questa:
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
Esempio di successo:
Da HTTP a HTTPS - 12
Per esportare il certificato in un file, è possibile eseguire:
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
Inoltre, possiamo ottenere i contenuti del Keystore in questo modo:
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
Ottimo, ora abbiamo un archivio chiavi che contiene il certificato. Ora puoi ottenerlo dal codice:
public KeyStore getKeyStore() {
	// Согласно https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore
	try(FileInputStream fis = new FileInputStream("C:/keystore.jks")){
		KeyStore keyStore = KeyStore.getInstance("pkcs12");
		keyStore.load(fis, "passw0rd".toCharArray());
		return keyStore;
	} catch (IOException ioe) {
		throw new IllegalStateException(ioe);
	} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
		throw new IllegalStateException(e);
	}
}
Se è presente un KeyStore, possiamo inizializzare il KeyManager:
public KeyManager[] getKeyManagers(KeyStore keyStore) {
	String keyManagerAlgo = KeyManagerFactory.getDefaultAlgorithm();
	KeyManagerFactory keyManagerFactory = null;
	try {
		keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgo);
		keyManagerFactory.init(keyStore, "passw0rd".toCharArray());
		return keyManagerFactory.getKeyManagers();
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	} catch (UnrecoverableKeyException | KeyStoreException e) {
		throw new IllegalStateException(e);
	}
}
Il nostro primo obiettivo è stato raggiunto. Resta da capire cos'è TrustManager. TrustManager è descritto nella documentazione JSSE nella sezione " L'interfaccia TrustManager ". È molto simile a KeyManager, ma il suo scopo è verificare se la persona che richiede la connessione è affidabile. Per dirla senza mezzi termini, questo è il KeyManager al contrario =) Non abbiamo bisogno di TrustManager, quindi passeremo null. Verrà quindi creato un TrustManager predefinito che non verifica l'utente finale che effettua richieste al nostro server. La documentazione dice così: "verrà utilizzata l'implementazione predefinita". Lo stesso con SecureRandom. Se specifichiamo null, verrà utilizzata l'implementazione predefinita. Ricordiamo solo che SecureRandom è una classe JCA ed è descritta nella documentazione JCA nella sezione " La classe SecureRandom ". In totale, la preparazione tenendo conto di tutti i metodi sopra descritti potrebbe assomigliare a questa:
public static void main(String[] args) {
	// 1. Подготавливаем приложение к работе по HTTPS
	App app = new App();
	SSLContext sslContext = app.getSSLContext();
	KeyStore keyStore = app.getKeyStore();
	KeyManager[] keyManagers = app.getKeyManagers(keyStore);
	try {
		sslContext.init(keyManagers, null, null);
	} catch (KeyManagementException e) {
		throw new IllegalStateException(e);
	}
Non resta che avviare il server:
// 2. Поднимаем server
 	int httpsPort = 443;
	Undertow server = Undertow.builder()
            .addHttpsListener(httpsPort, "localhost", sslContext)
            .setHandler(new HttpHandler() {
                @Override
                public void handleRequest(final HttpServerExchange exchange) throws Exception {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");
                    exchange.getResponseSender().send("Hello World");
                }
            }).build();
	server.start();
}
Questa volta il nostro server sarà disponibile al seguente indirizzo, https://localhost:443 tuttavia riceveremo comunque un errore indicante che questa risorsa non è attendibile:
Da HTTP a HTTPS - 13
Scopriamo cosa c'è che non va nel certificato e cosa fare al riguardo.
Da HTTP a HTTPS - 14

Gestione dei certificati

Quindi, il nostro server è già pronto per funzionare tramite HTTPS, ma il client non si fida di esso. Perché? Diamo un'occhiata:
Da HTTP a HTTPS - 15
Il motivo è che questo certificato è un certificato autofirmato. Un certificato SSL autofirmato si riferisce a un certificato a chiave pubblica emesso e firmato dalla stessa persona che identifica. Cioè, non è stato rilasciato da alcuna autorità di certificazione rispettata (CA, nota anche come Autorità di certificazione). L'autorità di certificazione agisce come fiduciario ed è simile a un notaio nella vita di tutti i giorni. Assicura che i certificati che rilascia sono affidabili. Il servizio di emissione di certificati da parte di tali CA è a pagamento, quindi nessuno ha bisogno di perdita di fiducia e rischi per la reputazione. Per impostazione predefinita, esistono diverse autorità di certificazione attendibili. Questo elenco è modificabile. E ogni sistema operativo ha la propria gestione dell'elenco delle autorità di certificazione. Ad esempio, la gestione di questo elenco in Windows può essere letta qui: " Gestisci i certificati radice attendibili in Windows ". Aggiungiamo il certificato a quelli attendibili come indicato nel messaggio di errore. Per fare ciò, scarica innanzitutto il certificato:
Da HTTP a HTTPS - 16
Nel sistema operativo Windows, premi Win+R ed esegui mmcper richiamare la console di controllo. Successivamente, premi Ctrl+M per aggiungere la sezione "Certificati" alla console corrente. Successivamente, nella sottosezione "Autorità di certificazione radice attendibili" eseguiremo Действия / Все задачи / Импорт. Importiamo il file scaricato in precedenza nel file. Il browser potrebbe aver ricordato lo stato di attendibilità passato del certificato. Pertanto, prima di aprire la pagina è necessario riavviare il browser. Ad esempio, in Google Chrome nella barra degli indirizzi devi eseguire chrome://restart. Nel sistema operativo Windows è inoltre possibile utilizzare l'utilità per visualizzare i certificati certmgr.msc:
Da HTTP a HTTPS - 17
Se abbiamo fatto tutto correttamente, vedremo una chiamata andata a buon fine al nostro server tramite HTTPS:
Da HTTP a HTTPS - 18
Come puoi vedere, il certificato è ora considerato valido, la risorsa è disponibile e non sono presenti errori.
Da HTTP a HTTPS - 19

Linea di fondo

Quindi abbiamo capito come appare lo schema per abilitare il protocollo HTTPS su un server web e cosa è necessario per questo. Spero che a questo punto sia chiaro che il supporto viene fornito dall'interazione tra Java Cryptography Architecture (JCA), che è responsabile della crittografia, e Java Secure Socket Extension (JSSE), che fornisce l'implementazione TLS sul lato Java. Abbiamo visto come viene utilizzata l'utilità keytool inclusa nel JDK per funzionare con la chiave KeyStore e l'archivio certificati. Inoltre, ci siamo resi conto che HTTPS utilizza i protocolli SSL/TLS per la sicurezza. Per rafforzare questo, ti consiglio di leggere ottimi articoli su questo argomento: Si spera che, dopo questa breve revisione, HTTPS diventi un po’ più trasparente. E se devi abilitare HTTPS, puoi facilmente comprendere i termini dalla documentazione dei tuoi server e framework delle applicazioni. #Viacheslav
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION