如何在python中使用密码加密文本?

der*_*end 8 python encryption cryptography

很难在Google上找到对此的直接答案.

我想要从用户那里收集一段文字和一条消息1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc hello world.

然后我希望能够以某种方式使用文本加密/解密消息,以便我可以将其保存在我的数据库中,而不用担心如果我的网站被黑客攻击,数据会被暴露, encrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', 'hello world') decrypt('1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc', <encrypted_text>)

有没有一种简单的方法来实现这一点与python,请有人提供/指导我的一个例子.

也许是如何使用诸如'1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc'?之类的种子创建公钥/私钥对的示例?

提前谢谢了 :)

编辑:只是要明确我正在寻找一种方法来加密我的用户数据以一种威慑的方式不混淆消息.

如果这意味着我必须通过使用文本1PWP7a6xgoYx81VZocrDr5okEEcnqKkyDc作为种子来动态生成PGP/GPG pub/pri密钥对,那么这很好,但是这样做的方法是什么?

zwe*_*wer 22

以下是如何在CBC模式下正确完成,包括PKCS#7填充:

import base64
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto import Random

def encrypt(key, source, encode=True):
    key = SHA256.new(key).digest()  # use SHA-256 over our key to get a proper-sized AES key
    IV = Random.new().read(AES.block_size)  # generate IV
    encryptor = AES.new(key, AES.MODE_CBC, IV)
    padding = AES.block_size - len(source) % AES.block_size  # calculate needed padding
    source += bytes([padding]) * padding  # Python 2.x: source += chr(padding) * padding
    data = IV + encryptor.encrypt(source)  # store the IV at the beginning and encrypt
    return base64.b64encode(data).decode("latin-1") if encode else data

def decrypt(key, source, decode=True):
    if decode:
        source = base64.b64decode(source.encode("latin-1"))
    key = SHA256.new(key).digest()  # use SHA-256 over our key to get a proper-sized AES key
    IV = source[:AES.block_size]  # extract the IV from the beginning
    decryptor = AES.new(key, AES.MODE_CBC, IV)
    data = decryptor.decrypt(source[AES.block_size:])  # decrypt
    padding = data[-1]  # pick the padding value from the end; Python 2.x: ord(data[-1])
    if data[-padding:] != bytes([padding]) * padding:  # Python 2.x: chr(padding) * padding
        raise ValueError("Invalid padding...")
    return data[:-padding]  # remove the padding
Run Code Online (Sandbox Code Playgroud)

它设置为使用bytes数据,因此如果您想加密字符串或使用字符串密码,请encode()在将它们传递给方法之前使用适当的编解码器确保它们.如果离开了encode参数Trueencrypt()输出将是编码字符串的base64和decrypt()源也应该是BASE64字符串.

现在,如果你测试它:

my_password = b"secret_AES_key_string_to_encrypt/decrypt_with"
my_data = b"input_string_to_encrypt/decrypt"

print("key:  {}".format(my_password))
print("data: {}".format(my_data))
encrypted = encrypt(my_password, my_data)
print("\nenc:  {}".format(encrypted))
decrypted = decrypt(my_password, encrypted)
print("dec:  {}".format(decrypted))
print("\ndata match: {}".format(my_data == decrypted))
print("\nSecond round....")
encrypted = encrypt(my_password, my_data)
print("\nenc:  {}".format(encrypted))
decrypted = decrypt(my_password, encrypted)
print("dec:  {}".format(decrypted))
print("\ndata match: {}".format(my_data == decrypted))
Run Code Online (Sandbox Code Playgroud)

你的输出类似于:

key:  b'secret_AES_key_string_to_encrypt/decrypt_with'
data: b'input_string_to_encrypt/decrypt'

enc:  7roSO+P/4eYdyhCbZmraVfc305g5P8VhDBOUDGrXmHw8h5ISsS3aPTGfsTSqn9f5
dec:  b'input_string_to_encrypt/decrypt'

data match: True

Second round....

enc:  BQm8FeoPx1H+bztlZJYZH9foI+IKAorCXRsMjbiYQkqLWbGU3NU50OsR+L9Nuqm6
dec:  b'input_string_to_encrypt/decrypt'

data match: True
Run Code Online (Sandbox Code Playgroud)

证明相同的密钥和相同的数据每次仍然产生不同的密文.

