当前位置: 首页 > article >正文

当今互联网安全的基石 - TLS/SSL

LSTransport Layer Security传输层安全协议发展历程TLS 是 SSL 协议的继任者。由于 SSL 协议存在一些安全漏洞并且随着网络安全需求的不断提高IETFInternet Engineering Task Force对 SSL 3.0 进行了改进推出了 TLS 1.0 协议于 1999 年发布。TLS 协议在后续又不断更新版本如 TLS 1.1、TLS 1.2 和 TLS 1.3每一个新版本都在安全性和性能等方面有所提升。工作原理TLS 的工作原理与 SSL 类似也包括握手阶段。在 TLS 握手过程中客户端和服务器会协商协议版本、加密算法套件、交换密钥等。TLS 1.3 相比之前的版本简化了握手过程减少了握手消息的往返次数从而提高了连接建立的速度并且增强了安全性如加强了密钥交换的安全性。例如TLS 1.3 使用了更安全的密钥交换算法如基于椭圆曲线的 Diffie - Hellman 密钥交换ECDHE这种算法能够有效抵抗中间人攻击等安全威胁。应用场景TLS 的应用范围非常广泛。它不仅用于 Web 安全还用于电子邮件安全如 IMAP、POP3 和 SMTP 协议的安全版本、虚拟专用网络VPN等众多网络通信场景。在现代互联网中几乎所有需要安全通信的地方都会优先考虑使用 TLS 协议。SSL Secure Sockets Layer安全套接层协议发展历程SSL 最初是由网景公司Netscape在 1994 年开发的目的是为了保障网络通信的安全特别是在 Web 浏览器和服务器之间的通信安全。SSL 1.0 由于存在安全漏洞从未发布SSL 2.0 在 1995 年发布但也有安全问题。SSL 3.0 于 1996 年发布它修复了许多 SSL 2.0 的问题成为当时比较流行的安全协议。工作原理SSL 协议在应用层和传输层之间工作。它通过使用公钥和私钥加密技术来保证通信的保密性。当客户端如浏览器与服务器建立连接时首先会进行 SSL 握手过程。在握手阶段客户端向服务器发送客户端支持的加密算法列表等信息。服务器从列表中选择一种加密算法并将服务器的证书发送给客户端。客户端验证服务器证书的合法性例如检查证书是否由可信任的证书颁发机构CA颁发证书是否过期等。如果证书验证通过客户端和服务器就会协商出一个对称加密密钥。之后的数据传输就使用这个对称密钥进行加密这样可以保证数据在传输过程中的保密性因为只有客户端和服务器知道这个对称密钥。应用场景SSL 主要应用于 Web 安全。例如在早期的 httpsHypertext Transfer Protocol Secure网站访问中就是通过 SSL 协议来加密浏览器和服务器之间的数据传输。这使得用户在网站上输入的敏感信息如登录密码、信用卡信息等在传输过程中不会被窃取。两者关系和区别关系TLS 可以看作是 SSL 的升级版。TLS 协议在设计上借鉴了 SSL 协议的很多理念并且在 SSL 的基础上进行了改进和扩展以应对不断出现的新的安全威胁和网络通信需求。区别安全性TLS 在安全性能上比 SSL 更优越。例如SSL 3.0 存在 POODLEPadding Oracle On Downgraded Legacy Encryption攻击漏洞而 TLS 协议通过不断更新版本来修复类似的安全漏洞。TLS 1.3 更是在密钥交换等关键环节采用了更先进的加密技术提供了更强的安全性。兼容性SSL 是比较早期的协议在一些旧的系统和软件中有应用。但随着网络安全标准的提高现代的应用和系统更倾向于使用 TLS 协议。TLS 在兼容性方面也在不断扩展能够更好地与各种新的网络技术和应用场景相结合。目前常用的是 TLS 1.2 和 TLS 1.3; SSL不多见了HTTPS 工作流程Client 发起请求端口443Server 返回公钥证书Client 验证证书Client 生成对称密钥用公钥加密后发给ServerServer使用私钥解密得到对称密钥C/S双方使用对称密钥加密明文并发送解密密文得到明文For TLS 1.2For TLS 1.3TLS证书签名与验证颁发证书的过程撰写证书元数据: 包括签发人(Issuer), 地址, 签发时间, 有效期 等, 还包括证书持有者(Owner)基本信息, 比如 DN(DNS Name, 即证书生效的域名),Owner 公钥等信息使用通用的 Hash 算法如SHA-256对证书元数据计算生成数字摘要使用签发人 Issuer (CA)的私钥对该数字摘要进行加密, 生成一个加密的数字摘要, 也就是Issuer的数字签名将数字签名附加到数字证书上, 变成一个签过名的数字证书将签过名的数字证书与Issuer (CA)的公钥, 一同发给证书使用者注意, 将公钥主动发给使用者是一个形象的说法, 通常系统或者浏览器都内置 Issuer (CA)的公钥 为可信任的根证书所以 CA的私钥是绝对不能被泄露, 它作为互联网被信任的基础验证证书的过程证书使用者获通过某种途径如浏览器访问获取到该数字证书, 解压后分别获得证书元数据和数字签名,(Owner)的公钥等使用同样的Hash算法计算证书元数据的数字摘要使用签发人 Issuer (CA)的公钥对数字签名进行解密, 得到 解密后的数字摘要从第1步获得的数字摘要值, 再对比 2 和 3 两个步骤得到的数字摘要值; 如果相同, 则说明这个数字证书确实是被 签发人Issuer 验证过合法证书, 证书中的信息最主要的是确定 Owner 的公钥是可信的, 此后通信 信任使用该公钥解密对方私钥加密的数据in short 握手时服务端需要提供自己的公钥 和被 ca签发过的数字摘要; 关键在与客户端需要一个可信任 Issuer(CA)证书链在 chrome 证书信息- 证书路径, 可以看到证书的签发链.例, 百度的网站:GlobalSign Root CA - GlobalSign Organization Validation CA - baidu.com这个路径可以抽象为三个级别:end-user: 即 baidu.com, 该证书包含百度的公钥, 访问者就是使用该公钥将数据加密后再传输给百度, 即在 HTTPS 中使用的证书intermediates: 即上文提到的 签发人 Issuer, 用来认证公钥持有者身份的证书, 负责确认 HTTPS 使用的 end-user 证书确实是来源于百度; 这类 intermediates 证书可以有很多级, 也就是说 签发人 Issuer 可能会有有很多级root: 可以理解为最高级别的签发人 Issuer, 负责认证intermediates 身份的合法性in short 其实就是一个可信任的链条, 最终的目的就是为了保证 end-user 证书是可信的, 该证书的公钥也就是可信的.参考: 关于证书链的一点认知TLS 握手步骤(0) Client 与Server之间建立 (TCP 三次握手) 连接(1) Client 向Server发送 client hello 消息, 里面包含了安全相关的信息, 例如SSL/TLS 版本号, Client 支持的加密套件 (CipherSuite); client hello 消息还包含了一个随机数client random, 用于通信密钥的计算;(可选)SSL/TLS 协议还允许 client hello 消息包含 Client 所支持的压缩算法(2)Server回复一条 server hello 消息, 里面包含了加密套件(Server 从 client hello 消息的 CipherSuites 列表中选择其中一个),session id和 另一个随机数server random; Server 还会在消息中附带自己的数字证书;(可选)如果 Server 需要 Client 的数字证书进行客户端认证, 会向 Client 发送 client certificate request 请求消息, 里面包含了Server 所支持的证书类型和认可的证书颁发机构 CA(双向认证, 同认证客户端同理)(3) Client 收到 server hello,验证 Server 端的数字证书, 并得到证书中Server 端的公钥(关键这里客户端有可信任的证书签发者Issuer, 验证服务端的证书)(4) Client 向Server发送第三个随机数 pre-master secret; 与之前不同,这次的随机数使用了 Server 的公钥加密(非对称加密); 现在双方同时拥有这三个随机数client random,server random,premaster secret, 可以用来计算生成共同的通信密钥 master secret 用于加密后面传输的业务数据;(5 - 可选) 如果收到Server端发来的 client certificate request 请求消息, Client 会向 Server 发送一个使用 Client 自己的私钥加密过的随机数 (暂时记作 secret-A), 附带 Client 的数字证书; 或者发送一个 no digital certificate alert 无证书警告, 这种情况下基本可以认为 SSL/TLS 握手失败;(6 - 可选)Server验证 Client 发送过来的数字证书, 并得到证书中公钥对 Client 进行身份认证 (通过公钥解密上面那个 secret-A);(7) Client 向Server发送 finished 消息, 使用第 4 步中计算出来的密钥进行加密传输 (对称加密), 这表示 Client 端握手阶段已经完成;(8)Server也向 Client 发送 finished 消息, 使用第 4 步中计算出来的密钥进行加密传输 (对称加密), 这表示 Server 端握手阶段完成;(9) SSL/TLS 握手阶段完成, 接下来双方通信的消息都会使用协商出来的密钥进行加密 (对称加密)Content Type: Handshake (22)证书的应用之一 —— TCPSSL通信实例及协议分析(中)每一条消息都会包含有ContentType,Version,HandshakeType等信息;ContentType 指示SSL通信处于哪个阶段, 是握手(Handshake), 开始加密传输(ChangeCipherSpec)还是正常通信(Application)等, 见下表HexDecType0x1420ChangeCipherSpec0x1521Alert0x1622Handshake0x1723ApplicationHandshake Type是在handshanke阶段中的具体哪一步, 见下表CodeDescription0HelloRequest1ClientHello2ServerHello11Certificate12ServerKeyExchange13CertificateRequest14ServerHelloDone15CertificateVerify16ClientKeyExchange20Finished关于双向认证所有通信中涉及两个端点即浏览器和它所连接的网站即客户端和服务器。 在单向SSL身份认证过程中仅验证一个端点服务器的身份。双向认证是指在SSL握手过程中将同时验证客户端和服务器的身份所以双向认证SSL证书至少包括两个或两个以上的证书一个是服务器证书另一个或多个是客户端证书即个人认证证书。主流的证书格式主流的SSL证书格式:DER、CER文件是二进制格式只保存证书不保存私钥。PEM一般是文本格式可保存证书可保存私钥。Privacy Enhanced Mail一般为文本格式以 -----BEGIN... 开头以 -----END... 结尾。中间的内容是 BASE64 编码。这种格式可以保存证书和私钥有时我们也把PEM 格式的私钥的后缀改为 .key 以区别证书与私钥CRT可以是二进制格式可以是文本格式与 DER 格式相同不保存私钥。PFX P12二进制格式同时包含证书和私钥一般有密码保护。JKS二进制格式同时包含证书和私钥一般有密码保护。各服务器平台使用的证书格式:NginxpemApachepemIISpfx/pkcs12TomcatjksNetty For TLS 实例证书生成CA 根证书生成或者称为 Issuer , 它可以签发证书, 作为客户端和服务端共同信任者创建一个私钥生成一个安全的私钥。这个私钥将用于签署根证书以及将来由该根CA签发的所有其他证书。为了确保安全性使用足够长的密钥长度。openssl genpkey -algorithm RSA -out root_ca.key -aes256 -pkeyopt rsa_keygen_bits:4096 # 不加密 # openssl genpkey -algorithm RSA -out root_ca.key -pkeyopt rsa_keygen_bits:2048输入私钥密码创建自签名的根证书使用刚刚生成的私钥来创建一个自签名的根证书。这一步骤不仅会生成证书本身还会收集有关颁发者的详细信息这些信息将嵌入到证书中。openssl req -x509 -new -nodes -key root_ca.key -sha256 -days 36500 -out root_ca.crtreq是用来处理证书请求的命令。-x509表示要创建一个X.509格式的自签名证书。-new表示这是一个新的证书请求。-nodes表示不加密私钥仅适用于这次命令因为私钥已经加密。-key root_ca.key指定了用于签名的私钥。-sha256指定了哈希算法。-days 3650指定了证书的有效期为10年。-out root_ca.crt指定了输出文件名为root_ca.crt。在运行该命令时你会被要求填写一系列问题这些问题的答案将作为证书的主题字段Subject的一部分。例如Country Name (2 letter code): 输入国家代码如CN。State or Province Name (full name): 输入省份或州的全名。Locality Name (eg, city): 输入城市名。Organization Name (eg, company): 输入组织名称。Organizational Unit Name (eg, section): 输入部门名称。Common Name (e.g. server FQDN or YOUR name): (注意这个) 输入通用名称通常是组织的域名或描述性名称对于根证书而言可以是类似于Root CA的字符串。Email Address: 可选可以留空。验证其内容openssl x509 -in root_ca.crt -text -noout客户端信任至此root_ca.crt是CA 自签名的带公钥的证书root_ca.key是CA的私钥(绝对不能泄露)为了让客户端信任这个新创建的根证书需要将CA公钥安装到目标系统的受信任根证书存储中。例如在Windows上你可以双击root_ca.crt文件然后按照提示选择“安装证书”并将安装位置设置为“受信任的根证书颁发机构CA 签署/颁发服务端证书生成目标实体的私钥openssl genpkey -algorithm RSA -out server.key -aes256 -pkeyopt rsa_keygen_bits:2048生成目标实体的签发请求 (CSR)使用刚刚生成的私钥来创建一个证书签名请求CSR。CSR包含了申请者的公钥以及一些识别信息如组织名称、域名等。openssl req -new -key server.key -out server.csr # openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj /CCN/STState/LCity/OOrganization/CNyour.server.domain使用CA根证书的私钥签署CSRopenssl x509 -req -in server.csr -CA root_ca.crt -CAkey root_ca.key -CAcreateserial -out server.crt -days 7300 -sha256 # openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256各文件说明至此有6个文件分别是:1.root_ca.crt (根CA证书公钥) - 自签名 公开 双方信任描述这是由CA自身创建并签署的自签名证书作为信任链的起点。根证书通常包含CA的公钥、名称和其他元数据并且是由CA私钥签名的用途根证书用于验证由该CA签发的所有下级证书的真实性。它通常被预装在操作系统、浏览器或其他应用程序的信任库中作为受信任的根证书机构2.root_ca.key (根CA证书私钥)描述这是与根CA证书配对的私钥用于签署其他证书。私钥是高度敏感的信息不应公开或共享。用途根CA私钥用于签署中间CA证书、服务器证书、客户端证书等。它是建立信任链的基础。安全性私钥应存储在安全的地方最好是在离线环境中只有在需要签署新证书时才连接到网络。 它一旦被攻破所有依赖它的证书都将失去信任。3.root_ca.srl (序列号文件)描述这是一个包含十六进制正整数的文件用于记录已签发证书的序列号。每次签署新证书时OpenSSL会从这个文件中读取当前的最大序列号并为新证书分配一个递增的序列号。4.server.crt (服务器公钥)描述这是由CA签署的服务器证书包含服务器的公钥以及关于证书的一些信息如版本、序列号、签名算法、颁发者、持有者、有效期等。服务器证书用于加密客户端与服务器之间的通信确保数据传输的安全性。用途服务器证书安装在Web服务器上用于HTTPS协议下的安全通信。客户端通过验证服务器证书的有效性来确认服务器的身份并使用证书中的公钥进行加密通信。格式常见的格式包括PEM以-----BEGIN CERTIFICATE-----开头-----END CERTIFICATE-----结尾和DER二进制格式。Linux系统通常使用.crt文件扩展名而Windows系统可能使用.cer5.server.csr (证书签名请求)描述这是证书申请者在申请数字证书时生成的文件包含了公钥和标识名称Distinguished Name, DN如国家、省份、城市、组织名称、通用名称通常是域名等。CSR是以-----BEGIN CERTIFICATE REQUEST-----开头-----END CERTIFICATE REQUEST-----结尾的Base64编码格式。用途CSR文件提交给CA后CA会使用其根证书私钥对CSR中的信息进行签名生成正式的数字证书。CSR文件本身不是证书而是申请证书的请求。生成CSR文件通常由服务器管理员使用OpenSSL等工具生成同时生成相应的私钥。生成CSR时必须确保私钥的安全性因为CSR文件中不包含私钥6.server.key (服务器私钥)描述这是与服务器证书配对的私钥用于解密客户端发送的加密信息。私钥是高度敏感的信息不应公开或共享。用途服务器私钥用于解密通过TLS/SSL协议传输的数据。在HTTPS通信中客户端使用服务器证书中的公钥加密数据服务器则使用私钥解密这些数据。格式常见的格式包括PEM以-----BEGIN RSA PRIVATE KEY-----或-----BEGIN PRIVATE KEY-----开头-----END RSA PRIVATE KEY-----或-----END PRIVATE KEY-----结尾和DER二进制格式。Linux系统通常使用.key文件扩展名每个角色验证需要**客户端需要root_ca.crt根 CA 的公钥证书。客户端将使用此证书来验证服务器的证书是否由可信的 CA 签发。**服务端端需要server.key服务器的私钥。服务器将使用此私钥来解密客户端发送的加密数据并签署响应。server.crt服务器的公钥证书由根 CA 签发。服务器将使用此证书向客户端证明其身份。root_ca.crt可选, 双向验证必选如果你的服务器证书是由中间 CA 签发的你可以将根 CA 证书或中间 CA 证书链一起加载到服务器的信任库中以便客户端可以验证整个证书链。在Netty 使用证书的加载公钥加载.pem, .crt, .cer: 通常以-----BEGIN CERTIFICATE-----开头以-----END CERTIFICATE-----结尾。它既可以包含公钥证书也可以包含私钥。Java本身支持 pem私钥加载.key: openssl 默认给私钥加密了 , 将其转为不加密的, 可使用 bouncycastle加载之# **转换为 PKCS#8 格式 (PEM 编码)** openssl pkcs8 -topk8 -inform PEM -in server.key -outform PEM -nocrypt -out server_pkcs8.key # **转换为 PKCS#8 格式 (DER 编码)** 二进制格式 openssl pkcs8 -topk8 -inform PEM -in server.key -outform DER -nocrypt -out server_pkcs8.der使用 bouncycastle 加载 pkcs8 私钥implementation(cn.hutool:hutool-all:5.8.16) implementation(org.bouncycastle:bcprov-jdk15on:1.70) implementation(org.bouncycastle:bcpkix-jdk15on:1.70)public static void main(String[] args) throws Exception { final String ca_cet E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\root_ca.crt; final String server_crt E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server.crt; final String server_key E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server.key; final String server_pkcs8_key E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server_pkcs8.key; PublicKey root_caKey PemUtil.readPemPublicKey(FileUtil.getInputStream(ca_cet)); System.out.println(root_caKey root_caKey ); Certificate certificate SecureUtil.readX509Certificate(FileUtil.getInputStream(server_crt)); System.out.println(X509C root_caKey ); PublicKey server_crtKey PemUtil.readPemPublicKey(FileUtil.getInputStream(server_crt)); System.out.println(server_crtKey server_crtKey ); PrivateKey privateKey PemUtil.readPemPrivateKey(FileUtil.getInputStream(server_pkcs8_key)); System.out.println(server_pkcs8 privateKey ); }使用 bouncycastle 如何 加载默认 openssl 加密的server.key?public static PrivateKey loadEncryptedPrivateKeyFromPem(String pemFile, char[] password) throws Exception { try (Reader reader new FileReader(pemFile); PEMParser pemParser new PEMParser(reader)) { Object object pemParser.readObject(); JcaPEMKeyConverter converter new JcaPEMKeyConverter().setProvider(BC); if (object instanceof PEMEncryptedKeyPair) { PEMEncryptedKeyPair encryptedKeyPair (PEMEncryptedKeyPair) object; InputDecryptorProvider decryptorProvider new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password); KeyPair kp converter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorProvider)); return kp.getPrivate(); } else { throw new IllegalArgumentException(Unsupported encrypted key type: object.getClass().getName()); } } }单向验证的TLS客户端单向了验证服务端服务端public static void main(String[] args) throws Exception { NioEventLoopGroup boosGroup new NioEventLoopGroup(); final ServerBootstrap serverBootstrap new ServerBootstrap(); int port 19977; // 1. 加载 PEM 私钥, 2. 加载 证书 final String server_crt E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server.crt; final String server_pkcs8_key E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server_pkcs8.key; Certificate certificate SecureUtil.readX509Certificate(FileUtil.getInputStream(server_crt)); PrivateKey privateKey PemUtil.readPemPrivateKey(FileUtil.getInputStream(server_pkcs8_key)); // 3. 创建 KeyStore 并将私钥和证书添加到其中 KeyStore keyStore KeyStore.getInstance(PKCS12); keyStore.load(null, null); keyStore.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[]{certificate}); // 4. 创建 KeyManagerFactory 并初始化 KeyManagerFactory kmf KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, password.toCharArray()); // 5. 构建 SslContext final SslContext sslContext SslContextBuilder.forServer(kmf) .build(); serverBootstrap .group(boosGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializerNioSocketChannel() { Override protected void initChannel(NioSocketChannel ch) { // 添加 SSL/TLS 处理器 ch.pipeline().addLast(sslContext.newHandler(ch.alloc())); ch.pipeline().addLast(new StringDecoder()) .addLast(new StringEncoder()) .addLast(new ServerHandler()); } }); ChannelFuture f serverBootstrap.bind(port).addListener(future - { if (future.isSuccess()) { System.out.println(new Date() : 端口[ port ]绑定成功!); } else { System.err.println(端口[ port ]绑定失败!); } }); //下面会进行阻塞, 等待服务器连接关闭之后 main 方法退出, 程序结束; f.channel().closeFuture().sync(); }客户端public static void main(String[] args) throws Exception { Bootstrap bootstrap new Bootstrap(); NioEventLoopGroup group new NioEventLoopGroup(); String host 192.168.20.130; int port 19977; // 1. 加载 CA 证书 final String ca_cet E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\root_ca.crt; Certificate certificate SecureUtil.readX509Certificate(FileUtil.getInputStream(ca_cet)); // 2. 创建 KeyStore 并将 CA 证书添加到信任库中 KeyStore trustStore KeyStore.getInstance(JKS); trustStore.load(null, null); trustStore.setCertificateEntry(ca, certificate); // 3. 创建 TrustManagerFactory 并初始化 TrustManagerFactory tmf TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 4. 构建 SslContext SslContext build SslContextBuilder.forClient() .trustManager(tmf) .build(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000) .option(ChannelOption.SO_KEEPALIVE, true) .handler( new SimpleChannelInboundHandlerString() { Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(客户端接收: msg); } }) .connect(host, port).addListener(new ChannelFutureListener() { Override public void operationComplete(ChannelFuture future) throws Exception { future.channel().pipeline() .addFirst(new StringEncoder()) .addFirst(new StringDecoder()) .addFirst( build.newHandler(future.channel().alloc() )); final String helloToServer 珠玉买歌笑糟糠养贤才。方知黄鹄举千里独徘徊。System.currentTimeMillis(); future.channel().writeAndFlush(helloToServer); } }); // group.shutdownGracefully(); }Android 上使用 java.security.KeyStore 时可能会遇到 KeyStoreException: JKS not found 的错误。这是因为 Android 的安全框架与标准 Java 不同Android 并不支持 JKSJava KeyStore格式。相反Android 支持其他类型的密钥库如 BKSBouncy Castle KeyStore和 PKCS12。private SslContext getSslContext() throws Exception { Certificate caCert CertificateUtils.getCertificate(); KeyStore trustStore KeyStore.getInstance(BKS); // 使用 BKS 或 PKCS12 trustStore.load(null, null); trustStore.setCertificateEntry(ca, caCert); TrustManagerFactory tmf TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); SslContext build SslContextBuilder.forClient() .trustManager(tmf) .build(); return build; }抓包看看双向验证的TLS其实同服务端一样的逻辑 1.生成客户端私钥, 2.生成客户端CRS, 3.用CA根证书签发客户端的公钥客户端证书准备生成客户端私钥openssl genpkey -algorithm RSA -out client.key -aes256 -pkeyopt rsa_keygen_bits:2048转换为pkcs#8 格式openssl pkcs8 -topk8 -inform PEM -in client.key -outform PEM -nocrypt -out client_pkcs8.key创建客户端的证书签名请求 (CSR)openssl req -new -key client.key -out client.csr使用根 CA 签名客户端的 CSRopenssl x509 -req -in client.csr -CA root_ca.crt -CAkey root_ca.key -CAcreateserial -out client.crt -days 7300 -sha256服务端public static void main(String[] args) throws Exception { NioEventLoopGroup boosGroup new NioEventLoopGroup(); final ServerBootstrap serverBootstrap new ServerBootstrap(); int port 19977; // 1. 加载 PEM 私钥, 2. 加载 证书 final String server_crt E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server.crt; final String server_pkcs8_key E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\server_pkcs8.key; Certificate certificate SecureUtil.readX509Certificate(FileUtil.getInputStream(server_crt)); PrivateKey privateKey PemUtil.readPemPrivateKey(FileUtil.getInputStream(server_pkcs8_key)); // 3. 创建 KeyStore 并将私钥和证书添加到其中 KeyStore keyStore KeyStore.getInstance(PKCS12); keyStore.load(null, null); keyStore.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[]{certificate}); // 4. 创建 KeyManagerFactory 并初始化 KeyManagerFactory kmf KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, password.toCharArray()); //双向认证 CA根证书 final String root_ca E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\root_ca.crt; X509Certificate caCertificate (X509Certificate) SecureUtil.readX509Certificate(FileUtil.getInputStream(root_ca)); // 5. 构建 SslContext final SslContext sslContext SslContextBuilder.forServer(kmf) .trustManager(caCertificate) .clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE)//双向认证 配置要求客户端也认证 .build(); serverBootstrap .group(boosGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializerNioSocketChannel() { Override protected void initChannel(NioSocketChannel ch) { // 添加 SSL/TLS 处理器 ch.pipeline().addLast(sslContext.newHandler(ch.alloc())); ch.pipeline().addLast(new StringDecoder()) .addLast(new StringEncoder()) .addLast(new ServerHandler()); } }); ChannelFuture f serverBootstrap.bind(port).addListener(future - { if (future.isSuccess()) { System.out.println(new Date() : 端口[ port ]绑定成功!); } else { System.err.println(端口[ port ]绑定失败!); } }); //下面会进行阻塞, 等待服务器连接关闭之后 main 方法退出, 程序结束; f.channel().closeFuture().sync(); }客户端public static void main(String[] args) throws Exception { Bootstrap bootstrap new Bootstrap(); NioEventLoopGroup group new NioEventLoopGroup(); String host 192.168.20.130; int port 19977; // 1. 加载 CA 证书 final String ca_cet E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\root_ca.crt; Certificate certificate SecureUtil.readX509Certificate(FileUtil.getInputStream(ca_cet)); // 2. 创建 KeyStore 并将 CA 证书添加到信任库中 KeyStore trustStore KeyStore.getInstance(JKS); trustStore.load(null, null); trustStore.setCertificateEntry(ca, certificate); // 3. 创建 TrustManagerFactory 并初始化 TrustManagerFactory tmf TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); // 双向认证 客户端证书 final String client_crt E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\client.crt; final String client_pkcs8_key E:\\content-for-work\\2024-12XXXXAPP\\tls_test\\client_pkcs8.key; // 4. 构建 SslContext SslContext build SslContextBuilder.forClient() .keyManager(FileUtil.file(client_crt), FileUtil.file(client_pkcs8_key)) .trustManager(tmf) .build(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000) .option(ChannelOption.SO_KEEPALIVE, true) .handler( new SimpleChannelInboundHandlerString() { Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(客户端接收: msg); } }) .connect(host, port).addListener(new ChannelFutureListener() { Override public void operationComplete(ChannelFuture future) throws Exception { future.channel().pipeline() .addFirst(new StringEncoder()) .addFirst(new StringDecoder()) .addFirst( build.newHandler(future.channel().alloc() )); final String helloToServer 珠玉买歌笑糟糠养贤才。方知黄鹄举千里独徘徊。System.currentTimeMillis(); future.channel().writeAndFlush(helloToServer); } }); // group.shutdownGracefully();

