密码技术之认证

第二部分:认证

  当比较两条消息是否一致时,我们不必直接对比消息本身的内容,只要对比它们的“指纹”就行了。

 

 一、单向散列函数(one-way hash function)

    单向散列函数也称为消息摘要函数(message digest function)、哈希函数或者杂凑函数。

    单向散列函数有一个输入和一个输出,其中输入称为消息(message),输出称为散列值(hash value)也称为消息摘要(message digest)或者指纹。单向散列函数可以根据消息的内容计算出散列值,而散列值就可以被用来检查消息的完整性。所谓完整性是指有没有被篡改,或称为一致性。

    这里的消息可以是文字,图片或者文件等。并且散列值的长度和消息的长度无关,无论消息的大小是多少,散列函数都会计算出固定长度的散列值。如SHA-256单向散列函数,它计算出的长度永远

是256bit。

 

  单项散列函数的性质

  (1)、根据任意长度的消息计算出固定长度的散列值

  (2)、能够快速计算出散列值

  (3)、消息不同,散列值也不相同(哪怕1bit,散列值也不同),难以发现碰撞的性质称为抗碰撞性,密码技术中所使用的单向单列函数,都具备抗碰撞性。也就是说难以找到两条消息的散列值相同。

  (4)、具备单向性,指无法通过散列值反算出消息的性质。破镜不能重圆

 

  单项散列函数的实际应用

  1、检测软件是否被篡改

  2、基于口令的加密

    单向散列函数也被用于基于口令的加密(Password Based Encryption,PBE)。PBE的原理是将口令和盐(slat,通过伪随机数生成器产生的随机值)混合后计算其散列值,然后将这个散列值用过加密的密钥。

  3、消息认证码

    消息认证码是将“发送者和接收者之间的共享密钥”和“消息”混合后计算其散列值。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装

  4、数字签名

    数字签名的处理非常耗时,因此一般不会对整个消息内容直接施加数字签名,而是先通过单向散列函数计算出消息的散列值,然后再对这个散列值施加数字签名。

  5、一次性口令

    一次性口令经常被用于服务端对客户端的合法性认证。在这种方式中,通过使用单向散列函数可以保证口令只在通信链路上传送一次,因此即使窃听者窃取了口令,也无法使用。

 

  常用的单向散列函数

  推荐使用SHA-2、SHA-3

  1、MD4,MD5,MD(Message Digest的缩写)

  MD4,能够产生128bit的散列值,已经不安全了

  MD5,能够产生128bit的散列值,不过MD5的强碰撞性已经被攻破,也就是说,现在能够产生具备相同散列值的两条不同的消息,因此也已经不安全了。

  2、SHA-1、SHA-256、SHA-384、SHA-512

  SHA-1:能够产生160bit的散列值的单向散列函数,其强碰撞性已于2005年被攻破,目前已经不推荐使用

  SHA-256、SHA-384、SHA-512,它们的散列值长度分别是256bit、384bit和512bit,这些单向散列函数合起来统称为SHA-2。它们的消息长度也存在上限(SHA-256的上限接近于2^64bit,SHA-384和SHA-512的上限接近于2^128bit)

  3、PIPEMD-160

  能够产生160bit的单向散列函数,其强碰撞性已于2004年被攻破,除了以兼容性为目的外,其他情况下都不推荐使用。比特币中使用的就是PIPEMD-160。

  3、SHA-3

  2012年确定了Keccak算法作为SHA-3的标准。

  其采用了与SHA-2完全不同的结构、并且结构清晰,易于分析、能够适用于各种设备,也适用于嵌入式应用、在硬件上的实现显示了很高的性能、比其他最终候选算法安全性边际更大。

  Keccak

  Keccak可以生成任意长度的散列值,但为了配合SHA-2的散列值长度,SHA-3标准中共规定了SHA3-224、SHA3-256、SHA3-384、SHA3-512这个4个版本。在输入长度上限方面没有长度限制。

 

  单向散列函数无法解决的问题

  使用单向散列函数可以实现完整性也叫一致性的检查,但有些情况下即便能够检查完整性也是没有意义的。比如攻击者伪装成发送者向接收者发送了消息和散列值。这时接收者能够通过单向散列函数检查消息的完整性,但是却无法检查出发送者的身份是否被伪装了。也就是说,单向散列函数能够辨别出”篡改“,但无法辨别出”伪装“

  因此,仅仅确认消息的完整性是不够的,还需要进行认证。用于认证的技术包括消息认证码和数字签名。

 

  补充:HMACSHA1 加密算法

  HMACSHA1是从SHA1哈希函数构造的一种键控哈希算法,被用作HMAC(基于哈希的消息验证代码)。此HMAC进程将密钥和消息数据混合,使用哈希函数对混合结果进行哈希计算,将所得哈希值与该密钥混合,然后再次应用哈希函数。输出的哈希值长度为160位。

  在发送方和接收方共享机密密钥的前提下,HMAC可用于确定通过不安全的信道发送的消息是否已被篡改。发送方计算原始数据的哈希值,并将原始数据和哈希值放在一个消息中同时传送。接收方重新计算所接收消息的哈希值,并检查计算所得的HMAC是否与传送的HMAC匹配。

  因为更改消息和重新生成正确的哈希值需要密钥,所以对数据或哈希值的任何更改都会导致不匹配。因此,如果原始的哈希值与计算出的哈希值相匹配,则消息通过身份验证。

  HMACSHA1接收任何大小的密钥,并产生长度为160位的哈希序列。

 

 

 二、消息认证码(Message Authentication Code)

  使用消息认证码可以判断消息是否被篡改,以及是否有人伪装成发送者发送了该消息。

  如针对汇款请求,我们需要关注汇款请求的完整性和认证这两个性质。完整性,要能够确认汇款请求的内容没有被篡改,如收款账号和收款金额。认证,要能够确认消息是谁发送的,没有被攻击者伪装。

  消息认证码是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC。

  消息认证码的输入包括任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据成为MAC值。

  根据任意长度的消息输出固定长度的数据,这一点和单向散列函数很类似。但是单向散列函数中计算散列值时不需要密钥,相对地,消息认证码中则需要使用发送者与接收者之间共享的密钥。

  可以理解为,消息认证码是一种与密钥相关联的单向散列函数。

  消息认证码的密钥配送问题

    消息认证码的发送者和接收者之间需要共享密钥,这与对称密码类似,密钥配送方式和对称密码也相同,如使用公钥密码、Diffie-Hellman密钥交换、密钥分配中心,或者使用其他安全的方式发送密钥等。

  消息认证码的应用实例

    1、SWIFT

      Society for Worldwide Interbank Financial Telecommunication,环球银行金融电信协会的简称,其目的是为国际银行间的交易保驾护航。银行与银行之间是通过SWIFT来传递交易消息的,而为了确认消息的完整性以及对消息进行认证,SWIFT中使用了消息认证码。

    2、IPsec  

      是对互联网基本通信协议——IP协议增加安全性的一种方式。在IPsec中,对通信内容的认证和完整性校验都是采用消息认证码来完成的

    3、SSL/TLS

      是安全通信协议,对通信内筒的认证和完整性校验也使用了消息认证码

  消息认证码的实现方法

    (1)使用SHA-2之类的单向散列函数可以实现消息认证码,其中一种实现方法称为HMAC

    (2)使用AES之类的分组密码实现,将分组密码的密钥作为消息认证码的共享密钥来使用,并用CBC模式将消息全部加密,初始化向量IV是固定的,将最后一个分组密文作为MAC值。AES-CMAC(RFC4493)就是一种基于AES来实现的消息认证码。

  消息认证码无法解决的问题

    1、对第三方证明

    假设Bob在接收了来自Alice的消息之后,想要向第三方验证者Victor证明这条消息的确是Alice发送的,但是用消息认证码无法进行这样的证明,因为,首先,Victor要校验MAC值,就需要知道Alice和Bob之间共享的密钥。能够计算出正确MAC值的人只有Bob和Alice,在他们两个人之间进行通信时,可以断定是对方计算出了MAC值,这是因为共享这个密钥的双方之中,有一方就是自己。然而,对于第三方Victor,Alice和Bob却无法证明是对方计算出了MAC值,而不是自己。

    2、防止否认

    假设Bob收到了包含MAC值的消息,这个MAC值是用Alice和Bob共享的密钥计算出来的,因此Bob能够判断这条消息的确来自Alice。但是上面说的Bob无法向Victor证明这一点,也就是说,发送者Alice可以向Victor声称:我没有向Bob发送过这条消息,这样的行为就称为否认

 

 

 三、数字签名

   消息认证码无法防止否认,是因为消息认证码需要在发送者和接收者两者之间共享同一个密钥。正是因为密钥是共享的,所以发送者和接收者都能计算出正确的MAC值,对于第三者而言,我们无法证明

