HTTPS总结

一、浏览器A发送一个请求A到服务器A,首先会经过TCP的三次握手,这个先不关心,然后就是SSL/TLS握手,(SSL和TLS都是网络安全套接字的意思,TLS是新版本,最新的已经到了TLS1.3了),这里只总结关键步骤,详细流程还是得查询一下资料,浏览器A会首先要求服务器A发送一个有效的证书链(根证书-》中间证书-》服务器证书)过来,证书链要有CA颁发机构,比如A,除了根证书外每个证书都得有公钥,及公钥签名及其他信息。

说明:根证书的公钥是浏览器内置的,表明我这个浏览器就默认信任这些证书,自己也可以安装,但是要慎重,而且浏览器会定时更新信任列表,这些都是浏览器自己做的,不用关心。需要理解的是这个公钥内置,全世界的CA机构就那些,全部都存在浏览器里面了,这个就是整个信任链的开始,根证书里面会有一个签名,业内术语叫做自签名,就是自己签名,自己验签,CA机构A拿自己的私钥A对自己的公钥A签名,然后浏览器根据根证书里面的机构名A,拿到内置的公钥A,验证这个签名,能通过,表明这个根证书是值得信任的。

信任了这个根证书之后,还需要验证中间证书B的签名B,这个中间证书签名B是根证书的私钥A对中间证书公钥B得出来的签名,还是拿内置的经过验证的公钥A来验签,通过就说明这个中间证书可信,拿到中间证书的公钥B,来验证服务器证书C的签名C,没错这个签名C就是中间证书的私钥B对服务器证书的公钥C签出来的,验证通过后,浏览器A就可以相信跟服务器A之间的通信是加密的值得可信的,不会有中间人在中间作梗,HTTPS的作用就这些,保证传输安全,不被人篡改。

再说明:浏览器和服务器SSL握手成功之后,由于非对称加密算法的低效率,传输内容的加密采用的是对称加密,而非对称加密只会加密对称加密使用的秘钥。然后浏览器还会对整个内容进行Hash签名(主要是快),保证数据的完整性。

三说明:SSL只能保证数据不被篡改,但是不能保证请求A就是用户A发出来的,用户B可以伪造,也不能保证服务器的运维开发人员通过技术手段拿到一些个人信息,这时就需要服务器来设计一套加解密机制保证应用信息的绝对安全,就是只有用户A,和处理请求的服务器A,以及绝对有责任的开发运维A知道这些信息,无关的运维人员即便截取到也是密文。

到这里浏览器与服务器之间的SSL就算是总结完了。

二、java客户端A发送一个请求A到服务器A,这里的区别就是java需要自己加载信任的根证书,需要自己安装到一个专门的jks或者通用的pkcs12/pfx/p12的证书仓库里面然后手动load,这个就是trustStore(值得信任的公钥库),然后如果还有双向认证呢,还需要加载一个keyStore,就是私钥库。

手撸一下代码(windows系统可以直接使用git bash自带的 openSSL,省的找包安装了):

1,生成一对根证书公私钥:

bash 复制代码
openssl req -x509 -new -newkey rsa:2048 -nodes -keyout rootCA.key -out rootCA.crt -days 3650

这里会让输入一些证书的信息如下:C=CN(国家)ST=Beijing(省份)L=Beijing(城市)O=MyOrganization(机构)OU=ITDepartment(部门)CN=MyPrivateRootCA(名称)emailAddress=admin@myorg.com(联系方式)自己测试就随便造个数据,这里私钥和公钥证书都一次生成了。

2,生成一个服务器证书的私钥key:

bash 复制代码
openssl req -new -key server.key -out server.csr -config server.csr.cnf 

这里只生成了一个私钥key,公钥证书需要拿根证书来签名生成。

3,生成一个证书申请文件csr:

bash 复制代码
openssl req -new -key server.key -out server.csr -config server.csr.cnf

这里需要一个server.csr.cnf文件 内容如下:

bash 复制代码
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
# req_extensions = req_ext

[dn]
# 以下信息应与你的组织或测试环境匹配
C = CN
ST = Beijing
L = Beijing
O = MyOrganization
OU = WebServer
# 最重要的字段:必须是你访问服务器时使用的域名
CN = localhost

# !!! 关键部分:主题备用名称(SAN)!!!
[req_ext]
subjectAltName = @alt_names

[alt_names]
# 列出所有需要被证书认可的访问方式
DNS.1 = localhost
DNS.2 = localhost.com
IP.1 = 127.0.0.1
# 你可以继续添加更多域名或IP,例如:
# DNS.3 = www.example.com
# IP.2 = 192.168.1.100

4,拿根证书去签名生成一个服务器证书文件,包含公钥及CA机构信息还有公钥签名等