相关文章:

当今互联网安全的基石 - TLS/SSL

LS(Transport Layer Security)传输层安全协议 发展历程 TLS 是 SSL 协议的继任者。由于 SSL 协议存在一些安全漏洞,并且随着网络安全需求的不断提高,IETF(Internet Engineering Task Force)对 SSL 3.0 进…...

3步实现HTML到Word的智能转换:html-to-docx技术深度解析

3步实现HTML到Word的智能转换:html-to-docx技术深度解析 【免费下载链接】html-to-docx HTML to DOCX converter 项目地址: https://gitcode.com/gh_mirrors/ht/html-to-docx 你是否曾遇到过这样的场景?精心设计的网页报告需要转换为Word文档进行…...

PID控制在自动循迹小车中的实战应用与参数整定指南

PID控制在自动循迹小车中的实战应用与参数整定指南 当你在实验室里第一次看到自己设计的自动循迹小车歪歪扭扭地沿着黑线前进时,那种既兴奋又挫败的感觉一定记忆犹新。为什么理论上完美的PID算法,在实际应用中却总是出现超调、振荡或者响应迟缓&#xff…...

从‘过拟合’到‘稳如狗’:聊聊EEG情感识别中数据增强与噪声注入的那些坑

从‘过拟合’到‘稳如狗’:EEG情感识别中的数据增强与噪声注入实战指南 当你第一次看到训练集准确率突破95%的EEG情感识别模型,在实际测试中面对新用户时表现却像从未训练过一样糟糕,这种落差感想必每个从业者都深有体会。个体差异就像一把双…...