这条消息是哪一方生成的。

  数字签名中,发送者和接收者使用不同的密钥。发送者使用只有自己才知道的私钥给消息签名,接收者使用公钥进行验证,接收者无法使用公钥生成签名,但是却可以对发送者私钥计算出的签名进行验证

,也就是说,可以确定这个签名是哪一方计算出来的。

  数字签名对签名密钥和验证密钥进行了区分,使用验证密钥是无法生成签名的。此外,签名密钥只能有签名的人持有,而验证密钥则是任何需要验证签名的人都可以持有。

  数字签名就是通过将公钥密码 [反过来用] 而实现的。

  在公钥密码中,组成密钥对的两个密钥之间存在严密的数学关系,他们是一对无法拆散的伙伴关系。用公钥加密所得到的密文,只能用与该公钥配对的私钥才能解密;同样的,用私钥加密所得到的密文,也只能用与该私钥配对的公钥才能解密。而用私钥进行加密这一行为只能由持有私钥的人完成,所以可以将私钥加密的密文作为签名来对待。同时,由于公钥是公开的,因此任何人都可以用公钥进行解密,即

任何人都能够对签名进行验证。

  数字签名的方法

    (1):直接对消息签名(一般不使用)

    (2):对消息的散列值签名(一般用这种)

  对整个消息进行加密,如果消息很长则非常耗时,因为公钥算法本来就很慢。所以先使用单向散列函数先对消息生成固定长度的散列值,再对散列值进行加密就可以了。

  生成签名和验证签名的过程:

  1、Alice生成密钥对

  2、Alice将公钥发送给Bob

  3、Alice用单向散列函数计算出消息的散列值

     4、Alice用自己的私钥对散列值进行加密(生成签名)

  5、Alice将消息和签名发送给Bob

  6、Bob将签名解密后得到的散列值与Alice直接发送的消息的散列值进行对比验证

 

  私钥加密后的密文为什么能作为签名使用?数字签名是利用了【没有私钥的人事实上无法生成使用该私钥所生成的密文】这一性质来实现的。这里所生成的密文并非用于保证机密性,而是被用于代表一种只有【持有该密钥的人才能够生成的信息】。

  数字签名可以附加在消息的末尾,也可以和消息分离,单独作为文件来发送。签名之后,可以对消息和签名都进行修改,但是这样修改之后,验证签名就会失败,进行验证的人就会发现这一修改行为。数字签名所要是心啊的并不是防止修改,而是识别修改。修改没问题,但验证签名会失败。

 

  通过RSA实现数字签名

    演示RSA生成签名和验证签名的过程,这里直接对消息进行签名,不使用单向散列函数。

    在RSA中,被签名的消息、密钥以及最终生成的签名都是数字形式的。在对文本进行签名时,需要事先将文本编码成数字

  1、用RSA生成签名

    签名=消息^D mod N  【D和N就是签名者的私钥】

  2、用RSA验证签名

    由签名求得的消息=签名^E mod N  【E和N就是签名者的公钥】

