通信过程的演进

https通信中的密码学

哈希
  • 哈希算法又称散列,它是一种将任意长度的数据转化为固定长度的算法
  • 哈希算法是不可逆的
  • 常见的哈希算法有 MD5 和 SHA1
对称加密
  • 对称加密指的是加密和解密使用相同一个密钥
  • 对称加密的优点是速度快,缺点是密钥管理不方便,必须共享密钥
  • 常见的对称加密算法有 DES、AES、Blowfish 等
非对称加密
  • 非对称加密指的是加密和解密使用不同的密钥,其中一个是公钥,另一个是私钥,公钥是公开的,私钥只有自己知道
  • 使用公钥加密的数据必须使用私钥解密,使用私钥加密的数据必须使用公钥解密
  • 公钥和私钥之间存在着某种联系,但是从公钥不能(或很难)推导出私钥
  • 非对称加密的缺点是速度慢,优点是密钥管理很方便
  • 常见的非对称加密算法有 RSA、ECC 等
数字证书

演进1

“client”->“server”:hi
“server”->“client”:hi,我是server
“client”->“server”:...

问题:client在接到消息后,并不能肯定这个消息就是由server回应的,某些“黑客”也可以冒充server发出这个消息。如何确定信息是由server发过来的呢?

演进2

解决1:只有服务器有私钥,只要能够确认对方有私钥,那么对方就是server。那么通信的过程可以改为如下:

“client”->“server”:hi
“server”->“client”:hi,我是server
“client”->“server”:请证明你是server
“server”->“client”:这是我的证明{XXX}[私钥|RSA]

为了证明自己,server用私钥加密数据,将数据和加密后的密文一起发给客户端;client接收到数据后,用公钥解密数据,如果发现是一致的,则证明是server。

问题:由于公钥是对外公开的,那么对私密信息,久起不到真正的私密处理。黑客可以用公钥解密查看数据。

演进3

解决2:引入对称加密来解决这个问题,在实际通信前由client端给出对称加密的方式,通过rsa给server。

“client”->“server”:hi
“server”->“client”:hi,我是server
“client”->“server”:请证明你是server
“server”->“client”:这是我的证明{XXX}[私钥|RSA]
“client”->“server”:{我们后面的通信过程,用对称加密来进行,这里是对称加密算法和密钥}[公钥|RSA] 
“server”->“client”:{OK,收到!}[密钥|对称加密算法]
“client”->“server”:{我的帐号是aaa,密码是123,把我的余额的信息发给我看看}[密钥|对称加密算法]
“server”->“client”:{你的余额是100元}[密钥|对称加密算法]

client在确认了server的身份后,client自己选择一个对称加密算法和一个密钥,把这个对称加密算法和密钥一起用公钥加密后发送给server。注意,由于对称加密算法和密钥是用公钥加密的,就算这个加密后的内容被“黑客”截获了,由于没有私钥,“黑客”也无从知道对称加密算法和密钥的内容。

由于是用公钥加密的,只有私钥能够解密,这样就可以保证只有服务器可以知道对称加密算法和密钥,而其它人不可能知道

RSA加密算法在这个通信过程中所起到的作用主要有两个:

  • 因为私钥只有“服务器”拥有,因此“客户”可以通过判断对方是否有私钥来判断对方是否是“服务器”。
  • 客户端通过RSA的掩护,安全的和服务器商量好一个对称加密算法和密钥来保证后面通信过程内容的安全。

问题:client端怎么知道公钥呢?问题的根源就在于,大家都可以生成公钥、私钥对,无法确认公钥对到底是谁的。

演进4

解决3:数字证书

“client”->“server”:hi
“server”->“client”:hi,我是server,这是我的数字证书
“client”->“server”:向我证明你就是服务器,这是{一个随机字符串}
“server”->“client”:{一个随机字符串}[私钥|RSA]

“客户”收到“服务器”的证书后,它会去验证这个数字证书到底是不是“服务器”的,数字证书有没有什么问题,数字证书如果检查没有问题,就说明数字证书中的公钥确实是“服务器”的。检查数字证书后,“客户”会发送一个随机的字符串给“服务器”用私钥去加密,服务器把加密的结果返回给“客户”,“客户”用公钥解密这个返回结果,如果解密结果与之前生成的随机字符串一致,那说明对方确实是私钥的持有者,或者说对方确实是“服务器”。

其他问题

【问题1】 通信过程中说到,在检查完证书后,“客户”发送一个随机的字符串给“服务器”去用私钥加密,以便判断对方是否真的持有私钥。但是有一个问题,“黑客”也可以发送一个字符串给“服务器”去加密并且得到加密后的内容,这样对于“服务器”来说是不安全的,因为黑客可以发送一些简单的有规律的字符串给“服务器”加密,从而寻找加密的规律,有可能威胁到私钥的安全。所以说,“服务器”随随便便用私钥去加密一个来路不明的字符串并把结果发送给对方是不安全的。

