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的全部核心内容,已经足以满足各人日常开辟需要
更高阶的用法,大概在专业的范畴才气用得到,希望各人遇到时,能勇于自己去研究!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |