Python中使用Fernet的对称加密-主密码用例

Python中使用Fernet的对称加密-主密码用例,python,encryption,sqlalchemy,encryption-symmetric,Python,Encryption,Sqlalchemy,Encryption Symmetric,我一直在试图了解对称加密是如何工作的,以及如何将其集成到CLI应用程序中,但我遇到了一些问题,我将在下面描述 我的用例如下所示: 我有一个CLI应用程序(SQLAlchemy+click+python3.8),它将是一个非常简单的密码管理器(个人使用) 启动时,我希望向用户请求主密码,以便他能够从数据库检索任何信息。如果用户还没有主密码,我会让他创建一个。我希望所有数据都用相同的主密钥加密 为了完成以上所有工作,我认为对称加密是最合适的,于是我开始写一些代码: 导入base64 从crypt

我一直在试图了解对称加密是如何工作的,以及如何将其集成到CLI应用程序中,但我遇到了一些问题,我将在下面描述

我的用例如下所示:

  • 我有一个CLI应用程序(
    SQLAlchemy
    +
    click
    +
    python3.8
    ),它将是一个非常简单的密码管理器(个人使用)

  • 启动时,我希望向用户请求主密码,以便他能够从数据库检索任何信息。如果用户还没有主密码,我会让他创建一个。我希望所有数据都用相同的主密钥加密

为了完成以上所有工作,我认为对称加密是最合适的,于是我开始写一些代码:

导入base64
从cryptography.fernet导入fernet,InvalidToken
从cryptography.hazmat.backends导入默认\u后端
从cryptography.hazmat.primitives导入哈希
从cryptography.hazmat.primitives.kdf.pbkdf2导入PBKDF2HMAC
def生成密钥派生(salt、主密码):
kdf=PBKDF2HMAC(
算法=hashes.SHA256(),
长度=32,
盐=盐,
迭代次数=100000次,
backend=默认值_backend()
)
key=base64.urlsafe_b64encode(kdf.derivate(master_password.encode()))
返回键
def encrypt(密钥、值到加密):
f=Fernet(键)
加密的\u key=f.encrypt(值\u到\u encrypt.encode())
返回加密的密钥
def解密(密钥、加密密钥):
f=Fernet(键)
尝试:
返回f.decrypt(加密密钥)
除InvalidToken外:
返回b''
现在,我试着从文档中理解:

在该方案中,盐必须储存在可回收的位置 以便将来从密码中派生相同的密钥

在我看来,这意味着:将盐存储在DB中,并在用户每次尝试使用应用程序时使用它。然后,运行用户通过密钥派生函数插入的主密码,并检查它是否匹配。。。钥匙但我没有初始密钥,因为我第一次没有将其与盐一起存储。如果我要保存它,难道没有人可以免费使用它来加密和解密数据吗?

预防上述情况的常见解决方案是什么

下面是一个使用
单击
的小POC:

导入操作系统
导入点击
从模型导入MasterPasswordModel
@单击.group(help=“个人使用的简单CLI密码管理器”)
@单击。传递上下文
def总管(ctx):
#如果用户尚未存储任何主密码,
#创建一个新的
如果MasterPasswordModel.is_为空():
#要求用户输入新的主密码
master_password=click.prompt(
'请输入新的主密码:',
隐藏输入=真
)
#制盐
盐=铀氧化物(16)
#生成密钥导出
#这不会被存储,因为如果它被存储,任何人都可以
#访问任何数据
key=generate\u key\u派生(salt、master\u密码)
#将盐储存到DB中
MasterPasswordModel.create(salt)
#如果用户存储了主密码,请检查该密码是否有效,并且
#允许他做其他的动作
其他:
#向用户询问现有主密码
master_password=click.prompt(
'请输入新的主密码:',
隐藏输入=真
)
#从数据库获取现有主密码
salt=MasterPasswordModel.get_salt()
#生成密钥导出
key=generate\u key\u派生(salt、master\u密码)
#在这一点上,我不知道如何检查“key”是否正确
#有效与否,因为我没有任何东西可以核对。
#我错过了什么?
我希望所有这些都有意义。作为TL;我想问题是:我怎样才能安全地保存钥匙,以便我能找回它进行进一步检查?或者这就是事情应该怎么做?我错过了什么?我肯定我误解了一些事情:)


乐:正如其中一条评论中所指出的,看起来可能有一个解决方案给我,但在这个过程中,我仍然被困在某个地方。其中规定:

如果您还没有这样做,我也强烈建议您不要这样做 直接使用用户提供的密钥,而是首先传递它 通过故意缓慢的键派生函数,如PBKDF2, b crypt或scrypt。你应该先做这件事,然后再尝试 验证密钥的正确性,并立即丢弃密钥 原始用户提供的密钥,并对所有内容使用派生密钥 (验证和实际加密/解密)

因此,让我们以每件事为例,一步一步:

1) 我第一次被要求输入主密码。它不存在于数据库中,所以,很明显,我必须创建并存储它

2) 除了新生成的salt,我还必须保存所提供的主密码的散列(为了示例起见,我将使用SHA-256)

3) 我现在有一个包含salt和hash主密码的记录,因此我可以继续使用该应用程序。我现在想在DB中创建一个新记录,它应该使用我的密钥进行加密

问题是什么键?如果我要应用上面写的内容,我必须使用我的
generate\u key\u derivation()
函数,使用DB中的salt和hash主密码,并将其用于加密/解密。但是,如果我这样做,难道没有人能够只获取存储在DB中的散列密钥,并使用相同的
generate\u key\u派生
来做任何他想做的事情吗

那么,我错过了什么