【加密与解密】【07】SSL安全套件全解析

SSL/TLS协议

SSL,Secure Socket Layer,安全套接层

TLS,Transport Layer Security,传输层安全协议

TLS是SSL的最终完善版本,一般也可称为SSL协议

SSL是负责传输层安全,确定传输层数据如何封装的一套协议

SSL协议阶段
  • 算法协商
  • 数字证书验证
  • 数据通信
SSL协议核心内容
  • 事先准备:

  • 服务端生成自己的私钥和公钥

  • 服务端向CA机构申请SSL证书

  • CA向服务器颁发数字证书,该证书通过CA私钥加密,包含了服务端域名和公钥,用于证明服务端身份

  • 算法协商阶段:

  • 客户端和服务端分别生成随机数RNC和RNS,连同SSL算法参数一起发送给对方

  • 数字证书验证阶段:

  • 服务端将自己的数据进行摘要,再通过私钥加密,生成数字签名

  • 服务端将数据+数字签名+数字证书,一同发往客户端

  • 客户端通过CA公钥解密数字证书,确认证书与域名一致后,拿到服务端公钥(保证了公钥未被篡改)

  • 客户端通过服务端公钥解开数字签名,得到数据摘要(保证了数据来自服务端)

  • 客户端对数据部分进行摘要,与数字签名中的摘要进行对比(保证了数据完整性)

  • 客户端生成随机数PMS(Pre Master Secret),通过服务端公钥加密,发给服务端

  • 服务端通过私钥解密出PMS

  • 双方根据RNC+RNS+PMS构建主密钥MS(Master Secret),完成对称秘钥协商

  • 数据通信阶段:

  • 服务端和客户端通过主密钥MS,加密解密通信数据

  • 双向认证:

  • 如果服务端也要求客户端持有SSL证书,则增加一步服务端验证客户端证书的过程

  • 秘钥协商算法:

  • 以上流程使用的是DH秘钥协商算法

  • 如果用的是RSA算法,则直接将PMS作为MS使用,并且由客户端发送给服务端

SSL协议应用
  • 最常见的是应用于Https协议
  • 此外也可应用于Socket和WebSocket
SSL核心类
  • KeyManager,用于管理自己的私钥和证书,多用于服务端
  • TrustManager,用于验证收到的证书是否可信,多用于客户端
  • 服务端也可以要求客户端也发送安全证书,即双向认证,此时双方都要同时设置KeyManager+TrustManager
  • KeyStore,秘钥仓库,用来存储私钥、公钥、证书等数据,可设置密码
  • TrustStore,KeyStore的一种,存储CA证书,用于验证服务端身份正确性,密码公开
  • KeyStore和TrustStore的区别,仅在于存储的内容不同,它们可以是同一个文件,但是不建议这么做
  • Java中比较常见的KeyStore文件格式是JKS,此外还有CRT、PEM、P12、KEY等格式,私钥和证书也可能分开保存
  • KeyManagerFactory,用于创建KeyManager,一般通过algorithm和keystore文件来初始化
  • TrustManagerFactory,用于创建TrustManager,一般通过algorithm和keystore文件来初始化
  • SSLContext,用于整体管理SSL相关事务,一般通过KeyManager和TrustManager来初始化
  • 当KeyManager和TrustManager未指定时,SSLContext会从系统已安装的SecurityProvider中,搜索合适的Provider来处理对应工作
  • SSLServerSocketFactory,用于创建带SSL功能的ServerSocket,可通过SSLContext创建
  • SSLSocketFactory,用于创建带SSL功能的ClientSocket,可通过SSLContext创建
  • 也可以不使用Factory和KeyStore,通过自定义的方式来创建KeyManager和TrustManager
  • SSLSessionContext,维护所有会话信息,可通过SSLContext.getSessionContext获得
  • SSLSession,可通过SSLSessionContext或SSLSocket来获得
  • SSLServerSocket可以接收来自多个客户端的连接,SSLSocket只能连接指定的服务器和端口
  • SSLEngine,用于实现SSL握手,通过SSLContext创建,一般不用自己去实现,属于偏内部的Class
通过KeyTool生成KeyStore

学习SSL首先得有用于测试的SSL证书库,所以我们先来看看如何生成KeyStore