保姆级避坑指南:在Ubuntu 22.04上为ROS2 Humble编译OpenCV 4.2.0和cv_bridge

深度解析:Ubuntu 22.04下ROS2 Humble与OpenCV 4.2.0的精准版本匹配实战 当视觉SLAM遇上ROS2生态,版本依赖就像一场精密的外科手术。本文将带你穿透ORB-SLAM3等视觉算法与ROS2 Humble环境整合时的核心痛点——特别是OpenCV 4.2.0与cv_bridge的版本锁定机…...

嵌入式开发必备:三大代码对比工具深度评测

1. 代码对比工具概述作为一名嵌入式开发工程师,我每天都要处理大量的代码修改和版本对比工作。在多年的开发实践中,我发现选择合适的代码对比工具能极大提升工作效率。虽然Beyond Compare是业内公认的标杆产品,但实际工作中我们还有更多选择&…...

从拆解到驱动:手把手教你用IMX6ULL驱动OV5640摄像头模块(附完整代码)

从拆解到驱动:手把手教你用IMX6ULL驱动OV5640摄像头模块(附完整代码) 1. 硬件连接与接口解析 OV5640作为一款500万像素的CMOS图像传感器,支持DVP和MIPI两种接口模式。在IMX6ULL平台上,我们选择使用DVP并行接口进行连接…...

