自签名证书与https服务

原理

https 的原理是通过在传输层(tcp)和应用层(http)之间添加一层协议来达到加密的目的。

一个https请求分为两个阶段:

  • 第一阶段: 在客户端生成key,该key用来数据传输的对称加密使用。这时候服务端是没有这个key的,所以客户端需要通过非对称加密的方式将key发送给服务端。

  • 第二阶段: 在服务端收到key之后,服务端和客户端就都有key了,这时候服务端和客户端就可以进行加密通信了。

证书就是非对称加密的公钥

非对称加密: 发送者和接收者使用两个不同的密钥。非对称加密,也称为公钥加密 ,使用公钥加私钥:用公钥加密的数据只能用私钥解密。

而证书就是公钥,整个加密流程如下:
在这里插入图片描述

通过上面的流程服务端和客户端都拥有了key,这时候就可以通过key加密传输的数据。

对称加密传输数据

为什么不直接使用非对称加密传输数据,这样少了一步同步key的操作?因为非对称加密是单向加密传输,接收端必须持有私钥。当然可以把客户端的公钥发送给服务端,然后服务端使用客户端的公钥进行解密内容。但是还有一个考量因素是非对称加密的效率比较低下,不适合频繁使用的场景。

生成方式

生成方式主要有两种,分别是openssl和jdk的keytool,下面是分别使用两种方式生成域名water.com的例子。

1 使用openssl

1.1 生成私钥

首先需要生成私钥。

openssl genpkey -algorithm RSA -out water
  • -algorithm:生成密钥库的算法,这里使用RSA。
  • -out:指定密钥库的输出路径。

1.2 生成证书签名请求

在生成证书之前,需要生成证书签名请求,该请求在生成证书的时候需要使用。

openssl req -new -key water -out cert.csr
  • -new:创建一个新的证书签名请求。
  • -key water:使用的私钥。
  • -out:输出请求的路径。

命令执行的时候需在Common Name这个选项填写域名water.com

1.3 生成证书

openssl x509 -req -days 365 -in cert.csr -signkey water -out water.pub
  • x509:使用证书处理命令。
  • -req:表示输入的是证书签名请求 (CSR)。
  • -days 365:指定生成的证书的有效期,这里设置为365天。
  • -in cert.csr:指定输入的证书签名请求文件路径和文件名,这里是cert.csr。
  • -signkey water:指定用于对证书签名的私钥文件路径和文件名,这里是 water。通过该私钥,OpenSSL将对CSR进行签名,生成一个数字证书。
  • -out water.pub:指定生成的数字证书文件的输出路径和文件名,这里是 water.pub。

1.4 查看证书

openssl x509 -noout -text -in water.pub
  • x509: 使用证书处理命令。
  • -noout: 不打印证书的文本表示(不显示证书的原始编码)。
  • -text: 显示证书的文本表示(显示证书的人类可读的详细信息)。
  • -in water.pub: 指定输入的数字证书文件路径和文件名,这里是water.pub。

2 使用keytool

keytool是jdk自带的一个密钥工具。

2.1 生成密钥库

keytool -genkey -alias water -keyalg RSA -keystore keystore
  • -genkey: 生成密钥对
  • -alias water: 指定生成的密钥对的别名。在后续的操作中,可以通过别名来引用该密钥对。
  • -keyalg RSA: 指定生成密钥对所使用的算法,这里是RSA算法。
  • -keystore keystore: 指定存储密钥对的密钥库文件的路径和文件名,这里是keystore。

在交互中What is your first and last name? 中需要填写域名water.com

2.2 从密钥库中导出证书

keytool -export -trustcacerts -alias water -file water.cer -keystore keystore
  • -export: 导出证书的命令。
  • -trustcacerts: 表示将证书链中的所有证书都导出,包括根证书和中间证书。如果用于配置信任链,通常需要加上这个选项。
  • -alias water: 指定要导出的证书的别名,这里是water。
  • -file water.cer: 指定导出的证书文件的输出路径和文件名,这里是water.cer。这个文件将会包含导出的证书信息。
  • -keystore keystore: 指定包含要导出证书的密钥库文件的路径和文件名,keystore。

生成方式的区别

keytool是先生成密钥库(包含公私钥),然后再从密钥库导出证书(公钥),文件内容是JKS格式。

JKS文件是一种经过加密的安全文件,以二进制的Java密钥库(KeyStore)格式存储的一组密钥和证书,需要密码才能打开

openssl先生成私钥,然后生成证书(公钥),文件内容是pem格式。

PEM(Privacy Enhanced Mail)**一般为文本格式,以“-----BEGIN ***-----”开头,以“-----END *-----结尾”,中间的内容是Base64编码

应用

为water.com配置https

配置域名到host文件

vim /etc/hosts
127.0.0.1 water.com

1 tomcat配置https

因为tomcat使用的格式是jks,所以使用jdk生成的keystore比较方便。

1.1 生成keystore密钥对

keytool -genkey -alias water -keyalg RSA -keystore keystore

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNZEFbAA-1691475072357)(./certificate.assets/image-20230803074113890.png)]

密码设置123456。

1.2 配置tomcat

修改tomcat的配置文件$TOMCAT_HOME/conf/server.xml,增加如下配置,只需要用到密钥库即可,并不需要手动导出证书,证书由tomcat根据密钥库生成并发送到浏览器。

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
SSLEnabled="true" maxThreads="150" scheme="https" 
secure="true" clientAuth="false" sslProtocol="TLS"
        keystoreFile="/home/yqz/tmp/keystore" keystorePass="123456"
         />

  • secure="true": 表示这个连接是安全的,基于HTTPS协议。
  • clientAuth="false": 是否需要客户端进行身份验证。这里设置为false,表示不需要客户端提供证书进行身份验证。
  • sslProtocol="TLS": 指定使用的SSL/TLS协议版本,这里是TLS,表示使用TLS协议进行加密通信。
  • keystoreFile="/home/yqz/tmp/keystore": 指定密钥库文件的路径和文件名,这里是keystore,用于存储服务器的密钥对和证书。
  • keystorePass="123456": 指定密钥库的密码,这里是123456,密钥库密码用于访问密钥库中的密钥对。