公钥 数E和N
私钥 数D和N
生成签名

签名=消息^D mod N   (消息的 D次方除以N的余数)

验证签名 由签名求得的消息=签名^E mod N  (签名的E次方除以N的余数),然后将由签名得到的消息和接收到的消息对比

 

  实际应用:

  RSA2

  RSA 和 RSA2 签名算法区别 :

 什么是数字签名?简单来说,签名主要包含两个过程:摘要和非对称加密,首先对需要的数据做摘要(类似于常见的MD5)后得到摘要结果,然后通过签名者的私钥对摘要结果进行非对称加密即可得到签名结果。签名算法及区别:

签名类型 标准签名算法名称 备注
RSA2 SHA256WithRSA 强制要求 RSA 密钥的长度至少为 2048
RSA SHA1WithRSA 对 RSA 密钥的长度不限制,推荐使用 2048 位以上

  RSA2必RSA签名算法具有更强的安全能力。同样是私钥加签,公钥验签。

 一、支付宝开放平台SDK的签名方案

  签名的过程即生成签名方(通常为客户端)将传送的消息用私钥加密的过程;验签则是指验签方(通常指服务端)使用公钥对消息进行验证的过程。

 JAVA语言,签名和验签的过程大致为:

  1、生成签名方(客户端)首先对参数放入一个字符串数组signFields,把参数和值放入一个对象或map中,使用JSONObject把这个对象转化成json对象。然后构建签名原文,在构建签名原文时,我们需要把参数按照字典(比如a,b,c)顺序排序,具体排序方法直接调用JAVA的Arrays.sort方法。然后使用RSA的私钥对签名原文进行签名。

  2、验签方(服务端),解密密文得到明文后,和签名方一样先生成签名原文,然后使用RSA的公钥生成签名,把签名原文对签名方传入的签名进行验证,验证结果为true则说明验证通过,否则未通过。

 

 二、自行实现签名

  1、筛选并排序

  获取所有请求参数,不包括字节类型参数,如文件、字节流,剔除sign字段,剔除值为空的参数,删除参数名和参数值前后的空格,并按照第一个字符的键值ASCII码递增顺序(字母升序顺序),如果遇到相同字符则按照第二个字符的ASCII码递增排序,以此类推。

  2、拼接

  将排序后的参数与其对应值,组合成“参数=参数值”的格式,并把这些参数用&字符拼接起来,此时生成的字符串为待签名字符串。

  3、调用签名函数,用私钥对待签名字符串进行签名,并进行Base64编码

  4、把生成的签名赋值给sign参数,拼接到请求参数中去

  如:

