易信通过tcp连接与服务器通讯的数据流都是经过加密的,下面通过对代码的分析来理清整个流程(代码有删减)
bool LinkSocket::SendPacket(const LinkFrame &lf, const nbase::Marshallable &req)
{
if (!is_encrypt_) {
bool send = true;
if (lf.service_id_ != SVID_YIXIN_AUTH) {
nbase::DEFLOG(nbase::LogInterface::LV_WAR, __FILE__, __LINE__, "Warning: Packet Ignored %d - %d Before Being Encrypted", lf.service_id_,lf.command_id_);
send = false;
}
else {
SendEncryptPacket(lf, req);
}
return send;
}
link_sender_->send(lf, req);
return true;
}
所有数据包发送之前都要判断isencrypt标志,该标志表示是否跟服务器协商好加解密相关的参数,如果没有协商好,则调用SendEncryptPacket,执行加解密参数协商流程
void LinkSocket::SendEncryptPacket(const LinkFrame &lf, const nbase::Marshallable &req
{
//生成rc4秘钥
UTF8String rc4_key = GetRandomRC4Key(); // 生成对称加密密钥
in_chiper_->SetDecryptKey(rc4_key);
out_chiper_->SetEncryptKey(rc4_key);
nbase::PackBuffer rc4_key_buffer;
LinkPacker rc4_key_packet(rc4_key_buffer);
rc4_key_packet.push_varstr(rc4_key);
//pack下一次的请求
nbase::PackBuffer next_request_buffer;
LinkPacker next_request_packet(next_request_buffer);
next_request_packet << lf;
next_request_packet << req;
next_request_packet.endpack();
//RSA加密
std::string rc4_key_request_conent(rc4_key_packet.data(),rc4_key_packet.size());
std::string next_request_content(next_request_packet.data(),next_request_packet.size());
std::string raw_content = rc4_key_request_conent + next_request_content;
std::string encrypted_content;
UTF8String hex_module;
int version;
ReadRSAConfig(hex_module, version);
RSAEncrypt(raw_content, hex_module,encrypted_content);
GetSessionKeyReqeust key_request;
key_request.version_ = version;
key_request.encrpyted_content_ = encrypted_content;
LinkFrame session_key_lf(0x50,10);
link_sender_->send(session_key_lf, key_request);
}
从上面代码我们可以看到两种加密技术:对称加密和非对称加密。所谓对称加密就是数据发送接收双方加密解密用的是相同的密钥。由于对称加密技术加解密的速度相对于非对称加密有明显的优势,所以传输数据一般使用对称加密。因为对称加密的密钥是在易信客户端生成的,需要加密之后传给服务器端,对该密钥的加密使用了RSA非对称加密。在非对称加密体系中密钥分公钥和私钥,公钥和私钥都可以对明文进行加密,但只有私钥可以对密文进行解密。易信服务器端存了私钥,在客户端存了公钥,传输对称加密密钥的时候客户端使用公钥对其加密然后传输给服务器。当服务器端收到客户端发过来的密钥之后,双方就可以对传输的数据进行加解密。
综上所述易信客户端的加密流程如下:
对于客户端来说公钥被窃取了也没有多大的问题,但是保存在服务器的私钥被窃取了那整个加密系统就形同虚设了。必须要跟换私钥公钥,重新发布客户端让用户升级。
为了避免重新发布客户端并且强制用户升级,我们会想让服务器下发公钥。一旦让服务器下发公钥就会引入一个更大的安全隐患,那就是可能遭到中间人攻击(Man-in-the-middle attack)。我们用一个现实中的场景来解释中间人攻击:我们客户端连接到一个被黑客控制的路由器,所有的数据传输都被监控,服务器传输给客户端的公钥被篡改了,客户端拿到的不是服务器下发的公钥而是黑客生成的公钥(黑客拥有私钥),客户端使用假的公钥加密数据,黑客用手中的私钥解密密文然后,再用截获的真正的公钥加密数据传输给服务器。这么一来客户端和服务器端都能正确的加密解密,但传输的数据已经被出于中间的黑客掌握。
https采用SSL/TLS协议来保证数据传输的安全。使用https通讯之前客户端和服务器需要使用SSL/TLS协议来进行握手,步骤如下:
可以看到整个流程跟我们上节提到的服务器下发公钥的流程基本上是一样的,不同的是多了一个证书真伪验证的过程。
上图是支付宝下发给浏览器的证书,我们可以看看到最重要的三个域:
从中我们可以看到电子签名是需要第三方“可信赖”的机构签的(显然需要钱)。
电子签名也是非对称加密技术的应用,一般使用流程如下:
从上面流程可以看到要验证电子签名的真伪,需要一把签名机构给的公钥,这么一来问题又来了,这个公钥怎么传输?如果通过网络传输不又要被中间人攻击了吗?这把公钥是不通过网络传输来获取的,而是由浏览器厂商打包到浏览器中随着浏览器一起发布(一般浏览器会打包进差不多600多家签名机构的根证书),在验证签名的过程中浏览器会根据证书中的签发机构信息找到该机构的根证书,再利用该根证书中的公钥来验证签名。
看起来一切都很完美,无懈可击啊!但真的是这样吗?我们可以看到整个https的安全是建立在CAs和浏览器开发厂商之上的,假如某个CA机构被黑或者迫于政府的压力签了一个假冒的证书出来(假冒谷歌或者其他的……),又或者浏览器开发商迫于政府的压力对某些假冒的证书开绿灯。这些并不是异想天开的事情,针对https的攻击每天都在发生,参见[1][2][3][4]。
所以如果你在干秘密的事情,https并不一定可靠!
有什么机制来替换CAs,这值得思考。
引用:
[1] P. Eckersley, "How secure is HTTPS today? How often is it attacked?", Electronic Frontier Foundation, https://www.eff.org/deeplinks/2011/10/how-secure-https-today (6 December 2013)
[2] "Spooks break most Internet crypto, but how?", Ars Technica, (6 December 2013).
[3] "Iranian Man-in-the-Middle Attack Against Google Demonstrates Dangerous Weakness of Certificate Authorities", Electronic Frontier Foundation, (6 December 2013).
[4] "New NSA Leak Shows MITM Attacks Against Major Internet Services", Schneier on Security, https://www.schneier.com/blog/archives/2013/09/new_nsa_leak_sh.html
本文来自网易实践者社区,经作者徐晖授权发布。