拿到SSL证书后, 如何才能解锁更多玩法?
如果你从上级或者其他人又或者证书提供商那里申请到ssl证书, 要如何才能解锁更多姿势呢?
(1) 直接配置在项目上面
(2) 配置在项目启动所依赖的tomcat上
(3) 配置在nginx上( 单向认证, 双向认证, 多域名认证)
… …
而这里则主要介绍了两种配置方式, 第一种是在项目中直接配置, 另一种就是通过nginx来进行配置
一. SpringBoots项目配置https访问接口(直接配置)SSL证书, 是基于SSL协议及相关加密算法生成的证书, 那么这个ssl协议又是什么呢?SSL协议 SSL协议既用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先使用非对称加密来对对称加密的密钥使用公钥,链路建立好之后,SSL对传输内容使用对称加密。
对称加密:速度高,可加密内容较大,用来加密会话过程中的消息非对称加密(公钥加密):加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥配置步骤首先可以选择直接在springboot项目上配置, 该配置优点是: 配置灵活, 无需安装nginx缺点也很明显, 就是只对单个项目生效, 如果服务器上还有其他项目仍需重复配置具体操作步骤如下
1. 获取证书/ 生成证书2. 放置证书 将证书放到项目的根目录下(便于后续的引用)
直接ctrl+c 复制证书, ctrl+v 到项目根目录 testone 下, 如下图
3. 配置项目 properties文件代码语言:javascript复制server.port=443
#你生成的证书名字
server.ssl.key-store=tomcat.keystore
#密钥库密码
server.ssl.key-store-password=12345678
server.ssl.keyStoreType=JKS
#如果是申请的证书, 则该别名一般是证书绑定的域名 eg:baidu.com
server.ssl.keyAlias:tomcat4. 在启动类中配置下面方法 这些方法的大致作用是监听当前http访问的端口重定向到https访问的端口(默认为443)
spingboot 2.x和springboot 1.x 有些差距, 下面将2.x和1.x单独分开展示
2.x书写代码语言:javascript复制// springboot2 写法
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
constraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
constraint.addCollection(collection);
context.addConstraint(constraint);
}
};
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}
@Bean
public Connector httpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
//监听http的端口号
connector.setPort(8080);
connector.setSecure(false);
//监听到http的端口号后转向到的https的端口号
System.out.println("监听到了80端口");
connector.setRedirectPort(443);//这里的端口写成和配置文件一样的端口就Ok
return connector;
}1.x书写方式代码语言:javascript复制 @Bean
public EmbeddedServletContainerFactory servletContainer(){
TomcatEmbeddedServletContainerFactory tomcat=new TomcatEmbeddedServletContainerFactory(){
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint=new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");//confidential
SecurityCollection collection=new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(httpConnector());
return tomcat;
}
@Bean
public Connector httpConnector(){
Connector connector=new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
} ps: 如果项目启动后, 仍出现 IOException: Alias name tomcat does not identify a key entry说明证书的别名(server.ssl.keyAlias)仍出现问题, 首先需要我们查看下证书的别名,
通过执行下面命令, 倒数第二行第一个字符串即为证书别名
代码语言:javascript复制[root@docker01 test]# keytool -keystore 证书名称(需要在证书所在的位置运行) -list
Enter keystore password:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
baidu.com, Mar 3, 2021, PrivateKeyEntry, # 注意这里第一个字符串就是正式别名
Certificate fingerprint (SHA1): D0:65:EB:AD:FB:66:A0:9E:94:0D:EC:D0:55:5F:1E:6A:59:FB:BD:84访问测试
代码语言:javascript复制# 通过https访问可以访问成功
https://localhost/test/123代码语言:javascript复制# 项目原本端口配置的是8080, 但在上面配置好证书以后, 如果通过http则会提示错误请求
http://localhost:8080/test/123二. 通过nginx配置证书(单向认证)单向认证流程https单向认证的流程大致如下:
客户端发送https连接请求, 并发送ssl协议相关信息, 服务器返回ssl协议信息以及公钥证书客户端校验公钥证书, 并提供可行加密方案给服务端, 服务端选择合适加密方案给客户端客户端按照指定加密方案加密一个随机码, 服务端根据证书私钥还原这个随机码作为后续通信中对称加密密钥双方正式建立连接, 通过该密钥加密传输的数据注意:
SSL协议信息主要包括: SSL协议版本号、加密算法种类、随机数等信息客户端校验公钥证书, 主要包括
• 证书是否过期
• 发行服务器证书的CA是否可靠
• 返回的公钥是否能正确解开返回证书中的数字签名
• 服务器证书上的域名是否和服务器的实际域名相匹配
• 通过后,将继续进行通信,否则,终止通信配置步骤1. 安装Nginx-1.16.1(开启https的ssl模块支持)代码语言:javascript复制# 安装依赖库
yum -y install gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel
# 下载安装包
wget http://nginx.org/download/nginx-1.16.1.tar.gz
# 解压文件
tar zxvf nginx-1.16.1.tar.gz
# 改名
mv nginx-1.16.1 nginx
# 移动到/usr/local下
rm nginx nginx-1.16.1.tar.gz -f
# 进入
cd /usr/local/nginx
# 配置ssl模块
./configure --user=nginx --prefix=/usr/local/nginx --with-http_ssl_module
# 安装配置
make & make install
# 创建 logs目录
mkdir logs
# 注意; 启动文件在 /objs 目录下
./nginx2. 生成自签证ssl证书 直接执行下面命令, 复制后直接全部执行, 但是需要注意自己nginx所在位置以及 localhost 所在位置(改为自己的ip或者域名)
代码语言:javascript复制openssl genrsa -out /usr/local/nginx/conf/server.key 2048
openssl req -new -key /usr/local/nginx/conf/server.key -out /usr/local/nginx/conf/server.csr< CN JS NJ Hrt OPS localhost EOF openssl x509 -req -days 36500 -in /usr/local/nginx/conf/server.csr -signkey /usr/local/nginx/conf/server.key -out /usr/local/nginx/conf/server.crt注意: CN,JS…相当与下图生成签证时填写的信息 3. 修改nginx.conf配置文件 需要根据注意事项来修改配置文件 注意 如果nginx的根目录下没有logs目录需要手动创建 mkdir logs验证配置中nginx路径是否和自己nginx所在路径一致ssl_certificate ,ssl_certificate_key 配置证书公钥和私钥所在地址配置文件最下面location / 的反向代理配置代码语言:javascript复制user nginx nginx; #下面两行按照实际配置 #worker_processes 8; #worker_cpu_affinity 0001 0010 0100 1000 0001 0010 0100 1000; pid logs/nginx.pid; worker_rlimit_nofile 204800; events { worker_connections 204800; use epoll; } http { include mime.types; default_type application/octet-stream; log_format main '{"@timestamp":"$time_iso8601",' '"@source":"$server_addr",' '"hostname":"$hostname",' '"ip":"$http_x_forwarded_for",' '"client":"$remote_addr",' '"request_method":"$request_method",' '"scheme":"$scheme",' '"domain":"$server_name",' '"referer":"$http_referer",' '"request":"$request_uri",' '"args":"$args",' '"request_body":"$request_body",' '"size":$body_bytes_sent,' '"@status": $status,' '"responsetime":$request_time,' '"upstreamtime":"$upstream_response_time",' '"upstreamaddr":"$upstream_addr",' '"http_user_agent":"$http_user_agent",' '"https":"$https"' '}'; access_log /usr/local/nginx/logs/access.log main; sendfile on; tcp_nopush on; open_file_cache max=204800 inactive=20s; open_file_cache_min_uses 1; open_file_cache_valid 40s; geo $whiteiplist { default 1; #192.168.158.188 0; #192.168.158.189 0; } map $whiteiplist $limit { 1 $binary_remote_addr; 0 ""; } limit_req_zone $limit zone=rateip:100m rate=1000r/s; limit_conn_zone $limit zone=perip:100m; autoindex off; server_tokens off; #keepalive_timeout 0; max_ranges 1; keepalive_timeout 65; send_timeout 120; proxy_read_timeout 120; proxy_connect_timeout 120; client_max_body_size 512m; client_header_timeout 60; client_body_timeout 60; server_names_hash_bucket_size 64; #gzip on; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png; gzip_vary on; client_body_buffer_size 1024k; client_header_buffer_size 1024k; large_client_header_buffers 4 4096k; proxy_send_timeout 1m; proxy_buffer_size 16k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 128k; proxy_temp_path /usr/local/nginx/proxy_temp; proxy_cache_path /usr/local/nginx/proxy_cache levels=1:2 keys_zone=content:20m inactive=1d max_size=100m; #add https server { listen 443 ssl; #server_name api.clife.cn; server_name localhost; root /usr/local/nginx/html; access_log /usr/local/nginx/logs/https_access.log main; limit_conn perip 1000; limit_req zone=rateip burst=100 nodelay; ssl on; ssl_certificate /usr/local/nginx/conf/server.crt; ssl_certificate_key /usr/local/nginx/conf/server.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; #按照这个协议配置 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置 ssl_session_cache shared:SSL:1m; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Request-URI $scheme://$host$uri; # if ($http_user_agent ~* "WMSDK") # { return 404; } if ($http_user_agent ~* "echo|uname|concat|select|bash|nmap|scan" ) { return 403; } location ~* \.(bak|save|sh|sql|mdb|old)$ { rewrite ^/(.*)$ $host permanent; } if ($query_string ~* "union.*select.*\(") { rewrite ^/(.*)$ $host permanent; } if ($query_string ~* "concat.*\(") { rewrite ^/(.*)$ $host permanent; } # 这里反向代理作用是: 当在浏览器输入 https://ip/ 的时候相当于访问了http://ip:8083, 可根据需要自行配置 location / { proxy_pass http://ip:8083; } } }三. 通过Nginx配置证书(双向认证)双向认证流程端向服务端发送SSL协议版本号、加密算法种类、随机数等信息。端给客户端返回SSL协议版本号、加密算法种类、随机数等信息,同时也返回服务器端的证书,即公钥证书。户端使用服务端返回的信息验证服务器的合法性,包括: • 是否过期 • 发型服务器证书的CA是否可靠 • 返回的公钥是否能正确解开返回证书中的数字签名 • 服务器证书上的域名是否和服务器的实际域名相匹配 • 验证通过后,将继续进行通信,否则,终止通信端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端。证客户端的证书,通过验证后,会获得客户端的公钥。户端向服务端发送自己所能支持的对称加密方案,供服务器端进行选择。器端在客户端提供的加密方案中选择加密程度最高的加密方式。密方案通过使用之前获取到的公钥进行加密,返回给客户端。端收到服务端返回的加密方案密文后,使用自己的私钥进行解密,获取具体加密方式,而后,产生该加密方式的随机码,用作加密过程中的密钥,使用之前从服务端证书中获取到的公钥进行加密后,发送给服务端。端收到客户端发送的消息后,使用自己的私钥进行解密,获取对称加密的密钥,在接下来的会话中,服务器和客户端将会使用该密码进行对称加密,保证通信过程中信息的安全。 双向认证大致流程为 客户端发送https连接请求, 并发送ssl协议相关信息, 服务器返回ssl协议信息以及公钥证书客户端校验公钥证书后, 将自己的公钥证书发送给服务端, 服务端进行校验,校验成功后, 客户端发送当前可行加密方案给服务端服务端, 服务端选择合适的方案给客户端客户端产生一个随机码并根据服务端指定的加密方案加密, 服务端收到后根据证书私钥解密改随机码并作为堆成加密秘钥建立连接, 并通过对称加密加密传输数据注意: SSL协议信息主要包括: SSL协议版本号、加密算法种类、随机数等信息校验公钥证书, 主要包括 • 证书是否过期 • 发行服务器证书的CA是否可靠 • 返回的公钥是否能正确解开返回证书中的数字签名 • 服务器证书上的域名是否和服务器的实际域名相匹配 • 通过后,将继续进行通信,否则,终止通信https的双向认证比起单向认证, 主要是在客户端验证服务端证书安全后, 将自己客户端的证书发送给服务端, 让服务端在校验一次.相对于单向认证, 双向认证可以确保通信双方的可靠性, 防泄漏, 防劫持, 安全性更高配置步骤 我们可以全程使用openssl来生成一些列的自签名证书,自签名证书没有通过证书机构的认证, 虽然很多浏览器会认为不安全,但我们用来实验是足够的。 需要安装的nginx版本在1.6.1且支持ssl 模块(见第二章第1节) 由上图梳理证书创建流程: 创建根证书 -> 根据根证书创建服务端证书 -> 根据根证书创建客户端证书 1. 创建根证书代码语言:javascript复制#(1)创建根证书私钥: openssl genrsa -out root.key 1024 #(2)创建根证书请求文件: openssl req -new -out root.csr -key root.key #后续参数请自行填写,下面是一个例子: Country Name (2 letter code) [XX]:cn State or Province Name (full name) []:jiangsu Locality Name (eg, city) [Default City]:nanjing Organization Name (eg, company) [Default Company Ltd]:hrt Organizational Unit Name (eg, section) []:njhrt Common Name (eg, your name or your servers hostname) []:root Email Address []: # 直接回车即可 A challenge password []: # 直接回车即可 An optional company name []: # 直接回车即可 #(3)创建根证书: openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650在创建根证书和下面服务器请求文件和客户端请求文件的时候需要注意: 根证书的Common Name可填写为 root . 所有客户端和服务器端的证书这个字段需要填写域名或者ip,一定要注意的是,根证书的这个字段和客户端证书、服务器端证书不能一样其他所有字段的填写,根证书、服务器端证书、客户端证书需保持一致无用项可以选择直接回车跳过经过上面三个命令, 我们可以得到 root.crt : 签名有效期为10年的根证书root.key: 根证书私钥文件后面我们可以用这个根证书去颁发服务器证书和客户端证书 2. 根据根证书创建服务端证书代码语言:javascript复制#(1)生成服务器端证书私钥: openssl genrsa -out server.key 1024 #(2) 生成服务器证书请求文件,过程和注意事项参考根证书,本节不详述: openssl req -new -out server.csr -key server.key #(3) 生成服务器端公钥证书 openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650通过上面的三个命令,我们得到: server.key:服务器端的秘钥文件server.crt:有效期十年的服务器端公钥证书,使用根证书和服务器端私钥文件一起生成server.csr : 服务端证书请求文件(请求证书时使用, 后续配置无用)3. 根据根证书创建客户端证书需要注意的是, 可以生成多个客户端证书, 只需安装下面1-4步重新生成即可 代码语言:javascript复制#(1)生成客户端证书秘钥: openssl genrsa -out client.key 1024 #(2) 生成客户端证书请求文件,过程和注意事项参考根证书,本节不详述: openssl req -new -out client.csr -key client.key #(3) 生客户端证书 openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650 #(4) 生客户端p12格式证书,需要输入一个密码,选一个好记的,比如123456 openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12重复使用上面的三个命令,我们得到: client.key:客户端的私钥文件client.crt:有效期十年的客户端证书,使用根证书和客户端私钥一起生成client.p12:客户端p12格式,这个证书文件包含客户端的公钥和私钥,主要用来给浏览器或postman访问使用clinet.csr : 客户端证书请求文件(请求证书时使用, 后续配置无用)4. nginx配置代码语言:javascript复制server { listen 443 ssl; server_name www.yourdomain.com;# 无域名可填写ip ssl on; ssl_certificate /data/sslKey/server.crt; #server公钥证书 ssl_certificate_key /data/sslKey/server.key; #server私钥 ssl_client_certificate /data/sslKey/root.crt; #根证书,可以验证所有它颁发的客户端证书 ssl_verify_client on; #开启客户端证书验证 # 反向代理eg: 作用是通过https(443端口)访问, 则会直接去请求本机的8991端口 location / { proxy_pass http://127.0.0.1:8991/; } #location / { # root html; # index index.html index.htm; # } }注意: 配置的主要内容是配置了服务器端证书的公钥私钥以及根证书的公钥, 并且ssl_verify_client 参数设置为 on 如果客户端证书不是由根证书直接颁发的,配置中还需要加一个配置:ssl_verify_depth 1; 配置完成后,通过nginx -s reload 令配置文件重新加载, 无需重启 访问测试1. postman访问 进入 File -> Settings 在设置General中先把SSL certificate verification关掉 可以Certificates中配置p12文件,p12文件可以认为是一对公私钥的合体文件, 同时也要配置p12文件的密码。注意这里的地址和端口要与实际的一致,否则请求时会认证失败。 也可以在Certificates中配置客户端公私钥证书。无需配置密码 访问测试 在未配置客户端证书时, 访问服务器端的项目. 会返回 400 No required SSL certificate was sent, 提示客户端没有提供所需的证书 在配置好客户端证书之后, 发现可以正常访问 2. 浏览器访问 浏览器一般用单向认证会比较多,双向认证的详细配置步骤这里就不多说了。 主要就是把自己客户端的证书文件(.p12, crt)双击之后安装到自己电脑的证书列表中再访问服务端, 如果提示服务端的证书有风险,点击继续就行。需要注意的是, 如果之前访问不成功, 在安装好证书之后需要重启浏览器才会生效 访问成功效果如下图所示: 关于更多证SSL书文档配置可见下面参考文档, 后续也会在此基础上继续更新… 参考文档 1. springboot项目配置阿里云ssl证书,http转https 2. Springboot配置使用ssl,使用https 3. Spring boot使用阿里云SSL证书报错:org/springframework/boot/context/properties/bind/Binder 4. IOException: Alias name tomcat does not identify a key entry 沃通证书部署问题解决办法 5. 双向认证配置指南 6. Nginx配置Https单向认证、双向认证以及多证书配置 7. 双向认证开发实践