private.keystore和public.truststore本质上都是JKS文件,只是为了区分作用而换了后缀

# 生成私钥证书,首个名称请输入域名或ip
keytool -genkeypair -alias alias -keyalg RSA -validity 365 -keystore private.keystore

# 生成公钥证书
keytool -export -alias alias -keystore private.keystore -rfc -file public.cer

# 公钥证书转为JKS格式
keytool -import -alias alias -file public.cer -keystore public.truststore
KeyStore格式转换

JKS是Java专用的秘钥仓库格式

不同平台和语言的对KeyStore的支持性和使用习惯并不一样

比如安卓就不支持JKS格式,安卓上的证书一般使用P12格式

我们可以通过KeyStore Explorer软件来转换

https://keystore-explorer.org/downloads.html
使用带SSL的TcpSocket
import java.io.*
import java.security.KeyStore
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLServerSocket
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManagerFactory

const val serverKeyStore = "resources/private.keystore"
const val clientKeyStore = "resources/public.truststore"
const val passphrase = "123456"

const val serverPort = 18001

fun main() {
    Thread(::launchServerSocket).start()
    Thread.sleep(500)
    Thread(::launchClientSocket).start()
}

fun launchServerSocket() {
    // load key manager from key store
    val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
    val keyStore = KeyStore.getInstance("JKS")
    keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
    keyManagerFactory.init(keyStore, passphrase.toCharArray())
    val keyManagers = keyManagerFactory.keyManagers
    // init ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(keyManagers, null, null)
    // create server socket
    val serverSocketFactory = context.serverSocketFactory
    val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
    serverSocket.needClientAuth = false
    // accept client session
    val socket = serverSocket.accept() as SSLSocket
    // communication with client
    while (true) {
        Thread.sleep(500)
        socket.getOutputStream().write("Hello World".encodeToByteArray())
        socket.getOutputStream().flush()
    }
}

fun launchClientSocket() {
    // load trust manager from trust store
    val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
    val trustStore = KeyStore.getInstance("JKS")
    trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
    trustManagerFactory.init(trustStore)
    val trustManagers = trustManagerFactory.trustManagers
    // init ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(null, trustManagers, null)
    // create client socket and auto connect
    val sslSocketFactory = context.socketFactory
    val socket = sslSocketFactory.createSocket("localhost", serverPort) as SSLSocket
    // communication with server
    val buffer = ByteArray(1024)
    while (true) {
        val len = socket.getInputStream().read(buffer)
        if (len > 0) {
            val message = String(buffer, 0, len)
            println("Client Received: $message")
        }
    }
}
使用带SSL的HttpServer

大多网络编程框架,都是默认支持Https协议的,但仅限于由CA机构颁发的可信任证书

对于人工签发,未经CA机构授权的自签名证书,必须由开发者自己去实现验证逻辑

import com.sun.net.httpserver.HttpsConfigurator
import com.sun.net.httpserver.HttpsServer
import java.io.*
import java.net.InetSocketAddress
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.security.KeyStore
import java.util.*
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory

const val serverKeyStore = "resources/private.keystore"
const val clientKeyStore = "resources/public.truststore"
const val passphrase = "123456"

const val serverPort = 18001

fun main() {
    Thread(::launchHttpServer).start()
    Thread.sleep(1500)
    Thread(::launchHttpClient).start()
}

fun launchHttpServer() {
    // load key manager from key store
    val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
    val keyStore = KeyStore.getInstance("JKS")
    keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
    keyManagerFactory.init(keyStore, passphrase.toCharArray())
    val keyManagers = keyManagerFactory.keyManagers
    // configure ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(keyManagers, null, null)
    // create https server
    val server = HttpsServer.create(InetSocketAddress(serverPort), 10)
    // configure https
    val configurator = HttpsConfigurator(context)
    server.httpsConfigurator = configurator
    // create service
    server.createContext("/home") { exchange ->
        val response = Date().toString().encodeToByteArray()
        exchange.sendResponseHeaders(200, response.size.toLong())
        exchange.responseBody.write(response)
        exchange.responseBody.close()
    }
    // start https server
    server.start()
}

