现在,通过HTTPS(TLS)使用安全连接和更高效的HTTP/2协议应该是所有web应用程序的理所当然的事情。您可以从Let's Encrypt获取经过域验证的证书,而无需设置传输层安全性(TLS)。HTTP服务器和web浏览器已经广泛采用HTTP/2。从Java 9和Spring Boot 2/Spring 5开始,您可以轻松地让web应用程序使用安全的HTTPS/TLS连接和HTTP/2协议。
作为一名开发人员,大部分时间您都在本地环境中工作,不能在这里使用任何经过正式验证的TLS证书。相反,开发人员正在使用不安全连接或自签名证书,从而导致浏览器警告。通过设置私有证书颁发机构(CA),您将能够使用安全连接,而不会出现这些恼人的浏览器警告。
在接下来的部分中,您将逐步了解如何实现这一点。
HTTP/2协议
根据规范,您可以在两种变体中使用HTTP/2协议:
明文HTTP上的HTTP/2(h2c)
加密HTTPS上的HTTP/2(h2)
实际上,所有的web浏览器都只支持第二种变体HTTP/2而不是HTTPS(h2)。
因此,您必须遵循以下路径,使用带有TLS(传输层安全性)的HTTPS连接来保护您的web应用程序。
TLS公司
当你读到HTTPS的时候,你总是会偶然发现SSL或TLS这两个术语。
这通常是误解的开始:
HTTP安全(HTTPS)基本上是通过TLS连接的HTTP。
安全套接字层(SSL)是TLS的前身,因此不推荐使用,也不安全。所有SSL版本(1.0、2.0、3.0)都易受攻击,不应再使用。
传输层安全性(TLS)作为SSL的后继协议应该用于HTTPS。目前,TLS 1.3是与tls1.2一起使用的,它仍然与兼容性相关。
使用HTTPS连接提供以下保护层:
加密:所有交换的数据都是加密的,因此没有人可以通过“监听”连接来获取您的数据。
数据完整性:交换的数据在传输过程中不能被修改或损坏而不被检测到。
认证:证明您与预期的网站进行了通信。这就是为什么除了私钥/公钥加密之外还需要证书的原因
我们将按照以下步骤建立有效的TLS连接:
生成强私钥和公钥
创建证书签名请求(CSR)并将其发送到证书颁发机构(CA)
在webserver中安装CA提供的证书(即在spring boot应用程序的嵌入式tomcat中使用java密钥存储)。
在这篇博文中,我将关注当地的发展。因此,在步骤2中,您不会将CSR发送到正式CA。相反,我们将在下一节中设置我们的私有证书颁发机构。
设置私有证书颁发机构(CA)
系统要求
您需要以下软件来执行此博客文章的所有步骤:
首先,您至少需要一个Java 9 JDK或更新版本(JDK 11是下一个长期的替代方案)
此外,为了生成证书和执行签名请求,您需要作为JDK一部分的keytool。
通常,您使用自签名证书进行本地开发。但这些证书总是在web浏览器中生成警告,并将所有请求标记为不安全。
目前,web浏览器还为具有验证警告的TLS证书启用HTTP/2协议。但是作为一个安全意识强的开发人员,当你看到这样的警告时,你应该总是感到害怕。
因此,要消除此警告,您必须创建一个受web浏览器信任的证书。为此,我们必须建立自己的私有证书颁发机构(CA)。使用专用证书颁发机构,以后可以颁发根证书。
最后,可以将此根证书作为新的颁发机构导入到web浏览器中,并用它签署服务器证书。
根CA的证书
开始之前,请先创建以下子目录:
在第一步中,您需要为根CA生成私钥/公钥和相应的证书。稍后,您将在服务器证书的签名部分中使用此根证书。
1 |
keytool -genkeypair -keyalg RSA -keysize 3072 -alias root-ca -dname "CN=My Root CA,OU=Development,O=My Organization,C=DE" -ext BC:c=ca:true -ext KU=keyCertSign -validity 3650 -keystore ./root-ca/ca.jks -storepass secret -keypass secret |
This command creates a new java keystore ca.jks in folder root-ca containing the private and public keys. The certificate uses the RSA algorithm with a bit length of 3072 and is valid for 10 years. This includes also the distinguished name CN=My CA,OU=Development,O=My Organization,C=DE.
Now you export the certificate to file ca.pem in the subdirectory root-ca using this command:
1 |
keytool -exportcert -keystore ./root-ca/ca.jks -storepass secret -alias root-ca -rfc -file ./root-ca/ca.pem |
In the next step you create another new java key store file containing the private/public keys for the server certificate.
The private key is required to generate the certificate signing request. The CA uses the public key for validating the certificate signing request.
1 |
keytool -genkeypair -keyalg RSA -keysize 3072 -alias localhost -dname "CN=localhost,OU=Development,O=My Organization,C=DE" -ext BC:c=ca:false -ext EKU:c=serverAuth -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -validity 3650 -keystore ./server/server.jks -storepass secret -keypass secret |
You can find the new java key store server.jks in the subdirectory server. Again we use the RSA algorithm with a bit length of 3072 and set it valid for 10 years.
Now you will continue with the generation of the signing request for your server certificate. This creates the file server.csr in the subdirectory server.
1 |
keytool -certreq -keystore ./server/server.jks -storepass secret -alias localhost -keypass secret -file ./server/server.csr |
With the next command, you will now sign and export your server certificate using the file server.csr from the previous step.
1 |
keytool -gencert -keystore ./root-ca/ca.jks -storepass secret -infile ./server/server.csr -alias root-ca -keypass secret -ext BC:c=ca:false -ext EKU:c=serverAuth -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -validity 3650 -rfc -outfile ./server/server.pem |
To achieve the required valid chain of trust between the root ca and the signed server certificate you have to perform the following last step.
1 2 |
keytool -importcert -noprompt -keystore ./server/server.jks -storepass secret -alias root-ca -keypass secret -file ./root-ca/ca.pem keytool -importcert -noprompt -keystore ./server/server.jks -storepass secret -alias localhost -keypass secret -file ./server/server.pem |
This imports the certificate for the root ca and updates the existing (unsigned) server certificate with the signed one.
Finally, we have a java key store containing the full chain of certificates ready to be used in our spring boot application.
Using the Keytool to manually perform all steps for creating the certificates is good for learning. But if you want to automate these things for subsequent usages then mkcert is a great tool for that.
Let’s continue with enabling trust in your web browser for our private certificate authority.
We will use the chrome browser here to demonstrate this. Just open the settings in chrome, expand the “Advanced” section and then go to “Manage certificates“.
Here you import the root ca certificate from file ./root-ca/ca.pem into the browser as a new authority. Don’t forget to mark the first checkbox as shown in the following picture.
In an earlier blog post, I have described how easy you can create a new web application with basic security in just 5 minutes.
You follow the same steps by using start.spring.io, but this time we will use Kotlin instead of Java.
To get simple feedback when testing our simple application just add the following rest controller class DemoController to our new spring boot application.
This just prints out an “It works” in the browser when navigating to localhost:8080.
1 2 3 4 5 6 |
@RestController class DemoController {
@GetMapping("/") fun index() = "It works" } |
But still, we are using unsecured HTTP connections here. It is time for you to change this just now!
To enable TLS put the following entries into your application.properties file.
1 2 3 4 5 6 7 |
server.port=8443 server.ssl.enabled=true server.ssl.key-store=classpath:server.jks server.ssl.key-store-type=PKCS12 server.ssl.key-store-password=secret server.ssl.key-alias=server server.ssl.key-password=secret |
With these property entries you will change the following behavior:
Important: Please do not forget to copy the java key store file server.jks you have created in the previous section into the src/main/resource folder of the new spring boot application.
You can now switch on HTTP/2 by adding the following entry to application.properties.
1 |
server.http2.enabled=true |
Before we can start our application we need to tweak the security configuration a bit.
The reasons for manually configuring this are:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@Configuration class WebSecurityConfiguration : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) { http.headers().httpStrictTransportSecurity().disable() // (1) .and().httpBasic().and().formLogin() // (2) .and().authorizeRequests().anyRequest().authenticated() // (3) }
@Bean // (4) fun myUserDetails(): UserDetailsService = InMemoryUserDetailsManager ( User.withUsername("user") .password("secret") .passwordEncoder { passwordEncoder().encode(it) } .roles("USER") .build() )
@Bean fun passwordEncoder(): PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder() } |
Now start the spring boot application and after successful start direct your browser to URL https://localhost:8443.
Here you will notice the secure HTTPS connection shown as valid by the browser.
After providing the user credentials “user” and “secret” you will get the message “it works“.
That’s it for this tutorial on Spring Boot Apps with HTTP/2 and TLS.
You can grab the complete accompanying project code from my GitHub repository at https://github.com/andifalk/ssl-demo.