先简单回顾下MAC
在前面的文章《消息验证码MAC入门指南》中,我们围绕消息验证码的工作原理进行了一些分析和实验。
在对消息进行MAC计算时,无论是使用HMAC亦或是CBC-MAC,它们都是基于"Encrypt-Then-MAC"的机制,其工程上的含义为:对明文加密生成密文,然后对密文进行MAC计算。
在现有的许多工程实践上,其实还存在另外两种MAC生成方式:
- MAC-Then-Encrypt 这种方式的使用,是先对明文进行MAC生成,然后将明文和MAC值一起进行加密,这种方式在早期的TLS中应用比较多。
- Encrypt-And-MAC 这种方式,也是首先对明文计算MAC值,只不过,在发送数据时,MAC值一直是保持明文的,明文的MAC值和对明文加密后的密文将会一起发送。早期的Secure Shell中就使用了这种方式。
这里多说一句,在针对消息不可篡改的使用目的上,工程学上其实更推荐"Encrypt-Then-MAC"的方式,本文不过多讨论关于这种方式安全性的算法论证,但是有一点是比较值得强调的,那就是,密码学领域的许多大佬专家都更倾向于认为,"Encrypt-Then-MAC"在避免直接修改密文的攻击手法上,效果会更好。
当然,在兼具不可篡改与保密性的加密选择上,AEAD(Authenticated Encryption With Additional Data)的方式是更值得推荐的,这一块后面有时间我们展开细聊,这篇文章先不过多探讨。
但是,无论MAC在算法实现上多么的优雅,MAC始终有一个很致命的问题,就是它需要共享密钥!
使用共享密钥意味着,一旦密钥泄露,数据的安全性将会极大的降低。
从MAC过渡到数字签名
相同的目的
与MAC的目的一样,数字签名的目的,其实也是为了验证消息来源真实性与消息不可篡改性。
消息来源真实性,在MAC中指的是,只有具有这把共享密钥的人,才可以验证通过。
消息不可篡改性,在MAC中指的是,一旦这个消息被篡改过,那么即使使用的是同一把共享密钥,最后计算出来的结果也一定具有可显式分辨的差异性。
回顾下非对称密钥的特性
在前面的文章《非对称密钥沉思系列(1):RSA专题之PKCSv1.5填充模式下的选择性密文攻击概述》中,我们探讨了非对称秘钥的一些特性,这里总结几个比较重要的性质:
- 非对称加密总是以密钥对的形式出现,而在对称加密中总是共享一般密钥。
- 公钥和私钥,总是一个加密,另一个解密,互为对立面。
- 在RSA中,公钥可以从私钥派生出来,而反过来不行。
- 私钥应该总是保持为私有,永远不能公开。
- 公钥应该允许广泛传播,允许被任何人持有。
总的来说,任何人都可以使用密钥对中的公钥进行消息加密,同时密文总是可以被密钥对中的私钥进行解密。
也就是说,生成密文消息的人,总是知道,只有拥有私钥的人才可以解密。
如果使用私钥加密呢?
私钥是可以加密的,也就是说,加密的方向,是可以反过来的。
并且,使用使用私钥加密的消息,也只有公钥能够解密,算法上这是没有问题的。
但是,这种加密方式,在工程上有什么意义呢???
前面我们提到了,公钥其实可以被任何持有,也就是说,任何持有公钥的人,都能解密由私钥加密的数据,这样对数据来说,其实其保密性相当于为零!!!
但是,想想对称密钥场景下,密文能被解密,则说明,解密者必定是持有共享密钥的那个人;
同样的道理,如果持有公钥的人可以解密被私钥加密的数据,那么说明,发送私钥加密密文的人,一定是持有私钥的人!!
也就是说:
如果你得到一条可以用你的公钥解密的消息,那么你一定能确定这条消息来自于对应的私钥持有者,因为其他任何人都不能加密它。
非对称加密的效率问题
这里其实就是要强调,非对称加解密是一个很慢的过程。
相比较于对称加密解密,非对称加解密的过程是一种相当低效的过程。
也就是说,在使用非对称加解密能力时,最好不要在数据量比较大的场景下使用。
诶???
这个时候有没有想到什么???
既然不能对原始大数据做加解密,那么,能否直接怼数据的哈希做加解密呢??
要记住,此时我们做加解密的目的,已经不是保密性,而是为了证明来源的真实性!!
RSA数字签名的基本思想
RSA数字签名,基本可以概括为以下几个步骤:
- 首先对数据进行哈希计算,得到原始数据的哈希值。
- 使用私钥对哈希值加密,此时得到的密文就是原始数据的签名。
- 将哈希密文与原始数据一起发送。
- 接收方接收到原始明文与哈希密文后,对原始明文同样计算哈希值,并使用公钥对哈希密文解密,对比两个哈希值是否一致,这个过程也叫做验签。
这里有必要再次强调,RSA的公钥加密与私钥加密,适用于不同的用途:
- 公钥加密,可以对消息保密,只有私钥所有者才能解密它
- 私钥加密,可以验证消息来源真实性,因为只有私钥持有者才能生成这个消息
在RSA工程化时间上,一般都建议:
- 使用RSA进行加密时,推荐使用OAEP的填充方式。
- 使用RSA进行签名时,推荐使用PSS的填充方式。
- PSS填充时,其掩码生成函数,有且仅有一个,那就是MGF1。
数字签名实践的决策点:先加密再签名 还是 先签名在加密?
在MAC的场景下,我们强调过,建议使用"Encrypt-Then-MAC"的方式,那么在RSA的签名场景下,我们是否也应该推荐先加密再签名的方式呢??
这里笔者的结论是,不建议使用先对称加密再签名的方式,而是应该先对明文签名,再对明文和签名做对称加密。
在做数字签名时,消息的完整性已经不仅仅是其目的,更重要的是,我们需要验证消息来源的真实性,也就是验证发送方的身份。
MAC场景下,其共享密钥,理论上只会被两方共享,持有第三方密钥的人无法替换由真实共享密钥生成的MAC值;
而在RSA数字签名的场景下,用于创建数字签名的私钥是不被共享的,而公钥确实公开且可以被任何人持有的;
公钥只能验签,但是不能伪造签名,先签名,后加密,可以保护签名人,因为在签名之后又做了一层加密,即使第三方截获了这段消息,也没办法知道是谁发送的。
而如果先加密,后签名,则这段消息如果被第三方拦截后,其可以修改这段密文并重新生成签名,此时虽然签名用的私钥变换了,但如果第三方的公钥同样也被接收者合法采用,则接收者在验签时是有概率认为签名成功的,且此时,接收者很可能会误以为发送者来自于第三方。