如何三步搞定iOS微信聊天记录完整导出:隐私保护与数据备份终极指南

如何三步搞定iOS微信聊天记录完整导出:隐私保护与数据备份终极指南 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 还在为无法永久保存重要微信对话而烦恼吗&…...

CSS 滚动驱动动画:让页面动起来的新维度

CSS 滚动驱动动画:让页面动起来的新维度代码如诗,滚动如歌。让我们用滚动驱动动画的魔法,为用户带来沉浸式的浏览体验。什么是滚动驱动动画? 滚动驱动动画(Scroll-driven Animations)是 CSS 中一项革命性的…...

在WinForm里玩转Halcon 3D点云:从C#代码导出到完整UI显示的保姆级避坑指南

在WinForm里玩转Halcon 3D点云:从C#代码导出到完整UI显示的保姆级避坑指南 当工业视觉项目需要处理复杂的三维场景时,Halcon的3D点云处理能力往往成为开发者的首选。但将Halcon的强大算法无缝集成到C# WinForm应用中,却可能遭遇一系列"…...

CSS 容器查询:组件级响应式设计

CSS 容器查询:组件级响应式设计代码如诗,容器如画。让我们用容器查询的强大能力,创建真正自适应的组件。什么是容器查询? 容器查询(Container Queries)是 CSS 中一项革命性的特性,它允许我们根据…...