【解决】 每次收到“客户”发来的要加密的的字符串时,“服务器”并不是真正的加密这个字符串本身,而是把这个字符串进行一个hash计算,加密这个字符串的hash值(不加密原来的字符串)后发送给“客户”,“客户”收到后解密这个hash值并自己计算字符串的hash值然后进行对比是否一致。也就是说,“服务器”不直接加密收到的字符串,而是加密这个字符串的一个hash值,这样就避免了加密那些有规律的字符串,从而降低被破解的机率。“客户”自己发送的字符串,因此它自己可以计算字符串的hash值,然后再把“服务器”发送过来的加密的hash值和自己计算的进行对比,同样也能确定对方是否是“服务器”。

数字证书

一个证书包含下面的具体内容:

  • 证书的发布机构
  • 证书的有效期
  • 公钥
  • 证书所有者(Subject)
  • 签名所使用的算法
  • 指纹以及指纹算法
    • 这个是用来保证证书的完整性的,也就是说确保证书没有被修改过
    • 其原理就是在发布证书时,发布者根据指纹算法(一个hash算法)计算整个证书的hash值(指纹)并和证书放在一起,使用者在打开证书时,自己也根据指纹算法计算一下证书的hash值(指纹),如果和刚开始的值对得上,就说明证书没有被修改过,因为证书的内容被修改后,根据证书的内容计算的出的hash值(指纹)是会变化的。

怎么验证证书的合法性?

客户端接收到证书后,会读取证书中的Issuer(发布机构),比如为XX,然后会在系统中受信任的发布机构的证书中去找是否有XX的证书,如果没有找到,则代表证书有问题;如果找到,则从证书中找出公钥,然后对证书里的指纹用公钥和对应的指纹算法使用RSA公钥解密,再计算证书的指纹,如果计算的指纹和证书中的指纹一致,则代表证书没有被修改,证书的公钥也是合法的。

数字证书的颁发过程

java里的证书概念

在 Java 平台下,证书常常被存储在 KeyStore 文件中。KeyStore不仅可以存储数字证书,还可以存储密钥,存储在 KeyStore文件中的对象有三种类型:

  • Certificate:证书
  • PrivateKey:非堆成加密中的私钥
  • SecretKey:对称加密中的密钥

根据存储的类型的不同,以及存储的方式的不同,keyStore有几种不同的类型:

  • JKS
    • 是java特有的,不支持其他语言
    • 可以存储PrivateKey和Certificate,但是不能存储SecretKey
    • PrivateKey不能被java解析出来
    • java默认的使用方式
    • 实现类:sun.security.provider.JavaKeyStore
  • JECKS
    • 是JKS的升级版本,支持更多的算法
    • 支持Certificate、PrivateKey和SecretKey三种类型
    • 对PrivateKey提供了更强级别的保护,使用了Triple DES加密
    • 实现类:com.sun.crypto.provider.JceKeyStore
    • jdk 1.4开始支持
  • PKCS12
    • 支持多语言
    • 支持所有类型
    • jdk 9后的默认使用方式
    • 实现类:sun.security.pkcs12.PKCS12KeyStore
  • PKCS11
    • 硬件类型的keystore
    • 提供了java接口给java库
    • 支持所有类型
    • 当加载keystore时,数据被从keystore中取出,然后转成硬件所支持的类型
    • 实现类:sun.security.pkcs11.P11KeyStore
  • DKS
    • 即Domain KeyStore
    • 是keystore的keystore
    • jdk8 开始引入
    • 实现类:sun.security.provider.DomainKeyStore.java
  • BKS
    • 即BoucyCastle keystore
    • 专门为java第三方密码库BouncyCastle提供的的一种keystore格式
    • 类似JKS,但是可以存储所有类型
    • 更适合于移动端的开发
KeyStore 和 TrustStore

KeyStore其实只是一种文件格式而已,实际上在Java的世界里 KeyStore文件分成两种:KeyStore 和 TrustStore。

两个东西从文件格式来看其实是一样的。KeyStore 保存私钥,用来加解密或者为别人做签名;TrustStore 保存一些可信任的证书,访问 HTTPS 时对被访问者进行认证,以确保它是可信任的。

除了 KeyStore 和 TrustStore ,Java 里还有两个类 KeyManager 和 TrustManager 与此息息相关。

可以看出如果要进行 SSL 会话,必须得新建一个SSLSocket对象,而SSLSocket对象是通过SSLSocketFactory来管理的,SSLSocketFactory对象则依赖于SSLContext,SSLContext对象又依赖于keyManager、TrustManager和 SecureRandom。

自定义 TrustManager 绕过证书检查
public class ZTTrustManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
为什么Fiddler可以抓https的数据包?

Fiddler能抓取到https的数据,是因为Fiddler给客户端下发了自己的CA证书,自己则伪装成客户端和server完成通信。

证书是怎么下发给客户端的?

results matching ""

    No results matching ""