REQUEST URL: https://openapi.alipay.com/gateway.do
REQUEST METHOD: POST
CONTENT:
app_id=2014072300007148
method=alipay.mobile.public.menu.add
charset=GBK
sign_type=RSA2
timestamp=2014-07-24 03:07:50
biz_content={"button":[{"actionParam":"ZFB_HFCZ","actionType":"out","name":"话费充值"},{"name":"查询","subButton":[{"actionParam":"ZFB_YECX","actionType":"out","name":"余额查询"},{"actionParam":"ZFB_LLCX","actionType":"out","name":"流量查询"},{"actionParam":"ZFB_HFCX","actionType":"out","name":"话费查询"}]},{"actionParam":"http://m.alipay.com","actionType":"link","name":"最新优惠"}]}
sign=e9zEAe4TTQ4LPLQvETPoLGXTiURcxiAKfMVQ6Hrrsx2hmyIEGvSfAQzbLxHrhyZ48wOJXTsD4FPnt+YGdK57+fP1BCbf9rIVycfjhYCqlFhbTu9pFnZgT55W+xbAFb9y7vL0MyAxwXUXvZtQVqEwW7pURtKilbcBTEW7TAxzgro=
version=1.0
则待签名字符串为:
app_id=2014072300007148&biz_content={"button":[{"actionParam":"ZFB_HFCZ","actionType":"out","name":"话费充值"},{"name":"查询","subButton":[{"actionParam":"ZFB_YECX","actionType":"out","name":"余额查询"},{"actionParam":"ZFB_LLCX","actionType":"out","name":"流量查询"},{"actionParam":"ZFB_HFCX","actionType":"out","name":"话费查询"}]},{"actionParam":"http://m.alipay.com","actionType":"link","name":"最新优惠"}]}&charset=GBK&method=alipay.mobile.public.menu.add&sign_type=RSA2&timestamp=2014-07-24 03:07:50&version=1.0

  加签方法示例:

/**
@param content 加签内容
@param privateKey 加签私钥
@param charset 加签字符集
@param charset 签名方法
**/
String AlipaySignature.rsaSign(String content, String privateKey, String charset,String signType)

 

  对数字签名的攻击

  1、中间人攻击

  针对公钥密码的中间人攻击对于数字签名来说也一样颇具威胁。

  对数字签名的中间人攻击,具体来说就是主动攻击者接入发送者和接收者的中间,对发送者伪装成接收者,对接收者伪装成发送者,从而能够在无需破解数字签名算法的前提下完成攻击。要防止中间人攻击,就需要确认自己所得到的公钥是否真的属于自己的通信对象,可以通过打电话确认的方式。

  2、对单向散列函数的攻击

  数字签名中所使用的单向散列函数必须具有抗碰撞性,否则攻击者就可以生成另外一条不同的消息,使其与签名所绑定的消息具有相同的散列值。

  3、利用数字签名攻击公钥密码

  攻击者窃听到通信密文之后,诱导接收者用私钥对密文进行签名,由于密文再签名相当于解密,所以攻击者就能获取到明文。

  4、潜在伪造

  为了应对潜在伪造,在RSA的基础上开发了RSA-PSS,RSA-PSS并不是对消息本身进行签名,而是对其散列值进行签名,另外,为了提高安全性,在计算散列值的时候还要对消息加盐(salt)。

 

  消息认证码和数字签名对比

  消息认证码 数字签名
