PBE加密算法说明

这是百度百科的定义:

PBE算法在加密过程中并不是直接使用口令来加密,而是加密的密钥由口令生成,这个功能由PBE算法中的KDF函数完成。KDF函数的实现过程为:将用户输入的口令首先通过“盐”(salt)的扰乱产生准密钥,再将准密钥经过散列函数多次迭代后生成最终加密密钥,密钥生成后,PBE算法再选用对称加密算法(如AES)对数据进行加密。

salt的作用是预防字典攻击。试想,如果没有salt,攻击者可以事先构造一个口令到key的字典,然后遍历字典里的候选key,这样就能大幅缩短尝试时间。但如果加了salt,准备候选key就变得非常困难,因为攻击者事先并不知道salt是啥,那他要准备的候选key的数量就会变得非常巨大。所以,salt应该要存放在一个安全的地方。

将准密钥经过散列函数多次迭代后生成最终加密密钥,这个过程称之为stretching。多次迭代的目的是为了加大攻击者破解的成本,对用户来说,准密钥就那一个,多次执行散列算法开销并不大。但对于攻击者而言,他是要用大量的候选key进行尝试的,再叠加多次散列算法执行,那就是一个很大的负担。当然,最终加密密钥也要放到一个安全的地方。

一个用python3写的PBE算法的例子:

from Crypto.Cipher import AES
from Crypto.Random.random import StrongRandom
from Crypto.Util.number import long_to_bytes
from Crypto.Hash import SHA256

def get_secure_random(byte_num) -> bytes:
    sr = StrongRandom()

    v = long_to_bytes(sr.getrandbits(byte_num * 8))
    l = len(v)

    if l == byte_num:
        return v

    assert (l < byte_num)
    res = v + b'0' * (byte_num - l)
    return res

# password based encryption
class PBE(object):
    """docstring for PBE"""

    _SALT_LEN = 32

    def __init__(self, password: str):
        super(PBE, self).__init__()
        self.password = password
        self.store_file = 'secret.bin'

    def encrypt(self, msg: str) -> bytes:
        salt = get_secure_random(PBE._SALT_LEN)
        kek = self.gen_kek(salt, self.password)

        msg_key = get_secure_random(32)

        aes4key = AESHelper(kek)
        enc_msg_key = aes4key.encrypt(msg_key)

        # we should store it in binary format, not text format
        with open(self.store_file, 'wb') as fp:
            fp.write(salt)
            fp.write(enc_msg_key)

        aes4msg = AESHelper(msg_key)
        return aes4msg.encrypt(msg)

    def decrypt(self, cipher_msg: bytes) -> bytes:
        # we should read it in binary format, not text format
        with open(self.store_file, 'rb') as fp:
            bs = fp.read()
            salt = bs[0:PBE._SALT_LEN]
            kek = self.gen_kek(salt, self.password)

            enc_msg_key = bs[PBE._SALT_LEN:]

            aes4key = AESHelper(kek)
            msg_key = aes4key.decrypt(enc_msg_key)

            aes4msg = AESHelper(msg_key)
            return aes4msg.decrypt(cipher_msg)

    def decrypt2str(self, cipher_msg: bytes) -> str:
        return byte2str(self.decrypt(cipher_msg))

    @staticmethod
    def gen_kek(salt, password):
        # salt is used to prevent dictionary-attack
        # we can call SHA multi times to stretch
        h = SHA256.new()
        h.update(salt + str2byte(password))
        kek = h.digest()  # get byte digest
        assert (len(kek) == 32)  # SHA256 gen 32bytes hash
        return kek

上述代码里的salt + str2byte(password)就是准密钥,经过SHA256安全散列算法生成kek,这里我们为简单起见,只做了一次散列,实际是可以做多次的。msg_key就是最终密钥,而kek(key encrypted key)是用来加密最终密钥msg_key的密钥。kek我们不用存,因为它可以通过salt+口令计算生成,但msg_key我们要加密后存储,这里我们将salt和msg_key放到了一个单独的secret.bin文件里,这个secret.bin文件也要保护起来(通过权限或口令)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值