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

打印 上一主题 下一主题

主题 916|帖子 916|积分 2748

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文件,只是为了区分作用而换了后缀
  1. # 生成私钥证书,首个名称请输入域名或ip
  2. keytool -genkeypair -alias alias -keyalg RSA -validity 365 -keystore private.keystore
  3. # 生成公钥证书
  4. keytool -export -alias alias -keystore private.keystore -rfc -file public.cer
  5. # 公钥证书转为JKS格式
  6. keytool -import -alias alias -file public.cer -keystore public.truststore
复制代码
KeyStore格式转换

JKS是Java专用的秘钥仓库格式
差别平台和语言的对KeyStore的支持性和使用习惯并不一样
比如安卓就不支持JKS格式,安卓上的证书一样平常使用P12格式
我们可以通过KeyStore Explorer软件来转换
  1. https://keystore-explorer.org/downloads.html
复制代码
使用带SSL的TcpSocket

  1. import java.io.*
  2. import java.security.KeyStore
  3. import javax.net.ssl.KeyManagerFactory
  4. import javax.net.ssl.SSLContext
  5. import javax.net.ssl.SSLServerSocket
  6. import javax.net.ssl.SSLSocket
  7. import javax.net.ssl.TrustManagerFactory
  8. const val serverKeyStore = "resources/private.keystore"
  9. const val clientKeyStore = "resources/public.truststore"
  10. const val passphrase = "123456"
  11. const val serverPort = 18001
  12. fun main() {
  13.     Thread(::launchServerSocket).start()
  14.     Thread.sleep(500)
  15.     Thread(::launchClientSocket).start()
  16. }
  17. fun launchServerSocket() {
  18.     // load key manager from key store
  19.     val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
  20.     val keyStore = KeyStore.getInstance("JKS")
  21.     keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
  22.     keyManagerFactory.init(keyStore, passphrase.toCharArray())
  23.     val keyManagers = keyManagerFactory.keyManagers
  24.     // init ssl context
  25.     val context = SSLContext.getInstance("TLS")
  26.     context.init(keyManagers, null, null)
  27.     // create server socket
  28.     val serverSocketFactory = context.serverSocketFactory
  29.     val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
  30.     serverSocket.needClientAuth = false
  31.     // accept client session
  32.     val socket = serverSocket.accept() as SSLSocket
  33.     // communication with client
  34.     while (true) {
  35.         Thread.sleep(500)
  36.         socket.getOutputStream().write("Hello World".encodeToByteArray())
  37.         socket.getOutputStream().flush()
  38.     }
  39. }
  40. fun launchClientSocket() {
  41.     // load trust manager from trust store
  42.     val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
  43.     val trustStore = KeyStore.getInstance("JKS")
  44.     trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
  45.     trustManagerFactory.init(trustStore)
  46.     val trustManagers = trustManagerFactory.trustManagers
  47.     // init ssl context
  48.     val context = SSLContext.getInstance("TLS")
  49.     context.init(null, trustManagers, null)
  50.     // create client socket and auto connect
  51.     val sslSocketFactory = context.socketFactory
  52.     val socket = sslSocketFactory.createSocket("localhost", serverPort) as SSLSocket
  53.     // communication with server
  54.     val buffer = ByteArray(1024)
  55.     while (true) {
  56.         val len = socket.getInputStream().read(buffer)
  57.         if (len > 0) {
  58.             val message = String(buffer, 0, len)
  59.             println("Client Received: $message")
  60.         }
  61.     }
  62. }
复制代码
使用带SSL的HttpServer