网络安全的概念与规范:从基础到实践

网络安全的概念与规范:从基础到实践 在数字化浪潮席卷全球的今天,网络安全已成为国家安全的重要组成部分。本文将系统梳理网络安全的核心概念、发展历程、主要威胁、前沿趋势以及标准规范,帮助读者建立完整的网络安全知识体系。 一、网络安全…...

忍者像素绘卷多场景应用:微信小程序插图、游戏素材、社交配图一站式生成

忍者像素绘卷多场景应用:微信小程序插图、游戏素材、社交配图一站式生成 1. 像素艺术的新纪元 忍者像素绘卷是一款基于Z-Image-Turbo深度优化的图像生成工作站,它将传统像素艺术与现代AI技术完美结合。这款工具特别适合需要快速生成高质量像素风格图像…...

Android定时开关机的5种实现方式对比:哪种最适合你的设备?

Android定时开关机技术全景解析:从系统API到硬件层控制的深度实践 在智能设备管理领域,定时开关机功能一直是工业控制、物联网终端和定制化Android设备的核心需求之一。想象一下,你正在部署一批智能售货机,需要在营业时间自动唤醒…...

亚洲美女-造相Z-Turbo算力适配实践:24G显存下支持batch_size=2高清图并行生成

亚洲美女-造相Z-Turbo算力适配实践:24G显存下支持batch_size2高清图并行生成 1. 快速了解亚洲美女-造相Z-Turbo 亚洲美女-造相Z-Turbo是一个专门针对亚洲女性形象生成优化的文生图模型,基于Z-Image-Turbo的LoRA版本进行深度定制。这个模型最大的特点是…...

