16 September 2017

为什么使用 HTTPS ?

随着网络逐渐融入到我们的日常生活,越来越多的人开始关注个人信息的安全性。由于现有的网络访问通常是基于 HTTP 协议进行传递的,因此 HTTP 协议的安全与否直接决定了我们个人信息的安全性。

HTTP 协议在通信过程中的三个缺点:

  • 信息采用明文传输,无法保证隐私性
  • 信息易被篡改,无法保证其完整性
  • 无法验证通信双方的身份

为了解决 HTTP 自身的不足,推出了 HTTPS

HTTPS = HTTP + 加密 + 认证 + 完整性保护

加密算法

加密算法分为对称加密算法和非对称加密算法。对称加密只有一个密钥,同时可用来加密和解密使用;非对称加密则有两个密钥:公钥和私钥,当公钥用于加密,私钥用于解密时,可以确保与通信方的安全;当私钥用于加密,公钥用于解密时,可以确保消息来自可信任的指定通信方 Public Key Cryptography

为了保证在通信前,双方能够安全的获取密钥,HTTPS 首先采用非对称加密算法,服务端发送其公钥给客户端,自己保留私钥,客户端使用服务端的公钥加密信息,并将信息发送给服务端,服务端接收到信息后使用私钥进行解密,从而保证了信息在网络中传输的安全性。但非对称加密相比于对称加密会消耗更多的时间,占用更多地资源,因此为了提高数据传输的效率,只在连接建立时时采用非对称加密算法,数据发送时采用对称加密算法,即客户端获取服务端发来的公钥之后,使用公钥加密一个随机密钥,并发送给服务端,接着双方使用该随机密钥进行通信。

认证

认证过程主要通过可信任的第三方结构颁发的证书来确认身份,如 CA 颁发证书给指定的服务器,客户端(如,浏览器或手机)中提前内置了可信任的 CA 颁发的公钥,当客户端访问支持 HTTPS 的服务端时,会首先根据公钥验证服务端证书的合法性,如果验证通过,则证明该服务器是合法的服务器。(2011.7 DigiNotar 曾被黑客入侵,并发布了伪造的可信任证书)

证书遵循 X.509 specification,包含如以下内容:

  • 服务端的公钥
  • 服务端的名称
  • 证书发起方的名臣
  • ……

证书内容通过私钥签名后分发给客户端,客户端通过公钥验证证书内容的合法性。

信息完整性

通过对消息进行哈希计算得到消息摘要,将消息摘要加密后得到签名信息,将签名存储到消息体中一并发送给服务器,服务器接收到消息后,对签名进行解密得到消息摘要,将得到的消息内容再次进行哈希运算,将计算结果与解密得到的消息摘要做对比,检验消息是否在传输过程中被篡改。

为什么使用 SSL Pinning ?

虽然 HTTPS 采用了公钥和证书的加密和验证方式,但依然存在 MIM(中间人攻击)的可能性,因为证书颁发机构可能被黑客入侵,而 HTTPS 只验证了证书的合法性,并没有验证当前服务器是否是我们要访问的服务器

为了保证我们当前访问的服务器就是我们需要访问的服务器,需要通过 SSL Pinning 的方式来实现,其包括 Certificate Pinning 和 Public Key Pinning 两种。

证书锁定需要把服务器的证书提前下载并内置到客户端中,当请求发起时,通过比对证书内容来确定连接的合法性,但由于证书存在过期时间,因此当服务器端证书更换时,需同时更换客户端证书。

公钥锁定则需提取证书中的公钥内置到客户端中,通过比对公钥值来验证连接的合法性,由于证书更换依然可以保证公钥一致,所以公钥锁定不存在客户端频繁更换证书的问题。

总结

  • 加密保证通信过程是安全的,但无法确认通信方是否可信任
  • 证书验证了通信方可信任,当无法确认是指定的通信方
  • Pinning 保证通信方即是希望通信的通信方

应用实践

以 Xamarin 结合 HttpClient,验证 www.baidu.com 为例:

首先在终端中输入命令获取 www.baidu.com 的公钥

openssl s_client -connect www.baidu.com:443 | openssl x509 -pubkey -noout

得到 Base64 编码后的 PUBLIC KEY

—–BEGIN PUBLIC KEY—–
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAyJkPC0Lev6L0shNY3OTO
……
—–END PUBLIC KEY—–

拷贝 Begin/End 之间的字符串并保存到 PublicKey 变量中,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 除外,由于该串字符表示加密算法(1.2.840.113549.1.1.1)可通过 GetKeyAlgorithm()可获取,而 GetPublicKey() 获取除算法标识外的公钥。

接着在 AppDelegateFinishedLaunching 方法中设置如下

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
    byte[] keyBytes = certificate.GetPublicKey();
    string remotePublicKey = Convert.ToBase64String(keyBytes);

    return PublicKey == remotePublicKey;
};

附:

生成 .cer 文件

openssl s_client -connect www.example.com:443 -servername www.baidu.com < /dev/null | openssl x509 -outform DER > server.cer

转换成 .pem 格式

openssl x509 -in server.cer -inform DER -out server.pem -outform PEM

hash -> base64

openssl s_client -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl dgst -sha256 -binary | openssl enc -base64

Pinning Cheat Sheet
SSL Certificate Pinning in mobile applications
Certificate and Public Key Pinning with Xamarin
Certificate Pinning
Increasing your trust certificate pinning on iOS Public-Key Cryptography and Certificates
RSA Key Formats
Transform Public Key Format
Creating Certificates for TLS Testing
openssl
Protecting your users with certificate pinning