fun launchHttpClient() {
    // load trust manager from trust store
    val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
    val trustStore = KeyStore.getInstance("JKS")
    trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
    trustManagerFactory.init(trustStore)
    val trustManagers = trustManagerFactory.trustManagers
    // init ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(null, trustManagers, null)
    // create http client
    val uri = URI.create("https://localhost:18001/home")
    val client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_1_1)
        .sslContext(context)
        .build()
    // send request
    val request = HttpRequest.newBuilder()
        .GET()
        .uri(uri)
        .build()
    // get response
    val response = client.send(request, HttpResponse.BodyHandlers.ofString())
    println(response.body())
}
使用带SSL的WebSocket

这里我们使用比较出名的Java-WebSocket库来实现WebSocket服务端和客户端功能

api("org.java-websocket:Java-WebSocket:1.5.1")

为WebSocketServer设置SSL

fun setServerSSL(server: WebSocketServer) {
    // load key manager from key store
    val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
    val keyStore = KeyStore.getInstance("JKS")
    keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
    keyManagerFactory.init(keyStore, passphrase.toCharArray())
    val keyManagers = keyManagerFactory.keyManagers
    // configure ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(keyManagers, null, null)
    // configure server ssl
    val websocketServerFactory = DefaultSSLWebSocketServerFactory(context)
    server.setWebSocketFactory(websocketServerFactory)
}

为WebSocketClient设置SSL

fun setClientSSL(client: WebSocketClient) {
    // load trust manager from trust store
    val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
    val trustStore = KeyStore.getInstance("JKS")
    trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
    trustManagerFactory.init(trustStore)
    val trustManagers = trustManagerFactory.trustManagers
    // configure ssl context
    val context = SSLContext.getInstance("TLS")
    context.init(null, trustManagers, null)
    // configure client ssl
    val sslSocketFactory = context.socketFactory
    client.setSocketFactory(sslSocketFactory)
}
在OkHttp中自定义KeyManager和TrustManager

以上案例,都是通过KeyStore来实现KeyManager和TrustManager的管理功能

现在我们不用KeyStore,通过自定义规则,来实现秘钥管理和证书验证功能

我们以OkHttp框架为例

import okhttp3.OkHttpClient
import java.security.cert.X509Certificate
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession
import javax.net.ssl.X509TrustManager

fun setOkHttpSSL(builder: OkHttpClient.Builder) {
    val trustManager = object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {}
        override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {}
        override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
    }
    val trustManagers = arrayOf(trustManager)
    val hostnameVerifier = HostnameVerifier { hostname: String, session: SSLSession -> true }
    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, trustManagers, null)
    val socketFactory = sslContext.socketFactory
    builder.sslSocketFactory(socketFactory, trustManager)
    builder.hostnameVerifier(hostnameVerifier)
}

现在我们用OkHttp来替换上面的HttpClient来访问服务器

fun launchHttpClient() {
    val url = "https://localhost:18001/home"
    // create okhttp client
    val builder = OkHttpClient.Builder()
    setOkHttpSSL(builder)
    val client = builder.build()
    // create request
    val request = Request.Builder()
        .url(url)
        .get()
        .build()
    // execute call
    val call = client.newCall(request)
    val response = call.execute()
    // print response
    val responseBody = response.body.string()
    println(responseBody)
}

这里我们为了演示,不让问题复杂化,只是简单地信任了所有的证书,并不能起到实际的安全作用

关于KeyManager,TrustManager,HostnameVerifier的正式用法,可以参考以下类的源码

SunX509KeyManagerImpl X509TrustManagerImpl OkHostnameVerifier

SSL证书格式
  • JKS,二进制格式存储,Java专属格式

    一般为私钥+证书+密码,或只有公钥的组合

    常用于Tomcat服务器

  • PEM,文本格式存储

    可保存私钥和证书,一般以BEGIN开头,END结尾,中间为BASE64编码字符串

    常用于Apache或Nginx服务器

  • CER/DER,二进制格式存储

    只能保存证书

    常用于Windows服务器

  • CRT,只是一个后缀名,可以是PEM编码,也可以是CER编码

    一般只用来保存证书,不存储私钥

    可以将私钥单独保存,以KEY作为后缀,来区分秘钥文件和证书文件

    KEY文件可以是PEM编码,也可以是CER编码

  • P12/PKCS12/PFX,二进制格式存储

    一般同时包含私钥和证书,有密码保护

    常用于Windows IIS服务器

  • CSR,证书请求文件

    这个不是证书,而是通过私钥向CA申请公钥的请求文件

  • 最后,要注意的是,文件后缀和证书格式之间没有必然关系,还是以文件内容的实际存储格式为准

