内容简介:早在2013年,我就设计了 开放平台,那时参考了 新浪开放平台 \腾讯\百度\淘宝\支付宝\豆瓣 开放平台,并研究了 OAUTH(1.0和2.0)最终第一版采用的OAUTH 1.0实现,第二版采用OAUTH 2.0实现。但是有一个疑问,当时没弄清楚,就是 “为什么 支付宝用的RSA加密和签名,而新浪、豆瓣等用的AES加密、SHA1签名?”
早在2013年,我就设计了 开放平台,那时参考了 新浪开放平台 \腾讯\百度\淘宝\支付宝\豆瓣 开放平台,并研究了 OAUTH(1.0和2.0)
最终第一版采用的OAUTH 1.0实现,第二版采用OAUTH 2.0实现。
但是有一个疑问,当时没弄清楚,就是 “为什么 支付宝用的RSA加密和签名,而新浪、豆瓣等用的AES加密、SHA1签名?”
现在,我又深入研究了一晚上,终于想明白了,下面从头说起。
一、关于加密算法
只谈AES算法和RSA算法,其他的都不讨论,比如DES,已经过时了。
问:AES和RSA算法的区别是什么,哪个安全性更高?
AES属于 对称加密算法,加密和解密用的密钥是一有的。
而RSA属于 非对称加密算,加密和解密过程使用不同的密钥。公钥即为公开的密钥,一般用作加密;私钥即为私有的密钥,一般用作解密。
从安全性角度比较,以目前的科技水平来看,RSA和AES都很难破解,可以认为是足够安全的。从算法角度来比较,AES 256 的安全性比 RSA 1024 的安全性还是要高得多,而且AES算法的计算速度比RSA要快得多。RSA太慢了,以至于不适合对比较大的数据进行加解密。
但从应用角度来看,RSA和AES各有各的用途,AES要向使用者公开密匙,是有很大安全隐患的。而RSA则只需要对使用者公开公钥,私钥不对外公开,但是由于RSA运算速度很慢,所以一般只用于签名等数据很少的情况。
AES算法,常用的两种模式:CBC 和 GCM。
据说从理论角度来看,GCM更有优势,应该是未来的主流趋势。而现在的主流,是CBC模式。
亚马逊云 AWS,默认采用的就是 AES_256_GCM (全称:ALG_AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384),安全性非常高。但是只提供了 Java 和 Python 的 client,而且我没搜到有其他语言的第三方实现。
而 AES_256_CBC,应用更为广泛,微信开放平台就是用的它,而且提供了 Java、 PHP 、Python、C#、C++ 五种语言的官方实现,而且第三方的实现还包括 Go 、NodeJS等。总之,这个算法实现较容易,各个语言对它的支持非常广,使用方便。
综上,我目前还是推荐 AES_256_CBC,我认为强度足够了,关键是方便。
AES算法,是对称加密算法,加密、解密共用一个密匙。
而RSA算法,是非对称加密算法,它有一对密匙:公钥用于加密、验签,私钥用于解密、加签。所以,RSA算法用于加解密时,可以把公钥直接公布出来,对方拿去加密,而私钥只有一份在自己手上,只有自己能解密,这个特性非常有用!本文后面会看到。
然而,遗憾的是,RSA算法只能用于很短的数据加密,以1024位key为例,最多只能加密127位数据。
二、关于签名算法
常见的签名算法是 MD5、SHA1、SHA256,其他的本文不谈。
所谓签名算法,就是可以根据数据,计算出一个长度固定的内容,只要数据有任何改变,算出来的值都不一样。
MD5算法安全性较弱,比较容易被暴力破解。但是MD5签名的速度很快,性能好。可以用于不是特别注重安全性的场景。
SHA1,可以作为MD5的替代者,它的性能也不错,而且几乎不会被破解。而SHA256,强度更高。
问题:消息摘要(MD-Message Digest,例如MD5、SHA256等)和消息认证(MA-Message Authentication,例如HMAC-MD5、HMAC-SHA1、HMAC-SHA256等)的区别是什么?
MD是用来防篡改的,而MA是用来核对身份的。
比如一个镜像文件,计算其MD5值,如果相同则没有被修改。但是如果进一步要求,这个镜像文件必须是从A网站下载的,这是就要使用MA进行认证,使用者根据key计算MA值,认证通过后就可以确认这个文件是从A网站下载的,而且没有被篡改。
再比如你和对方共享了一个密钥K,现在你要发消息给对方,既要保证消息没有被篡改,又要能证明信息确实是你本人发的,那么就把原信息和使用K计算的HMAC的值一起发过去。对方接到之后,使用自己手中的K把消息计算一下HMAC,如果和你发送的HMAC一致,那么可以认为这个消息既没有被篡改也没有冒充。
三、签名和加密结合的算法
常用的是 SHA1WithRSA、SHA256WithRSA,即 用SHA算法进行签名,再用RSA算法进行加密。最终得到的一个密文的签名。
四、常见应用案例 - 加密加签、防重放攻击
1、AES和RSA组合使用,发挥各自的特点
为了避免AES Key在网络上明文传输,先动态生成AES的 key,然后对数据进行 AES 加密。然后用 RSA 公钥加密 AES Key。最后将加密的数据和aesKey一起传给接收方。
接收方用RSA 私钥解密AES Key,然后用解密后的AES Key对数据进行AES解密。
2、AES和SHA组合使用,发挥各自的特点
与上面的RSA有所不同,但是利用RSA有异曲同工之妙。方法是,双方都保存同一个token(可以理解为密码),然后用token和所有参数一起利用SHA算法计算一个签名,接收方利用自己本地的相同的token和接收到的参数一起也计算一个签名,签名一致则代表数据没有被修改。为保证数据不明文传输,也可以用AES进行加密,但是AES的密匙需要事前配置好,双方都用同一个AES Key。
综上,不难看出这两种主流方式的区别。应该说特点不一样,各有优劣。AES+RSA方案,只需要客户端配置一个RSA公钥即可,用于加密AES Key。而AES+SHA方案,需要客户端配置aesKey和token。并且注意到,后者连签名一起做了,而前者只有加密,没有签名,如果要签名,则还需要引入SHA1-RSA算法,再提供一对公钥密钥。
3、两个方案的具体分析以及如何防重放攻击
AES和RSA:【发送方】操作步骤
1. 随机生成AES密匙(aesKey),用以对明文数据(clearData)加密,加密后的数据为encryptData。
2. 用接收方提供的RSA公钥(receiverPublicRsaKey),对AES密匙进行加密,得到encryptAesKey。
3. 用自己的RSA私钥(senderPrivateRsaKey),对密文数据(encryptData+encryptAesKey)进行签名,得到sign。
关键代码如下:
public
byte
[] encryptAndSign(
byte
[] clearData,
String encryptType,
byte
[] receiverPublicRsaKey,
String signType,
byte
[] senderPrivateRsaKey) {
if
(
null
!= receiverPublicRsaKey) {
// 加密
}
if
(
null
!= senderPrivateRsaKey) {
// 加签
}
return
clearData;
}
AES和RSA:【接收方】操作步骤
1. 用发送方提供的RSA公钥(senderPublicRsaKey),对接收密文数据(encryptData+encryptAesKey)进行验签,验签失败则结束。
2. 用自己的RSA私钥(receiverPrivateRsaKey),对encryptAesKey进行RSA解密,得到aesKey。
3. 用aesKey对密文数据encryptData进行AES解密,得到明文数据clearData。
关键代码如下:
public
byte
[] checkSignAndDecrypt(
byte
[] data,
String signType,
byte
[] senderPublicRsaKey,
byte
[] sign,
String encryptType,
byte
[] receiverPrivateRsaKey) {
if
(signType !=
null
) {
// 验签
}
if
(encryptType !=
null
) {
// 解密
}
return
null
;
}
关键之处: 应用端(接收方),要向服务端(发送方)提供 自己的appId和RSA解密公钥。
并且,要保存服务端(发送方)提供的RSA验签公钥。
为方便和易扩展,服务端(发送方)可以选择是否对数据进行加密和加签,
并且从数据上就可以判断,是否进行了加密和加签,以及加密和加签的类型。
所以,一个完整的数据组成如下(以JSON为例):
{data:"...", encryptType:"AES...", sign:"...", signType:"RSA..."}
其中,encryptType、sign和signType,都是非必须的。
注意到另外一种流行的签名方式:msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
这种签名方式的好处在于,它使用SHA1算法,这个算法和MD5类似,是公开的,不需要密匙。
另外注意,此处的token,其实是一个密码,是应用端(接收方)配置的,和appid一起配置的,
服务端(发送方)也知道这个密码,所以这个密码(Token)不会在网络上传输。
这样一来,黑客即使知道算法和传递的参数,但他不知道密码(Token),所以他生成的signature,在服务端无法通过校验。
另外,之所以加上timestamp 和 nonce,是为了能够防止重放攻击(Replay-Attack),但为此还得做特殊处理:
可选的实现方式是把每一次请求的Nonce保存到数据库,客户端再一次提交请求时将请求头中得Nonce与数据库中得数据作比较,
如果已存在该Nonce,则证明该请求有可能是恶意的。然而这种解决方案也有个问题,很有可能在两次正常的资源请求中,
产生的随机数是一样的,这样就造成正常的请求也被当成了攻击,随着数据库中保存的随机数不断增多,
这个问题就会变得很明显。所以,还需要加上另外一个参数Timestamp(时间戳)。
之所以把timestamp 和 nonce一并做SHA1,是防止被修改,
假如他修改了timestamp,他又没办法生成有效的signature,所以无法通过校验。
问题又来了,随着用户访问的增加,数据库中保存的nonce/timestamp/appid数据量会变得非常大。
对于这个问题,可选的解决方案是对数据设定一个“过期时间”,比如说在数据库中保存超过一天的数据将会被清除。
如果是这样的,攻击者可以等待一天后,再将拦截到的HTTP报文提交到服务器,这时候因为nonce/timestamp/appid数据已被服务器清除,请求将会被认为是有效的。
要解决这个问题,就需要给时间戳设置一个超时时间,比如说将时间戳与服务器当前时间比较,如果相差一天则认为该时间戳是无效的。
对比RSA签名方案,他们其实都要保存密码,RSA方案服务端(发送方)需要保存一个自己的RSA私钥以及所有应用端(接收方)的RSA公钥,
而SHA1方案,服务端(发送方)需要保存所有应用端(接收方)的token。但是,SHA1速度更快,
RSA验签其实是先把消息进行SHA1或者SHA256(以便控制长度,RSA加解密对长度有限制),然后再RSA解密,即SHA1WithRSA、SHA256WithRSA算法,故速度稍慢。
说到防重放攻击,RSA方案也是可以的,在RSA参数中,加上nonce和timestamp,并且一起做签名。
黑客无法篡改nonce和timestamp,但是又只能一次性使用,那就达到了防重放的目的。
总的来说,RSA方案缺点明显:担心私钥泄露,而且私钥改了会影响所有客户端数据签名,其优点就是不用保存客户端的密钥。
个人建议使用SHA方案,配置aesKey用于加密,配置token配合SHA算法用于数据校验。实际上微信开放平台就是用的这个方案。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- "简单"的加密签名
- 对称加密,非对称加密,数字签名,数字证书,SSL握手
- 一分钟了解加密钱包多重签名原理
- 非对称加密 Rsa 数字签名 Go 实战
- 非对称加密Rsa数字签名Go实战
- [译] 用 Golang 实现 RSA 加密和签名(有示例)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
开发高质量PHP框架与应用的实际案例解析
Sebastian Bergmann / 刘文瀚、刘海燕 / 清华大学出版社 / 2012-6 / 49.00元
PHP已经成为最受欢迎的编程语言之一,这使得用PHP创建高质量、易维护的应用程序和框架比以往受到更多的青睐。通过使用来自于知名公司的真实案例研究,《开发高质量PHP框架与应用的实际案例解析》为Web软件体系结构的不同层次介绍了规划、执行以及测试自动化方面的内容,并解释了这些公司如何测量和测试软件质量。《开发高质量PHP框架与应用的实际案例解析》作者Sebastian Bergmann、Stefan......一起来看看 《开发高质量PHP框架与应用的实际案例解析》 这本书的介绍吧!