保姆级教程:在PX4 SITL仿真中为Iris无人机挂载Kinect、RPLidar和FPV摄像头

PX4仿真环境多传感器集成实战:从零搭建SLAM无人机开发平台 无人机仿真开发中最令人头疼的,莫过于将各类传感器完美集成到飞行平台上。我曾花了整整两周时间调试Kinect和RPLidar在Gazebo中的兼容性问题,直到找到这套经过验证的解决方案。本文将…...

GooglePlay多账号管理神器推荐:5款工具帮你轻松实现合规隔离(2025亲测有效)

GooglePlay多账号管理实战指南:2025年高效合规工具与策略 在移动应用生态中,Google Play作为全球最大的应用分发平台,其严格的账号管理政策让许多开发者感到头疼。特别是对于那些需要运营多个账号的开发者来说,如何在合规前提下实…...

火山引擎语音合成SDK实战:从快速调用到高级参数调优

1. 火山引擎语音合成SDK初体验 第一次接触火山引擎的语音合成SDK时,我正为一个智能客服项目发愁。客户要求系统能够用不同音色、不同情感的语音播报订单状态,而市面上大多数TTS服务要么太贵,要么效果生硬。直到同事推荐了火山引擎的解决方案&…...

内网穿透技术应用:在本地开发机部署Qwen3-ASR-0.6B并供外网测试