SSL证书转换工具
  • OpenSSL
  • KeyTool
  • KeyStore Explorer
OpenSSL指令
  • genrsa,生成秘钥
  • req,创建自签名根证书,或生成证书请求文件
  • x509,查看,创建,或转换证书
  • -in,输入文件
  • -out,输出文件
  • -inform,输入文件格式
  • -outform,输出文件格式
  • -CA,指定根证书
  • -CAkey,指定根证书私钥
  • -CAserial,指定CA证书
  • -CAcreateserial,创建下级CA证书
OpenSSL制作证书
# 创建根证书
openssl req -new -x509 -days 365 -extensions v3_ca -keyout crt/ca.key -out crt/ca.crt

# 颁发服务端证书
openssl genrsa -out crt/server.key 2048
openssl req -out crt/server.csr -key crt/server.key -new
openssl x509 -req -in crt/server.csr -CA crt/ca.crt -CAkey crt/ca.key -CAcreateserial -out crt/server.crt -days 365

# 颁发客户端证书
openssl genrsa -out crt/client.key 2048
openssl req -out crt/client.csr -key crt/client.key -new
openssl x509 -req -in crt/client.csr -CA crt/ca.crt -CAkey crt/ca.key -CAcreateserial -out crt/client.crt -days 365

# 查看服务端证书
openssl x509 -in crt/server.crt -text -noout
End

到此为止,基本涵盖了Java SSL的所有核心内容,已经足以满足大家日常开发需要

更高阶的用法,可能在专业的领域才能用得到,希望大家遇到时,能勇于自己去研究!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/771793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Games101学习笔记 Lecture16 Ray Tracing 4 (Monte Carlo Path Tracing)

