JavaRush /Java Blog /Random EN /From HTTP to HTTPS
Viacheslav
Level 3

From HTTP to HTTPS

Published in the Random EN group
From HTTP to HTTPS - 1
Content:

Introduction

In the modern world, you can’t live without web applications. And we'll start with a small experiment. As a child, I remember how all the stalls sold such a newspaper as “Arguments and Facts”. I remembered them because, according to my personal perception from childhood, these newspapers always looked strange. And I decided whether we should go to their website:
From HTTP to HTTPS - 2
If we go to the Google Chrome help, we will read that this site does not use a secure connection and the information you exchange with the site may be accessible to third parties. Let's check some other news, for example St. Petersburg news from Fontanka, an electronic media:
From HTTP to HTTPS - 3
As you can see, the Fontanka website has no security problems according to these data. It turns out that web resources may or may not be safe. We also see that access to unprotected resources occurs via the HTTP protocol. And if the resource is protected, then data exchange is carried out using the HTTPS protocol, where the S at the end means “Secure”. The HTTPS protocol is described in the rfc2818 specification: " HTTP Over TLS ". Let's try to create our own web application and see for ourselves how it works. And along the way we will understand the terms.
From HTTP to HTTPS - 4

Web application in Java

So, we need to create a very simple web application in Java. First, we need the Java application itself. To do this, we will use the Gradle project’s automatic build system. This will allow us not to manually create the necessary directory structure + Gradle will manage all the libraries necessary for the project for us and ensure that they are available when executing the code. You can read more about Gradle in a short review: " A Brief Introduction to Gradle ". Let's use Gradle Init Plugin and run the command:
gradle init --type java-application
After this, let’s open the build script build.gradle, which describes what libraries our project consists of, which Gradle will provide to us. Let's add there a dependency on the web server on which we will experiment:
dependencies {
    // Web server
    implementation 'io.undertow:undertow-core:2.0.20.Final'
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
}
For a web application to work, we definitely need a web server where our application will be hosted. There are a huge variety of web servers, but the main ones are: Tomcat, Jetty, Undertow. This time we will choose Undertow. To understand how we can work with this web server of ours, let’s go to the official Undertow website and go to the documentation section . You and I have connected a dependency on Undertow Core, so we are interested in the section about this very Core , that is, the core, the basis of the web server. The easiest way is to use the Builder API for 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();
}
If we execute the code, we can navigate to the following web resource:
From HTTP to HTTPS - 5
It works simply. Thanks to the Undertow Builder API, we add an HTTP listener to localhost and port 8080. This listener receives requests from the web browser and returns the string "Hello World" in response. Great web application. But as we see, we use the HTTP protocol, i.e. This type of data exchange is not secure. Let's figure out how exchanges are carried out using the HTTPS protocol.
From HTTP to HTTPS - 6

Requirements for HTTPS