内网穿透技术应用:在本地开发机部署Qwen3-ASR-0.6B并供外网测试 你是不是也遇到过这种情况?在本地电脑上好不容易部署好了一个AI模型,比如Qwen3-ASR-0.6B这个语音识别模型,自己测试跑得挺欢。结果想给同事或者朋友演示一下&#…...

续航提升40%?EnergyStarX让Windows 11设备电量焦虑成为历史

续航提升40%?EnergyStarX让Windows 11设备电量焦虑成为历史 【免费下载链接】EnergyStarX 🔋 Improve your Windows 11 devices battery life. A WinUI 3 GUI for https://github.com/imbushuo/EnergyStar. 项目地址: https://gitcode.com/gh_mirrors/…...

保姆级教程:在Ubuntu 20.04上搞定Montreal Forced Aligner (MFA) 2.0安装与验证

保姆级教程:在Ubuntu 20.04上搞定Montreal Forced Aligner (MFA) 2.0安装与验证 语音对齐技术正在成为语音处理领域的基础工具,而Montreal Forced Aligner(MFA)作为当前最流行的开源解决方案,其2.0版本带来了显著的性…...

[STM32问题解决(2)]编译错误:Error: L6218E的深度解析与实战排查指南

1. 认识Error: L6218E编译错误 当你正在Keil MDK环境下开发STM32项目时,突然弹出一个红色错误提示:"Error: L6218E: Undefined symbol xxx (referred from xxx.o)",这可能是每个STM32开发者都会遇到的经典问题。我第一次遇到这个错…...