Lecture16 Ray Tracing 4 (Monte Carlo Path Tracing 一、蒙特卡洛积分 Monte Carlo Integration二、路径追踪 Path tracing1.Whitted-Style Ray Tracings Problems2.只考虑直接光照时3.考虑全局光照①考虑物体的反射光②俄罗斯轮盘赌 RR &#xff08;得到正确shade函数&#x…

精准畜牧业:多维传感监测及分析动物采食行为

全球畜牧业呈现出一个动态且复杂的挑战。近几十年来&#xff0c;它根据对动物产品需求的演变进行了适应&#xff0c;动物生产系统需要提高其效率和环境可持续性。在不同的畜牧系统中有效行动取决于科学技术的进步&#xff0c;这允许增加照顾动物健康和福祉的数量。精准畜牧业技…

JavaScript-WebAPI

文章目录 JS组成什么是 webApis 和APIDOM 简介document 对象 获取 DOM 对象利用css选择器来获取DOM元素选择指定css选择器的所有元素其他获取DOM元素方法&#xff08;了解&#xff09; 操作元素内容对象.innerText对象.innerHTML 操作元素属性操作元素常用属性操作元素样式属性…

pytorch中的contiguous()

官方文档&#xff1a;https://pytorch.org/docs/stable/generated/torch.Tensor.contiguous.html 其描述contiguous为&#xff1a; Returns a contiguous in memory tensor containing the same data as self tensor. If self tensor is already in the specified memory forma…

羊大师:羊奶养生,解锁健康之道的新密码

在探寻健康与养生的旅途中&#xff0c;我们总渴望找到那把开启健康之门的钥匙。而今&#xff0c;羊奶以其独特的营养价值和健康益处&#xff0c;正悄然成为那把解锁健康之道的新密码。 羊奶&#xff0c;自古以来便是自然赋予的珍贵礼物。它富含优质蛋白、多种维生素及矿物质&am…

pandas数据分析(6)

算数运算 和Numpy数组一样&#xff0c;DataFrame和Series也利用了向量化技术。例如&#xff1a; 不过pandas真正强大之初在于自动对齐机制&#xff1a;当对多个DataFrame使用算数运算符时&#xff0c;pandas会自动将它们按照列或行索引对齐。 结果DataFrame的索引和列是两个Da…

day02-统计数据

numpy统计学 1.求平均值[数组名.mean()/np.mean(数组名)] m1 np.arange(20).reshape((4,5))m1.mean() #9.5若想要求某一维的平均值&#xff0c;设置axis参数&#xff0c;多维数组元素指定&#xff1a; axis 0&#xff0c;将从上往下计算。axis 1&#xff0c;将从左往右计算…

VIM介绍

VIM&#xff08;Vi IMproved&#xff09;是一种高度可配置的文本编辑器&#xff0c;用于有效地创建和更改任何类型的文本。它是从 vi 编辑器发展而来的&#xff0c;后者最初是 UNIX 系统上的一个文本编辑器。VIM 以其键盘驱动的界面和强大的文本处理能力而闻名&#xff0c;是许…

拼接各列内容再分组统计

某个表格的第1列是人名&#xff0c;后面多列是此人某次采购的产品&#xff0c;一个人一次可以采购多个同样的产品&#xff0c;也可以多次采购。 ABCD1JohnAppleAppleOrange2PaulGrape3JohnPear4SteveLycheeGrape5JessicaApple 需要整理成交叉表&#xff0c;上表头是产品&…

透过 Go 语言探索 Linux 网络通信的本质

大家好&#xff0c;我是码农先森。 前言 各种编程语言百花齐放、百家争鸣&#xff0c;但是 “万变不离其中”。对于网络通信而言&#xff0c;每一种编程语言的实现方式都不一样&#xff1b;但其实&#xff0c;调用的底层逻辑都是一样的。linux 系统底层向上提供了统一的 Sock…

openlayers中区域掩膜的实现

概述 在前文完成了mapboxGL中区域掩膜的实现。近日有人问到说在openlayers中如何实现&#xff0c;本文就带大家看看如何在openlayers中实现区域掩膜。 实现效果 实现 1. 实现思路 在地图容器中添加一个canvas&#xff0c;设置其在map之上&#xff1b;监听map的postrender事…

Vue2-Vue Router前端路由实现思路

1.路由是什么&#xff1f; Router路由器&#xff1a;数据包转发设备&#xff0c;路由器通过转发数据包&#xff08;数据分组&#xff09;来实现网络互连 Route路由&#xff1a;数据分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程 | - 网络层 Distribute分发…

时空预测+特征分解!高性能!EMD-Transformer和Transformer多变量交通流量时空预测对比

时空预测特征分解&#xff01;高性能&#xff01;EMD-Transformer和Transformer多变量交通流量时空预测对比 目录 时空预测特征分解&#xff01;高性能&#xff01;EMD-Transformer和Transformer多变量交通流量时空预测对比效果一览基本介绍程序设计参考资料 效果一览 基本介绍…

顶级10大AI测试工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Oracle Database 23ai新特性:DB_DEVELOPER_ROLE角色

角色介绍 从 Oracle Database 23ai 开始&#xff0c;新角色“DB_DEVELOPER_ROLE”允许管理员快速分配开发人员为 Oracle 数据库设计、构建和部署应用程序所需的所有必要权限。&#xff08;包括构建数据模型所需的系统权限以及监视和调试应用程序所需的对象权限&#xff09;。通…

【数据结构】02.顺序表

一、顺序表的概念与结构 1.1线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0…

上位机图像处理和嵌入式模块部署(mcu项目1:实现协议)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 这种mcu的嵌入式模块理论上都是私有协议&#xff0c;因为上位机和下位机都是自己开发的&#xff0c;所以只需要自己保证上、下位机可以通讯上&…

ELK 企业实战7

ELKkafkafilebeat企业内部日志分析系统 1、组件介绍 1、Elasticsearch&#xff1a; 是一个基于Lucene的搜索服务器。提供搜集、分析、存储数据三大功能。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff…

[数据集][目标检测]刀具匕首持刀检测数据集VOC+YOLO格式8810张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;8810 标注数量(xml文件个数)&#xff1a;8810 标注数量(txt文件个数)&#xff1a;8810 标注…

sql查询练习

1.表的结构 课程表&#xff1a;课程编号cid&#xff0c;课程名称canme&#xff0c;老师tid&#xff0c; 教师表&#xff1a;教师tid&#xff0c;教师姓名tname 分数表&#xff1a;学生student_sid&#xff0c;课程 cours_id&#xff0c;&#xff0c;分数score 学生表&#xff…