HTTPS原理以及Java实现

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/woxueliuyun/article/details/78743418

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。

展开阅读全文

没有更多推荐了,返回首页