以下是我在开发公司产品的时候遇到的问题,写下来备忘,有时间再整理格式;
加密和签名
我们的app和服务器有什么安全隐患?
1)窃听;如果你连接了不安全的免费wifi,然后登录我们的app,
如果你的数据没有加密,你的密码是不是就可能被盗?
2)篡改;万一黑客截获了app给服务器发送的请求数据,然后
稍作修改,发送给服务器,实现修改服务器数据的目的;
3)重放;万一黑客截获了app给服务器发送的请求数据,(哪怕是
加密过的),然后冒充app向服务器发送重复的请求,服务器
会不会被堵死?
4)如果黑客反编译app,修改逻辑(比如放松一些前端的检查规则),
重新打包运行(运行在黑客自己的手机上,不需要重新签名),
由于没有动加密和签名机制,所以在server看来,请求完全来自
可信的app,咋办?
怎么解决?
针对问题1,我们的网络通信数据需要加密,这样黑客
就看不到真实数据了;
针对问题2,我们需要给数据加签名,如果黑客截获并
修改了数据,则服务器会发现解密后的数据和
原数据不匹配,知道这是一份被修改过的数据,拒绝处理;
针对问题3,据文献,https能防止重放攻击,但是我还没搞懂原理,TODO
针对问题4:所以说只在前端加限制是不够的,后端必须再做一次
检查,这样就没事了;
问题:我们现有的加密机制有什么问题?
答:我们现在用的是AES对称加密,黑客可以反编译代码,
看到算法和密钥,然后截获并破解密文;
问题:我们现有的签名机制有什么问题?
答:我们现在用的是MD5+密钥的算法,黑客可以反编译app获取算法
和密钥,然后冒充app发起请求;
问题:RSA密钥生成算法是怎样的?
答:
1)随机选取2个足够大的不同素数p和q;
(最好保证下文的n是1024位二进制长度)
2)计算p和q的乘积n;
3)计算n的欧拉函数r = (p-1)*(q-1);
4)随机取一个整数e,要求1 < e < r,且e与r互质;
5)计算e对于n的模反元素d;
6)将(n,e)作为公钥,(n,d)作为私钥;(其实可以互换);
7)假设原数据是一个整数m,则加密结果为c=m^e%n;
8)解密过程是c^d%n,可以证明这个结果等于原数据m;
对于任意需要加密的字符串abc,我们可以将每个字母
分开,然后每个字母转成ASCII码,再对每个整数单独加密;
这个算法安全性来自于:别人无法根据(n,e)轻易推断出(n,d),
为了推出d,你需要得到r,一种方法是暴力计算,复杂度
很高;另一种方法是先推断p和q,这属于大数分解问题,
复杂度也很高;另外,不要忘了,e的选取不是唯一的,
d的值也不是唯一的,这又增加了破解的复杂度;
问题:如何生成我们想要的keyStore和trustStore?
答:请综合以下几个帖子和文章:
下面的帖子清晰的解释了ssl握手的过程,client预先植入公钥,所以
单验证证书这一步是不需要网络通信的,没有安全隐患;
http://stackoverflow.com/questions/188266/how-are-ssl-certificates-verified
下面的帖子清晰的说明了keyStore是什么,证书是什么,trustStore是什么;
Sever和client之间是如何建立信任的;
http://stackoverflow.com/questions/2200176/keytool-create-a-trusted-self-signed-certificate
下文清晰的解释了首先生成包含公钥私钥对的keyStore,
然后从keyStore抽出公钥,看后缀.cert估计这个公钥就证书,
最后用这个证书生成trustStore的过程;
https://developer.jboss.org/wiki/GeneratingSelfSignedCertificateWithKeytool?_sscc=t
问题:一般的认证签名的过程是怎样的?
答案:以google.com为例,
通常情况下,认证不是一步完成的,需要好几步,以google.com为例,
我们可以在浏览器看到他的证书,*.google.com是由
Google Internet Authority G2认证的,而G2又是由GeoTrust Global CA认证的,
而GeoTrust是知名的根认证组织;浏览器认证这个证书的过程是这样的,
首先这个证书里肯定有G2发给google.com的证书和G2的公钥,
这个证书是用G2的私钥签名的,浏览器用G2的公钥解密这个证书,
如果成功说明这个证书的确是G2发出的,下面,继续认证G2的有效性,
这个证书里肯定还包含GeoTrust发给G2的证书,这个证书是用GeoTrust
的私钥加密的,注意,浏览器预先植入了这些知名根认证组织的公钥,
而不能再使用证书本身包含的公钥了,因为证书里自带的公钥肯能是假的,
于是浏览器用内置的公钥解密这个证书,如果成功,说明这个证书的确是
GeoTrust发给G2的,也就验证了G2的真实身份;
考虑这个情况,如果黑客篡改了证书,把浏览器指向错误的认证路径会怎么样?
那浏览器最后会到达不认识的根认证组织,于是浏览器就会提示用户这个
证书有问题;
问题:什么是自认证签名(self-signed)?
这是上面情况的一种特例,就是server A的证书是A自己发布的,
并且client用A自己的公钥来认证;我们app需要的就是这种证书,
之所以选择这种证书是因为,不需要拿到其他认证机构去认证,
省时省力省钱,而我们的server只供自己的app访问,
所以只要我们的app认可这个证书就足够了;
问题:我们在解决重复提交问题的时候引入了一个requestCode,
这个和token很像啊,他们到底有什么区别?
答:requestCode是客户端生成的,用来标识一次请求意图;
而token是服务器生成的,用来标识一个用户的有效登录;
顺便提下,我们原来的防重复提交方案过于复杂,完全可以采用更
简单的方案;所有的请求无外乎add, update, delete,对于add,
只需要前端做点防止重复点击的保护就行了,每次点击记录一个
时间,然后在2s之内,禁止用户再次点击,如果用户在间隔很久
之后又点了一下,造成新建了多个订单,这种情况用户完全可以
理解;如果网络超时造成用户误判又点了一下,生成多个订单,
淘宝都有这个问题,用户可以删除多余的订单嘛,也没有多大危害;
对于update和delete,后台本来就有责任验证状态,服务器永远
都不能相信客户端的数据,只要服务端做了验证,重复提交就不会
造成任何影响了;所以说,requestCode完全是多余的设计!
问题:如果server要验证app的身份,则必须在app端存放证书,
App端如何安全的存储私钥?
答:app可以被反编译,理论上总是能被看到,没有什么好办法;
问题:如果用了okHttp/AsyncHttpClient等第三方库,怎么使用https?
答:这些库对https都有支持,我google过了,有现成的解决方案;
问题:SSL握手过程是怎么样的?
答:见下图
问题:SSL为什么使用RSA和AES的混合方案?
答:首先如果单纯使用AES,则server和app是共享一个密钥的,
黑客可以反编译app获得这个密钥,所以这个方案不可行;
其次,RSA算法对被加密的数据有长度要求,假设密钥是128位
,则只能加密不超过128位长度的数据,实际的json数据很
可能超过密钥长度;另外,RSA算法相比对称加密算法DES
慢很多;所以单纯使用RSA也不可取;
所以SSL采用了混合方案,动态生成随机的DES密钥,
用RSA加密传输DES密钥,之后用这个DES密钥加密数据。
问题:我们原来设计的token有什么用?用了https还需要
token吗?
答:为了支持单点登录,因为https不关心业务逻辑,所以单点
登录需要我们自己用token实现;用户id只能区分不同的用户,
无法区分不同的设备,另外用户id是永远有效的,没有
“有效期”的概念;所以我们引入了token,一次成功的登录
换取一个token,之后app用这个token去请求其他接口,
直到用在其他设备重新登录,此token失效;
问题:https能防止重放攻击吗?
网上说能,具体什么原理还没搞清楚;TODO
请参考 Analysis of the SSL 3.0 protocol
https://www.schneier.com/academic/paperfiles/paper-ssl.pdf
问题:说了这么多,具体怎么编程?
答:看了这么多,这篇文章看似最靠谱,代码短小精悍,
作者是square前CTO;
Android: Trusting SSL certificates
http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
注意,我看了很多帖子发现,Java默认支持JKS(Java Key Store)证书格式,
而Android支持BKS(BouncyCastle Key Store )格式,不支持JKS格式;
下文补充了host verification的部分:
Android: Trusting SSL certificates
http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/
问题:Base64是什么鬼,和加密有什么关系?
答:请自行百度,大概意思是:
现有的网络协议会将不可见字符删除,导致你的数据发生变化,
所以我们需要将数据全部变成可见字符,然后再传输;
Base64的确让数据变的不可读了,但是这个过程是可逆的,
所以不能算加密;
问题:总感觉加密和签名有某种搞不清的关系?
答:你的感觉是对的,他们的确有很密切的联系;
首先,RSA算法既能用来加密(用公钥加密,用私钥解密),
也能用来签名(用私钥加密,用公钥解密);
问题:这儿谈的签名和apk签名有什么关系?
答:数据签名是为了验证server或者app的身份;
Apk签名是为了验证apk的真实来源;
签名算法是一样的,但是应用的对象不一样;
结论:我们担心的问题,https早已给出了解决方案,https是Netscape
公司在1994年发明的,早就成为业界标准了;
参考文献:
1) 《改变未来的九大算法》,这本书里有2个章节专门描述了加密和
签名,深入浅出,建议作为入门的第一篇文章读;
http://blog.csdn.net/clh604/article/details/22179907
上文介绍了证书的创建和认证算法
http://littlesvr.ca/grumble/2014/07/21/android-programming-connect-to-an-https-server-with-self-signed-certificate/
5)数字签名原理剖析
http://blog.csdn.net/turkeyzhou/article/details/4668045
6)two-way SSL
https://gist.github.com/jorgevila/4984072
http://www.ossmentor.com/2015/03/one-way-and-two-way-ssl-and-tls.html
8)HTTPS with Client Certificates on Android
http://chariotsolutions.com/blog/post/https-with-client-certificates-on/
https://github.com/rfreedman/android-ssl
中文版:http://www.oschina.net/translate/everything-you-ever-wanted-to-know-about-ssl
11)理解RSA算法的3篇好文章:
轻松学习RSA加密算法原理
http://blog.csdn.net/sunmenggmail/article/details/11994013
RSA算法原理(一)
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
RSA算法原理(二)
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
12)What Is SSL (Secure Sockets Layer) and What Are SSL Certificates?
https://www.digicert.com/ssl.htm
上文章清晰的解释了https链接建立的过程
13)Behind the Scenes of SSL Cryptography
https://www.digicert.com/ssl-cryptography.htm
上文强调了SSL同时使用了对称和非对称加密
14)Analysis of the SSL 3.0 protocol
https://www.schneier.com/academic/paperfiles/paper-ssl.pdf
上文回答了关于SSL防止重放攻击的问题
15)两篇系列文章,讲了证书的创建和双向认证;
Android - TLS/SSL Mutual Authentication
http://callistaenterprise.se/blogg/teknik/2011/11/24/android-tlsssl-mutual-authentication/
http://callistaenterprise.se/blogg/teknik/2011/11/24/creating-self-signed-certificates-for-use-on-android/
上文介绍了怎么创建自认证的keyStore和trustStore;