1.3 验证效果

启动tomcat,访问https://water.com:8443

在这里插入图片描述

成功访问,https划线是因为自签名证书对浏览器来说是不安全的。

2 nginx配置https

nginx需要使用pem格式的证书,所以使用openssl来进行生成更加方便。

2.1生成私钥

openssl genpkey -algorithm RSA -out water

2.2 生成证书签名请求

openssl req -new -key water -out cert.csr

在交互中,Common Name选项需要设置域名water.com
在这里插入图片描述

2.3 生成证书

openssl x509 -req -days 365 -in cert.csr -signkey water -out water.pub

至此我们得到了三个文件
在这里插入图片描述

water为私钥,water.pub为公钥

2.4 配置nginx

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    
  server {
	#监听443端口
    listen 443;
    #你的域名
    server_name water.com; 
    ssl on;
    
    #ssl证书的pem文件路径
    ssl_certificate  /home/yqz/tmp/openssl/water.pub;
    
    #ssl证书的key文件路径
    ssl_certificate_key /home/yqz/tmp/openssl/water;
    
    location / {
     proxy_pass  http://公网地址:项目端口号;
    }
}
}

2.5 验证

在这里插入图片描述

3 springboot配置https

3.1 生成keystore密钥对

keytool -genkey -alias water -keyalg RSA -keystore keystore

在这里插入图片描述

密码设置123456。

3.2 Gateway Maven依赖

<!-- 服务网关 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

3.3 application.yml

复制生成的密钥库到项目resource目录下

在这里插入图片描述

server:  
  port: 8083  
  ssl:  
    enabled: true  
    key-store: classpath: keystore
    key-store-type: JKS
    key-store-password: 123456

3.4 测试Controller

package com.yqz.testgateway;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {
        return "hello";
    }

}

3.5 验证

在这里插入图片描述

4 jdk信任证书

在chrome浏览器中,对于安全的证书能够手动设置信任该证书,但是在程序中如何调用不安全的https接口呢?这时候就需要在jdk手动导入第三方证书,使得jdk信任该证书。

4.1 测试程序中调用https接口

引入openfeign

<!--feign组件-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

定义feign接口

package com.yqz.testgateway;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "test", url = "https://water.com")
public interface TestFeign {

    @GetMapping("/test")
    String test();

}

测试

package com.yqz.testgateway;

import jakarta.annotation.Resource;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Resource
    private TestFeign testFeign;

    @GetMapping("/test")
    public String test() {
        return "hello";
    }

    @GetMapping("/test2")
    public String test2() {
        // 调用https://water.com:8083/test
        return testFeign.test();
    }

}

没有导入证书的时候调用不安全的https:water.com:8083/test2接口会抛出SunCertPathBuilderException异常。

feign.RetryableException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target executing GET https://water.com:8083/test

4.2 从keystore中导出证书

keystore是一个密钥库,包含了公钥和私钥,现在要导出公钥作为证书。

keytool -export -trustcacerts -alias water -file water.cer -keystore keystore

通过该命令得到water.cer文件,该文件就是公钥。

4.3 导入证书

将water.cer公钥导入到jdk中

keytool -import -trustcacerts -alias water -file water.cer -keystore "/home/yqz/Applications/jdk-17.0.8/lib/security/cacerts"

密码是cacerts的默认密码"changeit“,不是生成密钥库的密码123456

4.4 验证是否存在证书

import java.io.FileInputStream;  
import java.security.KeyStore;  
import java.security.cert.Certificate;  
import java.security.cert.CertificateFactory;  
import java.security.cert.X509Certificate;  
import java.util.Enumeration;  
  
public class Test {  
  
    public static void main(String[] args) throws Exception {  
        // 信任的证书文件路径  
        String certificateFilePath = "/home/yqz/tmp/water.cer";  
  
        // 加载信任的证书  
        Certificate certificate;  
        try (FileInputStream is = new FileInputStream(certificateFilePath)) {  
            CertificateFactory cf = CertificateFactory.getInstance("X.509");  
            certificate = cf.generateCertificate(is);  
        }  
  
        // 加载 cacerts 文件  
        String cacertsPath = System.getProperty("java.home") + "/lib/security/cacerts";  
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());  
        char[] password = "changeit".toCharArray(); // 默认的 cacerts 密码为 "changeit"        
        try (FileInputStream is = new FileInputStream(cacertsPath)) {  
            keyStore.load(is, password);  
        }  
  
        // 检查是否信任证书  
        String alias = null;  
        Enumeration<String> aliasEnum = keyStore.aliases();  
        while (aliasEnum.hasMoreElements()) {  
            String currentAlias = aliasEnum.nextElement();  
            Certificate currentCertificate = keyStore.getCertificate(currentAlias);  
            if (currentCertificate instanceof X509Certificate) {  
                if (currentCertificate.equals(certificate)) {  
                    alias = currentAlias;  
                    break;  
                }  
            }  
        }  
  
        // 输出结果  
        if (alias != null) {  
            System.out.println("The certificate is trusted. Alias: " + alias);  
        } else {  
            System.out.println("The certificate is NOT trusted.");  
        }  
    }  
  
}

4.4 再次测试https

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值