大多网络编程框架,都是默认支持Https协议的,但仅限于由CA机构颁发的可信任证书
对于人工签发,未经CA机构授权的自署名证书,必须由开辟者自己去实现验证逻辑
  1. import com.sun.net.httpserver.HttpsConfigurator
  2. import com.sun.net.httpserver.HttpsServer
  3. import java.io.*
  4. import java.net.InetSocketAddress
  5. import java.net.URI
  6. import java.net.http.HttpClient
  7. import java.net.http.HttpRequest
  8. import java.net.http.HttpResponse
  9. import java.security.KeyStore
  10. import java.util.*
  11. import javax.net.ssl.KeyManagerFactory
  12. import javax.net.ssl.SSLContext
  13. import javax.net.ssl.TrustManagerFactory
  14. const val serverKeyStore = "resources/private.keystore"
  15. const val clientKeyStore = "resources/public.truststore"
  16. const val passphrase = "123456"
  17. const val serverPort = 18001
  18. fun main() {
  19.     Thread(::launchHttpServer).start()
  20.     Thread.sleep(1500)
  21.     Thread(::launchHttpClient).start()
  22. }
  23. fun launchHttpServer() {
  24.     // load key manager from key store
  25.     val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
  26.     val keyStore = KeyStore.getInstance("JKS")
  27.     keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
  28.     keyManagerFactory.init(keyStore, passphrase.toCharArray())
  29.     val keyManagers = keyManagerFactory.keyManagers
  30.     // configure ssl context
  31.     val context = SSLContext.getInstance("TLS")
  32.     context.init(keyManagers, null, null)
  33.     // create https server
  34.     val server = HttpsServer.create(InetSocketAddress(serverPort), 10)
  35.     // configure https
  36.     val configurator = HttpsConfigurator(context)
  37.     server.httpsConfigurator = configurator
  38.     // create service
  39.     server.createContext("/home") { exchange ->
  40.         val response = Date().toString().encodeToByteArray()
  41.         exchange.sendResponseHeaders(200, response.size.toLong())
  42.         exchange.responseBody.write(response)
  43.         exchange.responseBody.close()
  44.     }
  45.     // start https server
  46.     server.start()
  47. }
  48. fun launchHttpClient() {
  49.     // load trust manager from trust store
  50.     val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
  51.     val trustStore = KeyStore.getInstance("JKS")
  52.     trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
  53.     trustManagerFactory.init(trustStore)
  54.     val trustManagers = trustManagerFactory.trustManagers
  55.     // init ssl context
  56.     val context = SSLContext.getInstance("TLS")
  57.     context.init(null, trustManagers, null)
  58.     // create http client
  59.     val uri = URI.create("https://localhost:18001/home")
  60.     val client = HttpClient.newBuilder()
  61.         .version(HttpClient.Version.HTTP_1_1)
  62.         .sslContext(context)
  63.         .build()
  64.     // send request
  65.     val request = HttpRequest.newBuilder()
  66.         .GET()
  67.         .uri(uri)
  68.         .build()
  69.     // get response
  70.     val response = client.send(request, HttpResponse.BodyHandlers.ofString())
  71.     println(response.body())
  72. }
复制代码
使用带SSL的WebSocket

这里我们使用比力出名的Java-WebSocket库来实现WebSocket服务端和客户端功能
  1. api("org.java-websocket:Java-WebSocket:1.5.1")
复制代码
为WebSocketServer设置SSL
  1. fun setServerSSL(server: WebSocketServer) {
  2.     // load key manager from key store
  3.     val keyManagerFactory = KeyManagerFactory.getInstance("SunX509")
  4.     val keyStore = KeyStore.getInstance("JKS")
  5.     keyStore.load(FileInputStream(serverKeyStore), passphrase.toCharArray())
  6.     keyManagerFactory.init(keyStore, passphrase.toCharArray())
  7.     val keyManagers = keyManagerFactory.keyManagers
  8.     // configure ssl context
  9.     val context = SSLContext.getInstance("TLS")
  10.     context.init(keyManagers, null, null)
  11.     // configure server ssl
  12.     val websocketServerFactory = DefaultSSLWebSocketServerFactory(context)
  13.     server.setWebSocketFactory(websocketServerFactory)
  14. }