Adams导入SOLIDWORKS模型“隐身”难题:从Parasolid格式到视图显示的完整排查指南

1. 当你的模型在Adams中"隐身"了怎么办? 最近有个做机械仿真的朋友跟我吐槽,说他在SOLIDWORKS里精心设计的模型,导出为Parasolid格式后导入Adams,结果模型树里明明有显示,3D视图区却空空如也。这种"看…...

Excel也能搞定GRR!不用买昂贵软件,这份保姆级模板和计算指南请收好

Excel也能搞定GRR!不用买昂贵软件,这份保姆级模板和计算指南请收好 在制造业质量管理中,测量系统分析(MSA)是确保数据可靠性的基石。但现实情况是,许多中小企业和初创团队面对动辄上万元的专业统计软件只能…...

Reaxys没权限?试试这个国产化学数据库MolAid:免费注册+中文界面实操指南

Reaxys没权限?试试这个国产化学数据库MolAid:免费注册中文界面实操指南 在化学研究领域,获取高质量的化合物数据是实验设计和论文写作的基础。然而,许多国际知名数据库如Reaxys需要机构订阅才能使用,这让独立研究人员和…...

嵌入式开发者的效率利器:在VS Code里实时看到MISRA-C违规提示(含头文件路径配置避坑)

嵌入式开发实战:用VS Code打造MISRA-C实时检查工作流 每次保存代码后才发现MISRA-C违规有多痛苦?想象一下这样的场景:你正在编写一段关键的车载控制逻辑,反复调试后终于通过了编译,却在提交前的静态检查中被揪出二十多…...

酒精测试仪

简 介: 本文介绍了一款酒精测试仪的使用方法。测试仪开机后需等待15秒预热(数字倒计时),预热结束后对着吹气口吹气3秒即可显示测量结果。实验表明,该仪器灵敏度较高:直接吹气显示11左右,不吹气显…...

避开这些坑!FFmpeg.wasm在Vue项目中的完整避坑指南(含SharedArrayBuffer报错解决方案)

FFmpeg.wasm在Vue项目中的深度实践与疑难解析 当现代Web应用需要处理音视频编辑、转码或流媒体时,FFmpeg.wasm正成为前端开发者的利器。本文将深入探讨如何在高安全要求的Vue项目中稳定集成这一技术方案,特别针对生产环境中可能遇到的SharedArrayBuffer限…...

英雄联盟智能工具League Akari:从效率提升到战术优化的全方位解决方案

英雄联盟智能工具League Akari:从效率提升到战术优化的全方位解决方案 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 你是否曾在英…...

别再被空白页坑了!用html2canvas + print-js打印Vue/React组件,保姆级避坑指南

彻底解决前端组件打印难题:html2canvas与print-js深度整合实践 在管理后台、数据报表等企业级应用中,精确打印特定组件是刚需,但现代前端框架的组件化特性让这个"简单需求"变得异常棘手。当你的Vue/React组件在屏幕上完美呈现&…...