现在,这比欧洲央行要好得多,但是......如果你打算用它进行交流 - 不要!这更多的是解释它应该如何构建,而不是真正用于生产环境,特别是不用于通信,因为它缺少一个关键的成分 - 消息认证.随意使用它,但你不应该使用自己的加密,有经过严格审查的协议可以帮助你避免常见的陷阱,你应该使用它们.

  • 这可能是一个很好的解决方案,但它看起来不必要地复杂。我认为没有理由不使用在一行代码中加密/解密的 `cryptography` 库 (https://cryptography.io/en/latest/)。该包是静态链接的,因此包含所有依赖项。 (3认同)
  • '有经过充分审查的协议可以帮助您避免常见的陷阱,您应该使用这些'您的意思是有一种更简单/更强大的方法来实现这一目标吗?如果是这样,你能把我链接到它吗? (2认同)
  • **警告**:使用SHA作为密钥(KBKDF)的派生方法,对于带有*非常高的熵*的输入字符串来说是很好的选择,但对于普通的密码或短语则不适用。CBC适合就地存储(例如,存储在数据库中),但对于传输模式安全性则“不行”:而是依靠TLS。 (2认同)

小智 7

基于 zwer 的答案,但显示了一个示例,尝试处理源文本恰好是 16 ( AES.block_size)的倍数的情况。但是@zwer 在评论中解释了此代码如何通过不适当填充源文本来破坏文本的加密,从而使您的管道不安全。

代码:

from builtins import bytes
import base64
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto import Random

def encrypt(string, password):
    """
    It returns an encrypted string which can be decrypted just by the 
    password.
    """
    key = password_to_key(password)
    IV = make_initialization_vector()
    encryptor = AES.new(key, AES.MODE_CBC, IV)

    # store the IV at the beginning and encrypt
    return IV + encryptor.encrypt(pad_string(string))

def decrypt(string, password):
    key = password_to_key(password)   
    
    # extract the IV from the beginning
    IV = string[:AES.block_size]  
    decryptor = AES.new(key, AES.MODE_CBC, IV)
    
    string = decryptor.decrypt(string[AES.block_size:])
    return unpad_string(string)

def password_to_key(password):
    """
    Use SHA-256 over our password to get a proper-sized AES key.
    This hashes our password into a 256 bit string. 
    """
    return SHA256.new(password).digest()

def make_initialization_vector():
    """
    An initialization vector (IV) is a fixed-size input to a cryptographic
    primitive that is typically required to be random or pseudorandom.
    Randomization is crucial for encryption schemes to achieve semantic 
    security, a property whereby repeated usage of the scheme under the 
    same key does not allow an attacker to infer relationships 
    between segments of the encrypted message.
    """
    return Random.new().read(AES.block_size)

def pad_string(string, chunk_size=AES.block_size):
    """
    Pad string the peculirarity that uses the first byte
    is used to store how much padding is applied
    """
    assert chunk_size  <= 256, 'We are using one byte to represent padding'
    to_pad = (chunk_size - (len(string) + 1)) % chunk_size
    return bytes([to_pad]) + string + bytes([0] * to_pad)
def unpad_string(string):
    to_pad = string[0]
    return string[1:-to_pad]

def encode(string):
    """
    Base64 encoding schemes are commonly used when there is a need to encode 
    binary data that needs be stored and transferred over media that are 
    designed to deal with textual data.
    This is to ensure that the data remains intact without 
    modification during transport.
    """
    return base64.b64encode(string).decode("latin-1")

def decode(string):
    return base64.b64decode(string.encode("latin-1"))

                              

                                                                                                                                                       
Run Code Online (Sandbox Code Playgroud)

测试:

def random_text(length):
    def rand_lower():
        return chr(randint(ord('a'), ord('z')))
    string = ''.join([rand_lower() for _ in range(length)])
    return bytes(string, encoding='utf-8')

def test_encoding():
    string = random_text(100)
    assert encode(string) != string
    assert decode(encode(string)) == string

def test_padding():
    assert len(pad_string(random_text(14))) == 16
    assert len(pad_string(random_text(15))) == 16
    assert len(pad_string(random_text(16))) == 32

def test_encryption():
    string = random_text(100)
    password = random_text(20)
    assert encrypt(string, password) != string
    assert decrypt(encrypt(string, password), password) == string
Run Code Online (Sandbox Code Playgroud)

  • @derrend - 抱歉耽搁了,所以没有通知我你的评论,我只是注意到了。我的示例中没有与源大小有关的错误 - 当它正好是`AES.block_size` 的乘积时,它返回 `AES.block_size` 作为填充,确保 PKCS#7 填充按照 [规范](https:// tools.ietf.org/html/rfc2315#section-10.3)。然而,Ignacio 的 `(chunk_size - (len(string) + 1)) % chunk_size` 将为 `AES.block_size - 1` 的所有倍数(在本例中为 15 个字节)返回零填充,这**将破坏**该大小的源的加密/解密过程。 (3认同)