第九章:加密-hmac:密码消息签名与验证-消息签名的应用

9.2.4 消息签名的应用
对于所有公共的网络服务,在安全性要求很高的地方存储数据,就应当使用HMAC认证。例如,通过一个管道或套接字发送数据时,应当对数据进行签名,然后在使用这个数据之前要检查这个签名。文件hmac_pickle.py中给出了一个扩展的例子。第一步是建立一个函数,计算一个串的摘要,另外实例化一个简单的类,并通过一个通信通道传递。

import hashlib
import hmac
import io
import pickle
import pprint

def make_digest(message):
    "Return a digest for the message."
    hash = hmac.new(
        b'secret-shared-key-goes-here',
        message,
        hashlib.sha1,
        )
    return hash.hexdigest().encode('utf-8')

class SimpleObject:
    """Demonstrate checking digests before unpickling."""

    def __init__(self,name):
        self.name = name

    def __str__(self):
        return self.name

接下来,创建一个BytesIO缓冲区表示这个套接字或管道。这个例子对数据流使用了一种易于解析的原生格式。首先写出摘要以及数据长度,后面是一个换行符。接下来是对象的串行化表示(由pickle生成)。实际的系统可能不希望依赖于一个长度值,毕竟如果摘要不正确,这个长度可能也是错误的。更合适的做法是使用真实数据中不太可能出现的某个终止符序列。然后示例程序向流写两个对象。写第一个对象时使用了正确的摘要值。

# Simulate a writeable socket or pipe with a buffer.
out_s = io.BytesIO()

# Write a valid object to the stream:
# digest\nlength\npickle
o = SimpleObject('digest matches')
pickled_data = pickle.dumps(o)
digest = make_digest(pickled_data)
header = b'%s %d\n' % (digest,len(pickled_data))
print('WRITING: {}'.format(header))
out_s.write(header)
out_s.write(pickled_data)

再用一个不正确的摘要将第二个对象写入流,这个摘要是为其他数据计算的,而并非由pickle生成。

# Write an invalid object to the stream.
o = SimpleObject('digest does not match')
pickled_data = pickle.dumps(o)
digest = make_digest(b'not the pickled data at all')
header = b'%s %d\n' % (digest,len(pickled_data))
print('\nWRITING: {}'.format(header))
out_s.write(header)
out_s.write(pickled_data)

out_s.flush()

既然数据在BytesIO缓冲区中,那么可以将它再次读出。首先读取包含摘要和数据长度的数据行,然后使用得到的长度值读取其余的数据。pickle.load()可以直接从流读数据,不过这种策略有一个假设,认为它是一个可信的数据流,但这个数据还不能保证足够可信到可以解除腌制。可以将pickle作为一个串从流读取,而不是真正将对象解除腌制,这样会更为安全。

# Simulate a readable socket or pipe with a buffer.
in_s = io.BytesIO(out_s.getvalue())

# Read the data.
while True:
    first_line = in_s.readline()
    if not first_line:
        break
    incoming_digest,incoming_length = first_line.split(b' ')
    incoming_length = int(incoming_length.decode('utf-8'))
    print('\nREAD:',incoming_digest,incoming_length)

一旦腌制数据在内存中,那么可以重新计算摘要值,并使用compare_digest()与所读取的数据进行比较。如果摘要匹配,就可以信任这个数据,并对其解除腌制。

incoming_pickled_data = in_s.read(incoming_length)

actual_digest = make_digest(incoming_pickled_data)
print('ACTUAL:',actual_digest)

if hmac.compare_digest(actual_digest,incoming_digest):
    obj = pickle.loads(incoming_pickled_data)
    print('OK:',obj)
else:
    print('WARNING:Data corruption')

输出显示第一个对象通过验证,不出所料,认为第二个对象“已被破坏”。

在一种计时攻击中,可能会使用简单的串或字节比较来对比两个摘要,通过传入不同长度的摘要来得到全部或部分秘密密钥。compare_digest()实现了一种快速但时间固定的比较函数,以避免计时攻击。
运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值