赞
踩
SSL,Secure Socket Layer,安全套接层
TLS,Transport Layer Security,传输层安全协议
TLS是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首先得有用于测试的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
JKS是Java专用的秘钥仓库格式
不同平台和语言的对KeyStore的支持性和使用习惯并不一样
比如安卓就不支持JKS格式,安卓上的证书一般使用P12格式
我们可以通过KeyStore Explorer
软件来转换
https://keystore-explorer.org/downloads.html
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") } } }
大多网络编程框架,都是默认支持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()) }
这里我们使用比较出名的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)
}
以上案例,都是通过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
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申请公钥的请求文件
最后,要注意的是,文件后缀和证书格式之间没有必然关系,还是以文件内容的实际存储格式为准
genrsa
,生成秘钥req
,创建自签名根证书,或生成证书请求文件x509
,查看,创建,或转换证书-in
,输入文件-out
,输出文件-inform
,输入文件格式-outform
,输出文件格式-CA
,指定根证书-CAkey
,指定根证书私钥-CAserial
,指定CA证书-CAcreateserial
,创建下级CA证书# 创建根证书
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
到此为止,基本涵盖了Java SSL的所有核心内容,已经足以满足大家日常开发需要
更高阶的用法,可能在专业的领域才能用得到,希望大家遇到时,能勇于自己去研究!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。