To understand how to enable HTTPS, let's return to the HTTPS specification: " RFC-2818: HTTP Over TLS ". According to the specification, data in the HTTPS protocol is transmitted over the cryptographic protocols SSL or TLS. People are often misled by the concept of SSL and TLS. In fact, SSL has evolved and changed its versions. Later, TLS became the next step in the development of the SSL protocol. That is, TLS is simply a new version of SSL. The specification says so: “SSL, and its successor TLS”. So, we learned that there are SSL/TLS cryptographic protocols. SSL is an abbreviation for Secure Sockets Layer and translates as “secure socket layer”. Socket translated from English is a connector. Participants in data transmission over a network use sockets as a programming interface (that is, an API) to communicate with each other over the network. The browser acts as a client and uses a client socket, and the server that receives a request and issues a response uses a server socket. And it is between these sockets that data exchange occurs. That's why the protocol was originally called SSL. But time passed and the protocol evolved. And at one point, the SSL protocol became the TLS protocol. TLS is short for Transport Layer Security. The TLS protocol, in turn, is based on the SSL protocol specification version 3.0. The TLS protocol is the topic of separate articles and reviews, so I will simply indicate materials that I find interesting: In short, the basis of HTTPS is the TLS handshake and the “Server Identity” check (i.e., server identification) using its digital certificate. It is important. Let's remember this, because... We will return to this fact later. So, earlier we used HttpListener to tell the server how to operate over the HTTP protocol. If in the example above we added an HttpListener to work over HTTP, then to work over HTTPS we need to add an HttpsListener:
From HTTP to HTTPS - 7
But to add it we need SSLContext. Interestingly, SSLContext is not a class from Undertow, but javax.net.ssl.SSLContext. The SSLContext class is part of the so-called " Java Secure Socket Extension " (JSSE) - a Java extension for ensuring Internet connection security. This extension is described in the " Java Secure Socket Extension (JSSE) Reference Guide ". As you can see from the introductory part of the documentation, JSSE provides a framework and Java implementation of the SSL and TLS protocols. How do we get the SSLContext? Open the JavaDoc SSLContext and find the getInstance method . As you can see, to get the SSLContext we need to specify the name "Secure Socket Protocol". The description of the parameters indicates that these names can be found in the " Java Cryptography Architecture Standard Algorithm Name Documentation ". Therefore, let's follow the instructions and go to the documentation. And we see that we can choose between SSL and TLS:
From HTTP to HTTPS - 8
Now we understand that we need to create the SSLContext as follows:
public SSLContext getSSLContext() {
	// 1. Получаем контекст, в рамках которого будем работать по TLS протоколу
	SSLContext context = null;
	try {
		context = SSLContext.getInstance("TLS");
	} catch (NoSuchAlgorithmException e) {
		throw new IllegalStateException(e);
	}
	return context;
}
Having created a new context, we remember that SSLContext was described in the " Java Secure Socket Extension (JSSE) Reference Guide ". We read and see that “A newly created SSLContext should be initialized by calling the init method”. That is, creating a context is not enough. It needs to be initialized. And this is logical, because about security, we only told you that we want to use the TLS protocol. To initialize the SSLContext we need to provide three things: KeyManager, TrustManager, SecureRandom.
From HTTP to HTTPS - 9

KeyManager

KeyManager is a key manager. He is responsible for what “authentication credential” to provide to someone who contacts us. Credential can be translated as identity. The identity is needed so that the client is sure that the server is who he claims to be and can be trusted. What will be used as identification? As we remember, Server Identity is verified by the server’s digital certificate. This process can be represented as follows:
From HTTP to HTTPS - 10
Additionally, the " JSSE Reference Guide: How SSL Works " says that SSL uses "asymmetric cryptography", which means that we need a key pair: a public key and a private key. Since we are talking about cryptography, the “Java Cryptography Architecture” (JCA) comes into play. Oracle provides an excellent document on this architecture: " Java Cryptography Architecture (JCA) Reference Guide ". In addition, you can read a brief overview of JCA on JavaRush: " Java Cryptography Architecture: First acquaintance ." So, to initialize the KeyManager, we need a KeyStore, which will store our server’s certificate. The most common way to create a key and certificate store is the keytool utility, which is included with the JDK. An example can be seen in the JSSE documentation: " Creating a Keystore to Use with JSSE ". So, we need to use the KeyTool utility to create a key store and write the certificate there. Interestingly, key generation was previously specified using -genkey, but now it is recommended to use -genkeypair. We will need to define the following things:
  • alias : Alias ​​or simply the name under which the entry will be saved in Keystore
  • keyalg : Key encryption algorithm. Let's choose the RSA algorithm, which is essentially a standard solution for our purpose.
  • keysize : Key size (in bits). The minimum recommended size is 2048, because... a smaller size has already been cracked. You can read more here: " a ssl certificate in 2048 bit ".
  • dname : Distinguished Name, distinguished name.
