HTTPS原理以及Java实现
Posted 清箫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTPS原理以及Java实现相关的知识,希望对你有一定的参考价值。
HTTPS协议是HTTP协议和SSL协议的结合体,使用HTTPS发送数据意味着消息首先经过SSL加密,然后通过HTTP协议转发,最后再由接收方的SSL解密。
都知道SSL/TLS使用了非对称加密(RAS或DSA),但非对称加密是很复杂而且很慢的。所以在实际中,客户端拿到第三方CertificateAuthority提供的数字证书(包含公钥),解出公钥之后并不是直接用公钥对数据做非对称加密。而是利用公钥生成一个对称加密(AES或DES)的秘钥,然后在C/S交互过程中,用这个对称加密的秘钥对数据加密。服务端要解密,需要利用私钥解出对称加密使用的秘钥,然后用这个对称秘钥解密出明文。
SSL Messages
RSA流程:
1. 假设服务端使用的密码组是TLS_RSA_WITH_AES_128_CBC_SHA。
2. 服务端首先查询KeyManager,给客户端返回Certificate/public key。
3. 客户端接收Certificate/public key,然后用TrustManager验证Certificate。
4. 如果客户端接受该Certificate,则用SecureRandom生成一个随机字节码,并使用Public key对这个随机字节码加密,并作为真正加密用的对称秘钥嵌入Client Key Exchange消息中发送给服务端。
5. 服务端利用Private key解密之后,重新获得前一步生成的随机字节码(对称秘钥),然后用改对称秘钥解密数据。
DSA流程:
1. 假设服务端使用的密码组是TLS_DHE_DSS_WITH_AES_128_CBC_SHA。
2. 双方都使用KeyPairGenerator生成一个临时的DH 公私密钥对(需要用到KeyFactory和DHPublicKeySpec)。
3. 双方都创建一个KeyAgree对象,并用自己的DH私钥初始化。
4. 服务端发送ServerKeyExchange消息(附带服务端公钥),客户端发送ClientKeyExchange消息(附带客户端公钥)。两个公钥同时塞入KeyAgreement对象,然后KeyAgreement根据这两个公钥生成一个真正加密使用的Key(对称加密秘钥)。
5. 双方使用前一步生成的对称加密秘钥对加密数据相互交互。
DSA比RAS更安全是因为使用了四个秘钥,而且不需要公开的证书,所以使得破解双方数据更难。
核心对象和概念
Security Providers
假设SSL/TLS的实现是由JSSE提供的,那么这些Provides的注册信息可以通过java.security.Security的getProviders()方法获取。Provider只提供映射关系,比如SSLContext.TLS->com.foo.TLSImpl,当我们调用SSLContext.getInstance("TLS"),就会从Providers列表中寻找对应的Provider实现。
SSLScoket
SSLSocket支持使用SecureSockets Layer(SSL)或TransportLayer Security(TLS)协议创建安全Socket。
SSLSocket提供的保护包括:
- 完整性保护:通过加密的方式,防止中间有人篡改数据。
- 双方验证:比如SSL会提供Peer Authentication。
Handshaking
SSLSocket提供的保护通过一个密码组,以及SSL连接提供的加密算法来实现。在S/C交互之前,双方需要在密码组上达成一致才能相互交流信息,这个谈判过程就叫handshaking。
Handshaking可能通过三种方式触发:
- 直接调用startHandshake方法。
- 在SSLScoket上任何读写应用数据的尝试都会触发隐式的handshake。
- 调用getSession(),但没有有效的session的时,也会触发隐式的handshake。
SSLScokets被创建的时候是没有handshake的,只有当需要发送数据时才会做handshake。至于是谁先发起handshaking过程,取决于SSLSocket的模式。SSLSocket分client和server模式,一旦设置好模式,就不能再修改。
Handshake Failure
Handshake失败,如果通过TrustManager或HostnameVerifier解决不了,最可能的原因是服务端升级了TSL版本,这个时候你也需要升级客户端使用的TSL版本,一般修改SSLContext构造函数的算法参数即可。比如把TSL改成TSLv1.2。
密码组
有两套密码组:
- 支持的密码组:SSL实现支持的密码组,通过getSupportedCipherSuites()可以获取。
- 嵌入式密码组:这一组通过getEnabledCipherSuites()设置,通过getEnabledCipherSuites()获取。
默认只有支持仅验证服务端和提供机密性的密码组会被激活,其他密码组只有当S/C双方都同意使用未验证或不加密的方式交流才会被选择。
SSLContext
这个类是JSSE的核心,它将最终创建SSLSocket和SSLEngine。
SSLContext初始化需要两个回调类:KeyManager和TrustManager,这两个类负责C/S之间的Peer Authentication。所以,如果出现Peer Authentication异常,则可以判断是KeyManager或TrustManager出现了问题。
JSSE KeyManager
负责选择合适的凭证(Credential)发送给对方。常用的策略是使用KeyStore文件,并导入RSA或DSA加密的X509Certificate。当KeyStore文件被加载,KeyFactory负责从文件中解出公钥和私钥,CertificateFactory负责解出证书链。当需要凭证的时候,KeyManager只需查阅KeyStore就可以决定使用哪个凭证。
KeyStore
KeyStore一般用JDK中的keytool生成。Keytool使用RSA或DSA KeyPairGenerator生成一个秘钥对并连同新生成的证书一起存入KeyStore文件中。
JSSE TrustManager
负责验证接受到的凭证(Credential)。有多种方式可以验证接收到的凭证,比如使用CertPath对象,让JDK内置的PublicKey Infrastructure(PKI)框架来验证。(CertPath实现会创建一个Signature对象,并用来验证证书链上的签名)。
服务端启用SSL
以Tomcat为例,需要三步:创建keystore文件(保存了私钥和公钥);配置Tomcat SSL连接器;创建客户端需要使用的电子证书。
获去证书需要走以下几步:
- 服务端人员需要使用keystore生成一个Certificate Signing Requst(csr文件)。
- 服务端人员把CSR文件发送给一个合法的CA机构,然后由CA机构生成一个证书。
- 服务端人员把证书下下来,然后部署到服务器中。
- SSL基于信任链来验证站点证书的合法性,所以,你还要有一个CA的根证书,这个证书也需要从CA下载。
- 把从CA下载的根证书和你通过CSR文件获取的证书一起导入你的keystore。
具体步骤请参考:https://www.mulesoft.com/cn/tcat/tomcat-ssl
客户端通过SSL协议访问
客户端往服务端发送加密数据,首先得需要获得公钥,这个公钥在保存在CA提供的证书中,所以客户端首先得下载一个证书文件(一般是用户代理自动下载)。
- 客户端使用证书时,SSL会发送一个请求到证书中所指定的CA再次确认你的证书确实是从CA获取的,而不是自己创建的。
- 客户端还可以验证服务端是否确实是自己调用的服务端。这两步构成了Peer Authentication。
以上是关于HTTPS原理以及Java实现的主要内容,如果未能解决你的问题,请参考以下文章
Java多线程系列:线程池的实现原理优点与风险以及四种线程池实现