发送者 用共享密钥计算MAC值 用私钥生成签名
接收者 用共享密钥计算MAC值 用公钥验证签名
密钥配送问题 存在 不存在,但公钥需要另外认证
完整性 ok ok
认证 ok(仅限通信对象双方,第三方无法认证) ok(可以适用于任何第三方认证)
防止否认 ok

  

  数字签名无法解决的问题

  用数字签名既可以识别出篡改和伪装,还可以防止否认。也就是说,我们同时实现了确认消息的完整性、进行认证以及防止否认。

  然而,要正确使用数字签名,有一个大前提,那就是用于验证签名的公钥必须属于真正的发送者。如果公钥是伪造的,那么即使数字签名算法再强大,也没用。

  为了能够确认自己的公钥是否合法,我们需要使用证书。所谓证书,就是将公钥当作一条消息,由一个可信的第三方对其签名后所得到的公钥。

 

 

 四、证书——为公钥加上数字签名

  公钥证书(Public-Key Certificate,PKC)里面记录了姓名等信息,以及属于此人的公钥,并由认证机构(Certification Authority,CA),如有名的认证机构赛门铁克Symantec。施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书也简称为证书(certificate),层级最高的认证机构称为 【根CA】。

  即通信双方不直接交流,而是通过第三方即认证机构交换公钥,有效防止中间人攻击

  应用证书后的步骤:

    1、Bob生成密钥对(可自行生成,也可由认证机构CA代生成)

    2、Bob在认证机构CA注册自己的公钥

    3、认证机构CA确认注册者身份后,用自己的私钥对Bob的公钥施加数字签名并生成证书

    4、Alice得到带有认证机构CA的数字签名的Bob的公钥(也就是证书)

    5、Alice使用认证机构CA的公钥验证数字签名,确认Bob的公钥的合法性

    6、Alice用Bob的公钥加密消息并发送给Bob

    7、Bob用自己的私钥解密密文得到Alice的消息

  

  证书标准规范

    证书是由认证机构颁发的,使用者需要对证书进行验证,因此如果证书的格式千奇百怪那就不方便了。于是,制定了证书的标准规范,其中使用最广泛的【X.509】规范(RFC3280)。很多应用程序都支持X.509并将其作为证书生成和交换的标准规范。

  公钥基础设施(PKI)

    公钥基础设施(Public-Key Infrastructure)是为了能够更有效地运用公钥而制定的一系列规范和规格的总称。如证书应由谁来颁发,如何颁发,私钥泄漏时如何作废证书,计算机之间的数据交换的格式等。

    PKI是一个总称,而并非指一个单独的规范,如RSA公司制定的PKCS(Public-Key Cryptography Standards,公钥密码标准)系列规范也是PKI的一种、证书的标准规范X.509也是PKI的一种、在开发PKI程序时所使用的各个公司编写的API和规格设计也是PKI的相关规格。

    PKI的组成要素

      (1)用户——使用PKI的实体

      (2)认证机构——颁发证书的人

      (3)仓库——保存证书的数据库,也叫证书目录

 

  认证机构CA的工作

    (1)生成密钥对(也可以由用户生成)

    (2)在注册公钥时对本人身份进行认证

    (3)生成并颁发证书

    (4)作废证书,认证机构需要制作一张证书作废清单(Certificate Revocation List,CRL)。

 

  对证书的攻击

    1、在公钥在CA注册之前进行攻击(CA在对公钥施加数字签名后将非常难以攻击,因此要在此之前)

    2、注册相似人名进行攻击

    3、窃取认证机构的私钥进行攻击

    4、攻击者伪装成认证机构进行攻击

    5、钻CRL的空子进行攻击

 

END

posted @ 2019-10-23 19:03  杨岂  阅读(936)  评论(0编辑  收藏  举报