复制代码
为WebSocketClient设置SSL
  1. fun setClientSSL(client: WebSocketClient) {
  2.     // load trust manager from trust store
  3.     val trustManagerFactory = TrustManagerFactory.getInstance("SunX509")
  4.     val trustStore = KeyStore.getInstance("JKS")
  5.     trustStore.load(FileInputStream(clientKeyStore), passphrase.toCharArray())
  6.     trustManagerFactory.init(trustStore)
  7.     val trustManagers = trustManagerFactory.trustManagers
  8.     // configure ssl context
  9.     val context = SSLContext.getInstance("TLS")
  10.     context.init(null, trustManagers, null)
  11.     // configure client ssl
  12.     val sslSocketFactory = context.socketFactory
  13.     client.setSocketFactory(sslSocketFactory)
  14. }
复制代码
在OkHttp中自界说KeyManager和TrustManager

以上案例,都是通过KeyStore来实现KeyManager和TrustManager的管理功能
现在我们不用KeyStore,通过自界说规则,来实现秘钥管理和证书验证功能
我们以OkHttp框架为例
  1. import okhttp3.OkHttpClient
  2. import java.security.cert.X509Certificate
  3. import javax.net.ssl.HostnameVerifier
  4. import javax.net.ssl.SSLContext
  5. import javax.net.ssl.SSLSession
  6. import javax.net.ssl.X509TrustManager
  7. fun setOkHttpSSL(builder: OkHttpClient.Builder) {
  8.     val trustManager = object : X509TrustManager {
  9.         override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {}
  10.         override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {}
  11.         override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
  12.     }
  13.     val trustManagers = arrayOf(trustManager)
  14.     val hostnameVerifier = HostnameVerifier { hostname: String, session: SSLSession -> true }
  15.     val sslContext = SSLContext.getInstance("SSL")
  16.     sslContext.init(null, trustManagers, null)
  17.     val socketFactory = sslContext.socketFactory
  18.     builder.sslSocketFactory(socketFactory, trustManager)
  19.     builder.hostnameVerifier(hostnameVerifier)
  20. }
复制代码
现在我们用OkHttp来更换上面的HttpClient来访问服务器
  1. fun launchHttpClient() {
  2.     val url = "https://localhost:18001/home"
  3.     // create okhttp client
  4.     val builder = OkHttpClient.Builder()
  5.     setOkHttpSSL(builder)
  6.     val client = builder.build()
  7.     // create request
  8.     val request = Request.Builder()
  9.         .url(url)
  10.         .get()
  11.         .build()
  12.     // execute call
  13.     val call = client.newCall(request)
  14.     val response = call.execute()
  15.     // print response
  16.     val responseBody = response.body.string()
  17.     println(responseBody)
  18. }
复制代码
这里我们为了演示,不让问题复杂化,只是简单地信任了全部的证书,并不能起到现实的安全作用
关于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制作证书

  1. # 创建根证书
  2. openssl req -new -x509 -days 365 -extensions v3_ca -keyout crt/ca.key -out crt/ca.crt
  3. # 颁发服务端证书
  4. openssl genrsa -out crt/server.key 2048
  5. openssl req -out crt/server.csr -key crt/server.key -new
  6. openssl x509 -req -in crt/server.csr -CA crt/ca.crt -CAkey crt/ca.key -CAcreateserial -out crt/server.crt -days 365
  7. # 颁发客户端证书
  8. openssl genrsa -out crt/client.key 2048
  9. openssl req -out crt/client.csr -key crt/client.key -new
  10. openssl x509 -req -in crt/client.csr -CA crt/ca.crt -CAkey crt/ca.key -CAcreateserial -out crt/client.crt -days 365
  11. # 查看服务端证书
  12. openssl x509 -in crt/server.crt -text -noout
复制代码
End

到此为止,基本涵盖了Java SSL的全部核心内容,已经足以满足各人日常开辟需要
更高阶的用法,大概在专业的范畴才气用得到,希望各人遇到时,能勇于自己去研究!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莫张周刘王

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表