加密和签名技术分析

栏目: 编程工具 · 发布时间: 5年前

内容简介:早在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),安全性非常高。但是只提供了 JavaPython 的 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算法用于数据校验。实际上微信开放平台就是用的这个方案。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Computing Patterns in Strings

Computing Patterns in Strings

Bill Smyth / Addison Wesley / 2003 / $ 75.00

The computation of patterns in strings is a fundamental requirement in many areas of science and information processing. The operation of a text editor, the lexical analysis of a computer program, the......一起来看看 《Computing Patterns in Strings》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具