bash 复制代码
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile server.csr.cnf -extensions req_ext

5,由于是自生成的根证书还需要将其安装到信任的CA列表里面,使用JAVA 的 keytool命令:

bash 复制代码
keytool -import -trustcacerts -alias myCaRoot -file rootCA.crt -keystore my-truststore.jks -storepass 123456 -noprompt

6,将自己生成的服务器证书和私钥配置在nginx 里面。

bash 复制代码
server {
        listen       8443 ssl;
        server_name  localhost;
        ssl_certificate      ssl/server.crt;
        ssl_certificate_key  ssl/server.key;
        location / {
            root   html;
            index  index.html index.htm;
        }
 }

7,浏览器访问 会提示不受信任。

8,JAVA代码访问,注意这个 localhost 的匹配:

java 复制代码
package org.example;

import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

public class SSLClientTest {

    @Test
    void testHttp() {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            ClassicHttpRequest httpGet = ClassicRequestBuilder.get("http://127.0.0.1:8848").build();
            httpclient.execute(httpGet, response -> {
                System.out.println(response.getCode() + " " + response.getReasonPhrase());
                final HttpEntity entity = response.getEntity();

                String string = EntityUtils.toString(entity);
                System.out.println(string);
                EntityUtils.consume(entity);
                return null;
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    void testHttps() throws Exception {
        final PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
            .setTlsConfigResolver(host -> TlsConfig.DEFAULT)
            .setTlsSocketStrategy(getDefaultClientTlsStrategy(loadTrustStore()))
            .build();
        try (CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build()) {

            ClassicHttpRequest request = ClassicRequestBuilder.get("https://localhost:8443/").build();
            System.out.println("Executing request " + request);
            httpclient.execute(request, response -> {
                System.out.println("----------------------------------------");
                System.out.println(request + "->" + new StatusLine(response));

                String string = EntityUtils.toString(response.getEntity());
                System.out.println(string);

                EntityUtils.consume(response.getEntity());
                return null;
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private DefaultClientTlsStrategy getDefaultClientTlsStrategy(KeyStore keyStore)
        throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        return new DefaultClientTlsStrategy(SSLContextBuilder.create().loadTrustMaterial(keyStore, null).build());
    }

    private KeyStore loadTrustStore()
        throws Exception {
        KeyStore instance = KeyStore.getInstance(KeyStore.getDefaultType());
        try (InputStream is = Files.newInputStream(Paths.get("D:/application/nginx-1.28.0/conf/ssl/my-truststore.jks"))) {
            if (is == null) {
                throw new RuntimeException("无法找到信任库文件: my-truststore.jks");
            }
            instance.load(is, "123456".toCharArray()); // 使用你设置的密码
        }
        System.out.println(instance.size());
        return instance;
    }
}

三、还有一个需要记录的就是各种文件格式:

pem 就是个base64的文件格式,可以是公钥 也可以是私钥。

der 是个二进制的文件,可以是公钥,也可以是私钥。

crt/cer 一般就是指证书,包含公钥及CA机构信息还有公钥签名等

jks java专用的公钥库、私钥库,

pkcs12/pfx/p12 通用的公钥库、私钥库

csr 证书签名请求。这个是申请证书的时候提供的东西,由自己的私钥签名信息 和公钥信息,证明我拥有该对秘钥。

以上搞一遍基本原理就能了解个七七八八了。

相关推荐
雨落秋垣12 小时前
大屏可视化系统:WebRTC视频流与WebSocket实时数据集成方案
websocket·网络协议·webrtc
ZeroNews内网穿透12 小时前
本地部署 Payara Server 公网访问
运维·服务器·网络协议·安全
阿巴~阿巴~13 小时前
从帧到包:深入解析链路层与局域网通信的核心机制
服务器·网络·网络协议·tcp/ip·智能路由器·mac·数据链路层
rfidunion13 小时前
ubuntu下使用qemu模拟ARM(一)-------安装samba服务器
服务器·arm开发·ubuntu
科技块儿13 小时前
金融级IP离线库深度测评:IP数据云 vs IPnews vs MaxMind
网络协议·tcp/ip·金融
回忆是昨天里的海13 小时前
dockerfile-镜像分层机制
linux·运维·服务器
翼龙云_cloud13 小时前
阿里云渠道商:阿里云弹性伸缩有哪几种
服务器·阿里云·云计算
双层吉士憨包13 小时前
如何安全访问 Kickass Torrents:代理选择、设置与最佳实践(2026)
运维·服务器
爱宇阳13 小时前
Windows 通过 SSH 下载服务器目录并完整上传到指定服务器目录(scp / rsync 实战教程)
服务器·windows·ssh
JY.yuyu13 小时前
Windows Server服务器数据备份 / 活动目录(AD域)
运维·服务器