It is important to understand that the requested resource (for example, https://localhost) will be compared against it. This is called "subject cn matching".
  • validity : Duration in days during which the generated certificate is valid, i.e. valid.
  • ext : Certificate Extension specified in " Named Extensions ".
For Self-signed Certificates (i.e., for certificates created independently), you must specify the following extensions:
  • -ext san:critical=dns:localhost,ip:127.0.0.1 > to perform subject matching by SubjectAlternativeName
  • -ext bc=ca:false > to indicate that this certificate is not used to sign other certificates
Let's run the command (example for Windows OS):
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
Because the file will be created, make sure that you have all rights to create the file. You'll also likely see advice like this:
From HTTP to HTTPS - 11
Here we are told that JKS is a proprietary format. Proprietary means it is the private property of the authors and is intended for use only in Java. When working with third-party utilities, a conflict may arise, which is why we are warned. In addition, we may receive the error: The destination pkcs12 keystore has different storepass and keypass. This error occurs because the passwords for the entry in the Keystore and for the keystore itself are different. As the keytool documentation says , "For example, most third-party tools require storepass and keypass in a PKCS #12 keystore to be the same." We can specify the key ourselves (for example, -destkeypass entrypassw). But it’s better not to violate the requirements and set the same password. So the import might look like this:
keytool -importkeystore -srckeystore C:/keystore.jks -destkeystore C:/keystore.jks -deststoretype pkcs12
Success example:
From HTTP to HTTPS - 12
To export the certificate to a file, you can run:
keytool -export -alias ssl -storepass passw0rd -file C:/server.cer -keystore C:/keystore.jks
Additionally, we can get the Keystore contents like this:
keytool -list -v -keystore C:/keystore.jks -storepass passw0rd
Great, now we have a keystore that contains the certificate. Now you can get it from code:
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);
	}
}
If there is a KeyStore, then we can initialize the 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);
	}
}
Our first goal has been achieved. It remains to figure out what TrustManager is. TrustManager is described in the JSSE documentation in the section " The TrustManager Interface ". It is very similar to KeyManager, but its purpose is to check whether the person requesting the connection can be trusted. To put it bluntly, this is the KeyManager in reverse =) We don’t need TrustManager, so we’ll pass null. A default TrustManager will then be created that does not verify the end user making requests to our server. The documentation says so: "default implementation will be used". Same with SecureRandom. If we specify null, the default implementation will be used. Let's just remember that SecureRandom is a JCA class and is described in the JCA documentation in the section " The SecureRandom Class ". In total, preparation taking into account all the methods described above may look like this:
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);
	}
All that remains is to start the 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();
}
This time our server will be available at the following address. https://localhost:443 However, we will still receive an error that this resource cannot be trusted:
From HTTP to HTTPS - 13
Let's figure out what's wrong with the certificate and what to do about it.
From HTTP to HTTPS - 14

Certificate management

So, our server is already ready to work via HTTPS, but the client does not trust it. Why? Let's get a look:
From HTTP to HTTPS - 15
The reason is that this certificate is a Self-signed Certificate. A self-signed SSL certificate refers to a public key certificate issued and signed by the same person it identifies. That is, it was not issued by any respected certification authority (CA, also known as Certificate Authority). The Certificate Authority acts as a trustee and is similar to a notary in everyday life. He assures that the certificates he issues are reliable. The service of issuing certificates by such CAs is paid, so no one needs loss of trust and reputational risks. By default, there are several certificate authorities that are trusted. This list is editable. And each operating system has its own management of the list of certification authorities. For example, managing this list in Windows can be read here: " Manage Trusted Root Certificates in Windows ". Let's add the certificate to the trusted ones as indicated in the error message. To do this, first, download the certificate:
From HTTP to HTTPS - 16
In OS Windows, press Win+R and execute mmcto call the control console. Next, press Ctrl+M to add the “Certificates” section to the current console. Next, in the subsection "Trusted Root Certification Authorities" we will execute Действия / Все задачи / Импорт. Let's import the file that was downloaded earlier into the file. The browser may have remembered the past trust state of the certificate. Therefore, before opening the page you need to restart the browser. For example, in Google Chrome in the address bar you need to run chrome://restart. In OS Windows, you can also use the utility to view certificates certmgr.msc:
From HTTP to HTTPS - 17
If we did everything correctly, we will see a successful call to our server via HTTPS:
From HTTP to HTTPS - 18
As you can see, the certificate is now considered valid, the resource is available, and there are no errors.
From HTTP to HTTPS - 19

Bottom line

So we have figured out what the scheme for enabling the HTTPS protocol on a web server looks like and what is needed for this. I hope at this point it is clear that support is provided by the interaction of the Java Cryptography Architecture (JCA), which is responsible for cryptography, and the Java Secure Socket Extension (JSSE), which provides the TLS implementation on the Java side. We saw how the keytool utility included in the JDK is used to work with the KeyStore key and certificate store. Additionally, we realized that HTTPS uses SSL/TLS protocols for security. To reinforce this, I advise you to read excellent articles on this topic: Hopefully, after this little review, HTTPS will become a little more transparent. And if you need to enable HTTPS, you can easily understand the terms from the documentation of your application servers and frameworks. #Viacheslav
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION