根据密码对字符串进行编码的简单方法?

2025-01-22 08:45:00
admin
原创
102
摘要:问题描述:Python 是否有一种内置的、简单的使用密码对字符串进行编码/解码的方法?像这样:>>> encode('John Doe', password = 'mypass') 'sjkl28cn2sx0' >>> decode('sjkl28cn2sx0', pass...

问题描述:

Python 是否有一种内置的、简单的使用密码对字符串进行编码/解码的方法?

像这样:

>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'

因此字符串“John Doe”被加密为“sjkl28cn2sx0”。要获取原始字符串,我将使用密钥“mypass”(这是我源代码中的密码)“解锁”该字符串。我希望这是我使用密码加密/解密 Word 文档的方法。

我想使用这些加密字符串作为 URL 参数。我的目标是混淆,而不是强大的安全性;没有对任何关键任务进行编码。我意识到我可以使用数据库表来存储键和值,但我试图做到极简主义。


解决方案 1:

Python 没有内置加密方案。您还应该认真对待加密数据存储;一个开发人员认为不安全的简单加密方案和玩具方案可能会被经验不足的开发人员误认为是安全方案。如果您加密,请正确加密。

不过,您不需要做太多工作来实现适当的加密方案。首先,不要重新发明加密轮子,而是使用受信任的加密库来为您处理此问题。对于 Python 3,该受信任的库是cryptography

我还建议对字节进行加密和解密;先将文本消息编码为字节;stringvalue.encode()编码为 UTF8,然后使用轻松恢复bytesvalue.decode()

最后但并非最不重要的一点是,在加密和解密时,我们谈论的是密钥,而不是密码。密钥不应该是人类可以记住的,它是你存储在秘密位置但机器可以读取的东西,而密码通常是人类可以读取和记住的。只要稍加注意,你就可以从密码中得出密钥。

但是对于在集群中运行且无需人工干预的 Web 应用程序或进程,您需要使用密钥。密码适用于只有最终用户需要访问特定信息的情况。即便如此,您通常也会使用密码保护应用程序,然后使用密钥(可能是附加到用户帐户的密钥)交换加密信息。

对称密钥加密

Fernet – AES CBC + HMAC,强烈推荐

cryptography库包含Fernet 配方,这是使用加密的最佳实践配方。Fernet 是一种开放标准,可在多种编程语言中实现,并且它为您打包了 AES CBC 加密以及版本信息、时间戳和 HMAC 签名,以防止消息篡改。

Fernet 可让您轻松加密和解密消息,并保证您的安全。它是使用秘密加密数据的理想方法。

我建议您使用Fernet.generate_key()来生成安全密钥。您也可以使用密码(下一节),但完整的 32 字节密钥(16 字节用于加密,另 16 字节用于签名)将比您能想到的大多数密码更安全。

Fernet 生成的密钥是一个bytes具有 URL 和文件安全的 base64 字符的对象,因此可打印:

from cryptography.fernet import Fernet

key = Fernet.generate_key()  # store in a secure location
# PRINTING FOR DEMO PURPOSES ONLY, don't do this in production code
print("Key:", key.decode())

Fernet()要加密或解密消息,请使用给定的密钥创建一个实例,然后调用Fernet.encrypt()Fernet.decrypt(),要加密的纯文本消息和加密的令牌都是bytes对象。

encrypt()并且decrypt()函数看起来如下:

from cryptography.fernet import Fernet

def encrypt(message: bytes, key: bytes) -> bytes:
    return Fernet(key).encrypt(message)

def decrypt(token: bytes, key: bytes) -> bytes:
    return Fernet(key).decrypt(token)

演示:

>>> key = Fernet.generate_key()
>>> print(key.decode())
GZWKEhHGNopxRdOHS4H4IyKhLQ8lwnyU7vRLrM3sebY=
>>> message = 'John Doe'
>>> token = encrypt(message.encode(), key)
>>> print(token)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> decrypt(token, key).decode()
'John Doe'

带密码的 Fernet –从密码派生出的密钥,在一定程度上削弱了安全性

你可以使用密码代替密钥,只要你使用强密钥派生方法即可。然后你必须在消息中包含盐和 HMAC 迭代计数,因此如果不先分离盐、计数和 Fernet 令牌,加密值就不再与 Fernet 兼容:

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

backend = default_backend()
iterations = 100_000

def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
    """Derive a secret key from a given password and salt"""
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(), length=32, salt=salt,
        iterations=iterations, backend=backend)
    return b64e(kdf.derive(password))

def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
    salt = secrets.token_bytes(16)
    key = _derive_key(password.encode(), salt, iterations)
    return b64e(
        b'%b%b%b' % (
            salt,
            iterations.to_bytes(4, 'big'),
            b64d(Fernet(key).encrypt(message)),
        )
    )

def password_decrypt(token: bytes, password: str) -> bytes:
    decoded = b64d(token)
    salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
    iterations = int.from_bytes(iter, 'big')
    key = _derive_key(password.encode(), salt, iterations)
    return Fernet(key).decrypt(token)

演示:

>>> message = 'John Doe'
>>> password = 'mypass'
>>> password_encrypt(message.encode(), password)
b'9Ljs-w8IRM3XT1NDBbSBuQABhqCAAAAAAFyJdhiCPXms2vQHO7o81xZJn5r8_PAtro8Qpw48kdKrq4vt-551BCUbcErb_GyYRz8SVsu8hxTXvvKOn9QdewRGDfwx'
>>> token = _
>>> password_decrypt(token, password).decode()
'John Doe'

在输出中包含盐值使得可以使用随机盐值,从而确保加密输出完全随机,无论密码重用或消息重复。包含迭代计数可确保您能够根据 CPU 性能随时间推移的提高进行调整,而不会失去解密旧消息的能力。

仅密码就可与 Fernet 32​​ 字节随机密钥一样安全,前提是您从类似大小的池中生成完全随机的密码。32 字节为您提供 256 ^ 32 个密钥,因此如果您使用 74 个字符的字母表(26 个大写字母、26 个小写字母、10 个数字和 12 个可能的符号),则您的密码math.ceil(math.log(256 ** 32, 74))长度至少应为 == 42 个字符。但是,精心选择的较大 HMAC 迭代次数可以在一定程度上缓解熵不足的问题,因为这会使攻击者暴力破解的成本大大增加。

只需知道,选择一个较短但仍然合理安全的密码不会破坏此方案,它只会减少暴力攻击者必须搜索的可能值的数量;请确保为您的安全要求选择一个足够强的密码。

替代方案

遮蔽

另一种方法是不加密。不要试图使用低安全性密码或自制实现,例如 Vignere。这些方法没有安全性,但可能会让未来负责维护代码的缺乏经验的开发人员产生安全的错觉,这比没有安全性更糟糕。

如果您只需要隐蔽性,只需将数据进行 base64 处理即可;对于 URL 安全要求,该base64.urlsafe_b64encode()函数就足够了。这里不要使用密码,只需编码即可。最多添加一些压缩(例如zlib):

import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

def obscure(data: bytes) -> bytes:
    return b64e(zlib.compress(data, 9))

def unobscure(obscured: bytes) -> bytes:
    return zlib.decompress(b64d(obscured))

这变成b'Hello world!'b'eNrzSM3JyVcozy_KSVEEAB0JBF4='

唯有诚信

如果您需要的只是一种方法来确保数据在被发送到不受信任的客户端并被接收回来之后不会被更改,那么您想要对数据进行签名,您可以使用SHA1(对于 HMAC 签名仍然被认为是安全的)或更好的hmac库:

import hmac
import hashlib

def sign(data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    assert len(key) >= algorithm().digest_size, (
        "Key must be at least as long as the digest size of the "
        "hashing algorithm"
    )
    return hmac.new(key, data, algorithm).digest()

def verify(signature: bytes, data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
    expected = sign(data, key, algorithm)
    return hmac.compare_digest(expected, signature)

使用它来签名数据,然后将签名附加到数据中并将其发送给客户端。当您收到数据后,拆分数据和签名并进行验证。我已将默认算法设置为 SHA256,因此您需要一个 32 字节的密钥:

key = secrets.token_bytes(32)

您可能需要查看itsdangerous库,它将所有这些内容与各种格式的序列化和反序列化一起打包起来。

使用 AES-GCM 加密提供加密和完整性

Fernet 在 AEC-CBC 基础上采用 HMAC 签名来确保加密数据的完整性;由于密文已经过签名,恶意攻击者无法向您的系统输入无意义的数据,让您的服务陷入错误输入的循环中。

Galois /Counter 模式分组密码生成密文和标签以达到相同的目的,因此可用于相同的目的。缺点是,与 Fernet 不同,没有易于使用的通用配方可在其他平台上重复使用。AES-GCM 也不使用填充,因此此加密密文与输入消息的长度匹配(而 Fernet/AES-CBC 将消息加密为固定长度的块,从而在一定程度上掩盖了消息长度)。

AES256-GCM 采用通常的 32 字节密钥作为密钥:

key = secrets.token_bytes(32)

然后使用

import binascii, time
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidTag

backend = default_backend()

def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes:
    current_time = int(time.time()).to_bytes(8, 'big')
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.GCM(iv), backend=backend)
    encryptor = cipher.encryptor()
    encryptor.authenticate_additional_data(current_time)
    ciphertext = encryptor.update(message) + encryptor.finalize()        
    return b64e(current_time + iv + ciphertext + encryptor.tag)

def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes:
    algorithm = algorithms.AES(key)
    try:
        data = b64d(token)
    except (TypeError, binascii.Error):
        raise InvalidToken
    timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:]
    if ttl is not None:
        current_time = int(time.time())
        time_encrypted, = int.from_bytes(data[:8], 'big')
        if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted:
            # too old or created well before our current time + 1 h to account for clock skew
            raise InvalidToken
    cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend)
    decryptor = cipher.decryptor()
    decryptor.authenticate_additional_data(timestamp)
    ciphertext = data[8 + len(iv):-16]
    return decryptor.update(ciphertext) + decryptor.finalize()

我已经添加了一个时间戳来支持 Fernet 支持的相同生存时间用例。

本页上的其他方法(使用 Python 3)

AES CFB -与 CBC 类似,但无需填充

这是All Іѕ Vаиітy所遵循的方法,尽管不正确。这是cryptography版本,但请注意,我将 IV 包含在密文中,它不应存储为全局变量(重复使用 IV 会削弱密钥的安全性,而将其存储为模块全局变量意味着它将在下一次 Python 调用时重新生成,从而使所有密文都无法解密):

import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_cfb_encrypt(message, key):
    algorithm = algorithms.AES(key)
    iv = secrets.token_bytes(algorithm.block_size // 8)
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(message) + encryptor.finalize()
    return b64e(iv + ciphertext)

def aes_cfb_decrypt(ciphertext, key):
    iv_ciphertext = b64d(ciphertext)
    algorithm = algorithms.AES(key)
    size = algorithm.block_size // 8
    iv, encrypted = iv_ciphertext[:size], iv_ciphertext[size:]
    cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
    decryptor = cipher.decryptor()
    return decryptor.update(encrypted) + decryptor.finalize()

这缺少 HMAC 签名的附加保护,也没有时间戳;您必须自己添加这些。

以上内容还说明了错误地组合基本加密构建块是多么容易;All Іѕ Vаиітy 对 IV 值的错误处理可能会导致数据泄露或所有加密消息因 IV 丢失而无法读取。改用 Fernet 可以保护您免受此类错误的影响。

AES ECB –不安全

如果您以前实现了AES ECB 加密,并且仍然需要在 Python 3 中支持它,那么您也可以这样做cryptography同样的警告也适用,ECB对于实际应用来说不够安全。为 Python 3 重新实现该答案,添加填充的自动处理:

from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

backend = default_backend()

def aes_ecb_encrypt(message, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(cipher.algorithm.block_size).padder()
    padded = padder.update(msg_text.encode()) + padder.finalize()
    return b64e(encryptor.update(padded) + encryptor.finalize())

def aes_ecb_decrypt(ciphertext, key):
    cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder()
    padded = decryptor.update(b64d(ciphertext)) + decryptor.finalize()
    return unpadder.update(padded) + unpadder.finalize()

再次强调,这缺少 HMAC 签名,无论如何你都不应该使用 ECB。以上内容仅用于说明它cryptography可以处理常见的加密构建块,甚至是你实际上不应该使用的构建块。

解决方案 2:

假设您只是在寻找简单的混淆方法,让旁观者无法发现事物并且您不想使用第三方库。我推荐使用 Vigenere 密码之类的东西。它是简单古代密码中最强大的密码之一。

维吉尼亚密码

实现起来又快又简单。例如:

import base64

def encode(key, string):
    encoded_chars = []
    for i in xrange(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return base64.urlsafe_b64encode(encoded_string)

解码几乎相同,只是要减去密钥。

如果您编码的字符串很短,或者很难猜测所用密码的长度,则破解起来会困难得多。

如果您正在寻找加密的东西,PyCrypto 可能是您的最佳选择,尽管之前的答案忽略了一些细节:PyCrypto 中的 ECB 模式要求您的消息长度为 16 个字符的倍数。因此,您必须填充。此外,如果您想将它们用作 URL 参数,请使用base64.urlsafe_b64_encode(),而不是标准参数。这会将 base64 字母表中的几个字符替换为 URL 安全字符(正如其名称所暗示的那样)。

但是,在使用前,您应该绝对确定这一层非常薄的混淆层足以满足您的需求。我链接到的维基百科文章提供了破解密码的详细说明,因此任何有一定决心的人都可以轻松破解它。

解决方案 3:

由于您明确表示您想要的是模糊性而不是安全性,因此我们将避免因您建议的弱点而斥责您:)

因此,使用 PyCrypto:

import base64
from Crypto.Cipher import AES

msg_text = b'test some plain text here'.rjust(32)
secret_key = b'1234567890123456'

cipher = AES.new(secret_key,AES.MODE_ECB) # never use ECB in strong systems obviously
encoded = base64.b64encode(cipher.encrypt(msg_text))
print(encoded)
decoded = cipher.decrypt(base64.b64decode(encoded))
print(decoded)

如果有人掌握了您的数据库和代码库,他们将能够解码加密数据。确保您的secret_key安全!

解决方案 4:

以下是@qneill答案中函数的 Python 3 版本:

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc).encode()).decode()

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc).decode()
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

需要额外的编码/解码是因为 Python 3 将字符串/字节数组分成两个不同的概念,并更新了它们的 API 来反映这一点。

解决方案 5:

@smehmood 的Vigenere 密码答案中提到的“encoded_c”应该是“key_c”。

这是有效的编码/解码功能。

import base64
def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

免责声明:正如评论所暗示的,这不应该用于保护实际应用程序中的数据,除非您阅读了本文并且不介意与律师交谈:

XOR加密有什么问题?

解决方案 6:

免责声明:正如评论中提到的,这不应用于保护实际应用程序中的数据。

XOR加密有什么问题?

https://crypto.stackexchange.com/questions/56281/writing-a-xor-cipher-of-known-key-length

https://github.com/hellman/xortool


正如所提到的,PyCrypto 库包含一套密码。如果你不想自己做,可以使用 XOR“密码”来完成这项脏活:

from Crypto.Cipher import XOR
import base64

def encrypt(key, plaintext):
  cipher = XOR.new(key)
  return base64.b64encode(cipher.encrypt(plaintext))

def decrypt(key, ciphertext):
  cipher = XOR.new(key)
  return cipher.decrypt(base64.b64decode(ciphertext))

密码的工作原理如下,无需填充明文:

>>> encrypt('notsosecretkey', 'Attack at dawn!')
'LxsAEgwYRQIGRRAKEhdP'

>>> decrypt('notsosecretkey', encrypt('notsosecretkey', 'Attack at dawn!'))
'Attack at dawn!'

感谢https://stackoverflow.com/a/2490376/241294提供的base64编码/解码函数(我是python新手)。

解决方案 7:

这是使用 AES(PyCrypto)和 base64 实现 URL 安全加密和解密的实现。

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

AKEY = b'mysixteenbytekey' # AES key must be either 16, 24, or 32 bytes long

iv = Random.new().read(AES.block_size)

def encode(message):
    obj = AES.new(AKEY, AES.MODE_CFB, iv)
    return base64.urlsafe_b64encode(obj.encrypt(message))

def decode(cipher):
    obj2 = AES.new(AKEY, AES.MODE_CFB, iv)
    return obj2.decrypt(base64.urlsafe_b64decode(cipher))

如果您遇到类似https://bugs.python.org/issue4329 ( TypeError: character mapping must return integer, None or unicode)的问题,请str(cipher)在解码时使用如下方法:

return obj2.decrypt(base64.urlsafe_b64decode(str(cipher)))

测试:

In [13]: encode(b"Hello World")
Out[13]: b'67jjg-8_RyaJ-28='

In [14]: %timeit encode("Hello World")
100000 loops, best of 3: 13.9 µs per loop

In [15]: decode(b'67jjg-8_RyaJ-28=')
Out[15]: b'Hello World'

In [16]: %timeit decode(b'67jjg-8_RyaJ-28=')
100000 loops, best of 3: 15.2 µs per loop

解决方案 8:

该库cryptocode提供了一种使用密码对字符串进行编码和解码的简单方法。安装方法如下:

pip install cryptocode

加密消息(示例代码):

import cryptocode

encoded = cryptocode.encrypt("mystring","mypassword")
## And then to decode it:
decoded = cryptocode.decrypt(encoded, "mypassword")

文档可在此处找到

解决方案 9:

在 python3 中使用编码/解码函数(很少改编自 qneill 的答案):

def encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = (ord(clear[i]) + ord(key_c)) % 256
        enc.append(enc_c)
    return base64.urlsafe_b64encode(bytes(enc))

def decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + enc[i] - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

解决方案 10:

感谢您的精彩回答。没有什么原创内容可以添加,但这里有一些使用一些有用的 Python 工具对 qneill 的答案的渐进式重写。我希望您同意它们简化并澄清了代码。

import base64


def qneill_encode(key, clear):
    enc = []
    for i in range(len(clear)):
        key_c = key[i % len(key)]
        enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def qneill_decode(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i in range(len(enc)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

enumerate()-- 将列表中的项目与其索引配对

迭代字符串中的字符

def encode_enumerate(key, clear):
    enc = []
    for i, ch in enumerate(clear):
        key_c = key[i % len(key)]
        enc_c = chr((ord(ch) + ord(key_c)) % 256)
        enc.append(enc_c)
    return base64.urlsafe_b64encode("".join(enc))


def decode_enumerate(key, enc):
    dec = []
    enc = base64.urlsafe_b64decode(enc)
    for i, ch in enumerate(enc):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(ch) - ord(key_c)) % 256)
        dec.append(dec_c)
    return "".join(dec)

使用列表推导式创建列表

def encode_comprehension(key, clear):
    enc = [chr((ord(clear_char) + ord(key[i % len(key)])) % 256)
                for i, clear_char in enumerate(clear)]
    return base64.urlsafe_b64encode("".join(enc))


def decode_comprehension(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(ch) - ord(key[i % len(key)])) % 256)
           for i, ch in enumerate(enc)]
    return "".join(dec)

在 Python 中,通常根本不需要列表索引。使用 zip 和 cycle 完全消除循环索引变量:

from itertools import cycle


def encode_zip_cycle(key, clear):
    enc = [chr((ord(clear_char) + ord(key_char)) % 256)
                for clear_char, key_char in zip(clear, cycle(key))]
    return base64.urlsafe_b64encode("".join(enc))


def decode_zip_cycle(key, enc):
    enc = base64.urlsafe_b64decode(enc)
    dec = [chr((256 + ord(enc_char) - ord(key_char)) % 256)
                for enc_char, key_char in zip(enc, cycle(key))]
    return "".join(dec)

和一些测试...

msg = 'The quick brown fox jumps over the lazy dog.'
key = 'jMG6JV3QdtRh3EhCHWUi'
print('cleartext: {0}'.format(msg))
print('ciphertext: {0}'.format(encode_zip_cycle(key, msg)))

encoders = [qneill_encode, encode_enumerate, encode_comprehension, encode_zip_cycle]
decoders = [qneill_decode, decode_enumerate, decode_comprehension, decode_zip_cycle]

# round-trip check for each pair of implementations
matched_pairs = zip(encoders, decoders)
assert all([decode(key, encode(key, msg)) == msg for encode, decode in matched_pairs])
print('Round-trips for encoder-decoder pairs: all tests passed')

# round-trip applying each kind of decode to each kind of encode to prove equivalent
from itertools import product
all_combinations = product(encoders, decoders)
assert all(decode(key, encode(key, msg)) == msg for encode, decode in all_combinations)
print('Each encoder and decoder can be swapped with any other: all tests passed')

>>> python crypt.py
cleartext: The quick brown fox jumps over the lazy dog.
ciphertext: vrWsVrvLnLTPlLTaorzWY67GzYnUwrSmvXaix8nmctybqoivqdHOic68rmQ=
Round-trips for encoder-decoder pairs: all tests passed
Each encoder and decoder can be swapped with any other: all tests passed

解决方案 11:

如果想要安全,可以使用 Fernet,它在加密方面是可靠的。如果不想单独存储,可以使用静态“盐”——这样只会失去字典和彩虹攻击预防。我之所以选择它,是因为我可以选择长密码或短密码,而使用 AES 则不那么容易。

from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64

#set password
password = "mysecretpassword"
#set message
message = "secretmessage"

kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt="staticsalt", iterations=100000, backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(password))
f = Fernet(key)

#encrypt
encrypted = f.encrypt(message)
print encrypted

#decrypt
decrypted = f.decrypt(encrypted)
print decrypted

如果这太复杂,有人建议使用 simplecrypt

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)

解决方案 12:

我给出4个解决方案:

1)使用 Fernet 加密cryptography

以下是使用包的解决方案cryptography,您可以照常安装pip install cryptography

import base64
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

def cipherFernet(password):
    key = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=b'abcd', iterations=1000, backend=default_backend()).derive(password)
    return Fernet(base64.urlsafe_b64encode(key))

def encrypt1(plaintext, password):
    return cipherFernet(password).encrypt(plaintext)

def decrypt1(ciphertext, password):
    return cipherFernet(password).decrypt(ciphertext)

# Example:

print(encrypt1(b'John Doe', b'mypass'))  
# b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg=='
print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'mypass')) 
# b'John Doe'
try:  # test with a wrong password
    print(decrypt1(b'gAAAAABd53tHaISVxFO3MyUexUFBmE50DUV5AnIvc3LIgk5Qem1b3g_Y_hlI43DxH6CiK4YjYHCMNZ0V0ExdF10JvoDw8ejGjg==', b'wrongpass')) 
except InvalidToken:
    print('Wrong password')

您可以使用自己的盐、迭代次数等进行调整。此代码与@HCLivess 的答案相差不远,但目标是提供随时可用的encrypt功能decrypt。来源:https ://cryptography.io/en/latest/fernet/#using-passwords-with-fernet 。

注意:如果您想要字符串而不是字节,请在任何地方使用.encode()和。.decode()`'John Doe'`b'John Doe'


2)使用Crypto库进行简单的 AES 加密

这适用于 Python 3:

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

def cipherAES(password, iv):
    key = SHA256.new(password).digest()
    return AES.new(key, AES.MODE_CFB, iv)

def encrypt2(plaintext, password):
    iv = Random.new().read(AES.block_size)
    return base64.b64encode(iv + cipherAES(password, iv).encrypt(plaintext))

def decrypt2(ciphertext, password):
    d = base64.b64decode(ciphertext)
    iv, ciphertext = d[:AES.block_size], d[AES.block_size:]
    return cipherAES(password, iv).decrypt(ciphertext)

# Example:    

print(encrypt2(b'John Doe', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'mypass'))
print(decrypt2(b'B/2dGPZTD8V22cIVKfp2gD2tTJG/UfP/', b'wrongpass'))  # wrong password: no error, but garbled output

注意:如果您不想要文本可读输出和/或您想将密文作为二进制文件保存到磁盘,您可以base64.b64encode删除。.b64decode


3)AES 使用更好的密码密钥派生函数和测试“输入密码错误”的能力,带有Crypto

解决方案 2) 使用 AES“CFB 模式”是可行的,但有两个缺点:SHA256(password)很容易通过查找表进行暴力破解,并且无法测试是否输入了错误的密码。这里通过使用“GCM 模式”中的 AES 解决了这个问题,如AES:如何检测是否输入了错误的密码?和这种说“您输入的密码错误”的方法安全吗?中所述:

import Crypto.Random, Crypto.Protocol.KDF, Crypto.Cipher.AES

def cipherAES_GCM(pwd, nonce):
    key = Crypto.Protocol.KDF.PBKDF2(pwd, nonce, count=100000)
    return Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_GCM, nonce=nonce, mac_len=16)

def encrypt3(plaintext, password):
    nonce = Crypto.Random.new().read(16)
    return nonce + b''.join(cipherAES_GCM(password, nonce).encrypt_and_digest(plaintext))  # you case base64.b64encode it if needed

def decrypt3(ciphertext, password):
    nonce, ciphertext, tag = ciphertext[:16], ciphertext[16:len(ciphertext)-16], ciphertext[-16:]
    return cipherAES_GCM(password, nonce).decrypt_and_verify(ciphertext, tag)

# Example:

print(encrypt3(b'John Doe', b'mypass'))
print(decrypt3(b'xbaN_x90Rxdfxa9xc7xd6x16/xbb!xf5Qxa9]xe5xa5xafx81xc3
2e/("Ixb4xab5xa6ezux8c%xa50', b'mypass'))
try:
    print(decrypt3(b'xbaN_x90Rxdfxa9xc7xd6x16/xbb!xf5Qxa9]xe5xa5xafx81xc3
2e/("Ixb4xab5xa6ezux8c%xa50', b'wrongpass'))
except ValueError:
    print("Wrong password")

4)使用 RC4(不需要库)

改编自https://github.com/bozhu/RC4-Python/blob/master/rc4.py

def PRGA(S):
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        yield S[(S[i] + S[j]) % 256]

def encryptRC4(plaintext, key, hexformat=False):
    key, plaintext = bytearray(key), bytearray(plaintext)  # necessary for py2, not for py3
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    keystream = PRGA(S)
    return b''.join(b"%02X" % (c ^ next(keystream)) for c in plaintext) if hexformat else bytearray(c ^ next(keystream) for c in plaintext)

print(encryptRC4(b'John Doe', b'mypass'))                           # b'x88xafxc1x04x8bx98x18x9a'
print(encryptRC4(b'x88xafxc1x04x8bx98x18x9a', b'mypass'))   # b'John Doe'

(自最新编辑以来已过时,但保留以供将来参考):我在使用 Windows + Python 3.6 + 所有涉及的答案时遇到了问题pycrypto(无法pip install pycrypto在 Windows 上使用)或pycryptodome(此处带有的答案from Crypto.Cipher import XOR失败,因为XOR此 fork 不支持pycrypto;并且使用的解决方案... AES也失败了TypeError: Object type <class 'str'> cannot be passed to C code)。此外,该库simple-crypt具有pycrypto依赖性,因此它不是一个选项。

解决方案 13:

这个答案对安全来说很糟糕。不要用于任何敏感信息

来这里的人(和赏金猎人)似乎正在寻找没有太多设置的单行代码,而其他答案没有提供。所以我提出了 base64。

现在,请记住,这只是基本的混淆,并且绝对不符合安全性,但这里有一些单行代码:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data, key): # the key DOES NOT make this safe
    return urlsafe_b64encode(bytes(key+data, 'utf-8'))

def decode(enc, key):
    return urlsafe_b64decode(enc)[len(key):].decode('utf-8')

print(encode('hi', 'there')) # b'dGhlcmVoaQ=='
print(decode(encode('hi', 'there'), 'there')) # 'hi'

需要注意以下几点:

  • 你可能希望自己处理更多或更少的字节到字符串编码/解码,具体取决于你的 I/O。查看bytes()bytes::decode()

  • base64 很容易通过所使用的字符类型识别,并且通常以=字符结尾。当我们在网站上看到它们时,像我这样的人绝对会在 javascript 控制台中解码它们。这就像btoa(string)(js)一样简单

  • 顺序是密钥+数据,就像在 b64 中一样,末尾出现的字符取决于开头的字符(因为字节偏移。维基百科有一些很好的解释)。在这种情况下,编码字符串的开头对于使用该密钥编码的所有内容都是相同的。优点是数据将更加模糊。反过来做会导致数据部分对于每个人来说都完全相同,无论密钥是什么。

现在,如果您想要的甚至不需要任何类型的密钥,而只是一些混淆,您可以再次使用 base64,而不需要任何类型的密钥:

from base64 import urlsafe_b64encode, urlsafe_b64decode

def encode(data):
    return urlsafe_b64encode(bytes(data, 'utf-8'))

def decode(enc):
    return urlsafe_b64decode(enc).decode()

print(encode('hi')) # b'aGk='
print(decode(encode('hi'))) # 'hi'

解决方案 14:

这可行,但密码长度必须准确8。这很简单,需要pyDes。

from pyDes import *

def encode(data,password):
    k = des(password, CBC, "", pad=None, padmode=PAD_PKCS5)
    d = k.encrypt(data)
    return d

def decode(data,password):
    k = des(password, CBC, "", pad=None, padmode=PAD_PKCS5)
    d = k.decrypt(data)
    return d

x = encode('John Doe', 'mypass12')
y = decode(x,'mypass12')

print x
print y

输出:

³.ÞåS¾+æÅ`;Ê
John Doe

解决方案 15:

您可以使用 AES 加密字符串并设置密码。不过,您需要选择一个足够强的密码,这样别人就无法轻易猜出它是什么(抱歉,我没办法。我是一个想要成为安全专家的人)。

AES 具有强大的密钥大小,而且易于与 PyCrypto 一起使用。

解决方案 16:

再添加一个带有解码和编码的代码以供参考

import base64

def encode(key, string):
    encoded_chars = []
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) + ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    arr2 = bytes(encoded_string, 'utf-8')
    return base64.urlsafe_b64encode(arr2)

def decode(key, string):
    encoded_chars = []
    string = base64.urlsafe_b64decode(string)
    string = string.decode('utf-8')
    for i in range(len(string)):
        key_c = key[i % len(key)]
        encoded_c = chr(ord(string[i]) - ord(key_c) % 128)
        encoded_chars.append(encoded_c)
    encoded_string = "".join(encoded_chars)
    return encoded_string

def main():
    answer = str(input("EorD"))
    if(answer in ['E']):
        #ENCODE
        file = open("D:enc.txt")
        line = file.read().replace("
", " NEWLINEHERE ")
        file.close()
        text = encode("4114458",line)
        fnew = open("D:\\new.txt","w+")
        fnew.write(text.decode('utf-8'))
        fnew.close()
    else:
        #DECODE
        file = open("D:\\new.txt",'r+')
        eline = file.read().replace("NEWLINEHERE","
")
        file.close()
        print(eline)
        eline = eline.encode('utf-8')
        dtext=decode("4114458",eline)
        print(dtext)
        fnew = open("D:\\newde.txt","w+")
        fnew.write(dtext)
        fnew.close

if __name__ == '__main__':
    main()

解决方案 17:

@qneill 代码的另一种实现包括原始消息的 CRC 校验和,如果校验失败则抛出异常:

import struct
import zlib
import base64

def vigenere_encode(text, key):
    text = text.encode() + struct.pack('i', zlib.crc32(text.encode()))
    enc = []
    for i in range(len(text)):
        key_c = key[i % len(key)]
        enc_c = chr((text[i] + ord(key_c)) % 256)
        enc.append(enc_c)

    enc = ''.join(enc).encode()
    enc = base64.urlsafe_b64encode(enc)

    return enc.decode()

def vigenere_decode(encoded_text, key):
    dec = []
    encoded_text = base64.urlsafe_b64decode(encoded_text).decode()
    for i in range(len(encoded_text)):
        key_c = key[i % len(key)]
        dec_c = chr((256 + ord(encoded_text[i]) - ord(key_c)) % 256)
        dec.append(dec_c)

    dec = "".join(dec)
    checksum = dec[-4:]
    dec = dec[:-4]

    crc = struct.pack('i', zlib.crc32(dec.encode()))
    assert [int(i) for i in crc] == [ord(i) for i in checksum], 'Decode Checksum Error'

    return dec

解决方案 18:

你可以使用新的PyCryptodomex,因为 PyCrypto 已被弃用,并且已经有一段时间没有维护了

import base64
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
###################
class Cryptor:
    def __init__(self, key):
      self.SECRET_KEY = str(key).encode("utf-8")
      self.BLOCK_SIZE = 32 # Bytes
      self.CIPHER = AES.new(self.SECRET_KEY, AES.MODE_ECB) # never use ECB in strong systems obviously

    def encrypt(self, text):
       text = str(text).encode("utf-8")
       return base64.b64encode(self.CIPHER.encrypt(pad(text, self.BLOCK_SIZE))).decode("utf-8")
    
    def decrypt(self, encoded_text):
       self.CIPHER = AES.new(self.SECRET_KEY, AES.MODE_ECB)
       return unpad(self.CIPHER.decrypt(base64.b64decode(encoded_text)), self.BLOCK_SIZE).decode("utf-8")
       
cryptor = Cryptor("1234567890123456")
text = "hello world"
text = cryptor.encrypt(text)
print(text)
print(cryptor.decrypt(text))

注意:确保您的密钥是 16 个字节。

你可以使用它来安装它,pip install pycryptodomex因为它是目前唯一一个独立于旧 PyCrypto 库的版本。注意:所有模块都安装在 Cryptodome 包下,例如

from Cryptodome.Cipher import AES

您可以从这里的官方文档中阅读更多内容https://www.pycryptodome.org/src/introduction

解决方案 19:

外部库提供密钥加密算法。

例如,CypherPyCrypto 中的模块提供了多种加密算法的选择:

  • Crypto.Cipher.AES

  • Crypto.Cipher.ARC2

  • Crypto.Cipher.ARC4

  • Crypto.Cipher.Blowfish

  • Crypto.Cipher.CAST

  • Crypto.Cipher.DES

  • Crypto.Cipher.DES3

  • Crypto.Cipher.IDEA

  • Crypto.Cipher.RC5

  • Crypto.Cipher.XOR

MeTooCrypto是OpenSSLPython的包装器,它(除其他功能外)还提供了一个功能齐全的通用加密库。其中包括对称密码(如 AES)。

解决方案 20:

如果你想要安全加密:

对于 python 2,您应该使用 keyczar http://www.keyczar.org/

对于 python 3,在 keyczar 可用之前,我已经编写了 simple-crypt http://pypi.python.org/pypi/simple-crypt

这两者都将使用密钥加强,这使得它们比这里的大多数其他答案更安全。而且由于它们非常易于使用,您可能希望在安全性并不重要的情况下使用它们......

解决方案 21:

简单密码

使用所谓的维吉尼亚密码作为起点,我决定改进这个想法,直到我得到一些看起来完全加密的东西,因为维吉尼亚密码被认为与 n-凯撒密码一样相对容易破解。

这个简单的密码有以下特点:

  • 无需第三方库,仅需内置库

  • 随机秘密盐(即胡椒)

  • 一次性密码本(每次都不同)

  • 无论输入大小如何,总大小仅增加 32 个字节

以下示例设计得尽可能简单,考虑到替代方案,并且可以根据需要优化/适应各种要求。换句话说,这主要是为了说明密钥生成和密码的核心功能……

    import hashlib, secrets
    
    
    def encipher(source_bytes, password):
        source_bytes = bytes(
                secrets.choice(range(256)) for _ in range(32)
                ) + source_bytes
        destination_bytearray = bytearray(len(source_bytes))
        secure_hash = hashlib.sha256(password).digest()
        
        for i, current_byte in enumerate(source_bytes):
            # Rotate hash...
            if i % 32 == 0 and i > 0:
                secure_hash = hashlib.sha256(
                        secure_hash
                        + source_bytes[i-32:i]
                ).digest()
    
            destination_bytearray[i] = (current_byte + secure_hash[i%32]) % 256
    
        return destination_bytearray
    
    
    def decipher(source_bytes, password):
        destination_bytearray = bytearray(len(source_bytes))
        secure_hash = hashlib.sha256(password).digest()
        
        for i, current_byte in enumerate(source_bytes):
            # Rotate hash...
            if i % 32 == 0 and i > 0:
                secure_hash = hashlib.sha256(
                        secure_hash
                        + destination_bytearray[i-32:i]
                ).digest()
    
            destination_bytearray[i] = (current_byte - secure_hash[i%32]) %256
    
        return destination_bytearray[32:]

用法:

>>> ciphertext = encipher(b'John Doe', b'Passw0rd!')
>>> plaintext = decipher(ciphertext, b'Passw0rd!')
>>> plaintext
bytearray(b'John Doe')

常规的 Unicode 文本字符串可以使用以下方法转换为字节字符串:

bytestring = bytes('Some Unicode text string here...', 'utf-8')

并将其解码回 Unicode 文本字符串,内容如下:

unicode = bytestring.decode()

解决方案 22:

因此,由于没有对任何关键任务进行编码,因此您只想加密以进行混淆

让我展示一下凯撒的密码

在此处输入图片描述

凯撒密码或凯撒移位是最简单、最广为人知的加密技术之一。它是一种替换密码,其中明文中的每个字母都被字母表中固定位置上的字母替换。例如,左移 3 位,D 将被 A 替换,E 将被 B 替换,依此类推。

供您参考的示例代码:

def encrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) + s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) + s - 97) % 26 + 97) 

        return result 

    def decrypt(text,s): 
        result = "" 

        # traverse text 
        for i in range(len(text)): 
            char = text[i] 

            # Encrypt uppercase characters 
            if (char.isupper()): 
                result += chr((ord(char) - s-65) % 26 + 65) 

            # Encrypt lowercase characters 
            else: 
                result += chr((ord(char) - s - 97) % 26 + 97) 

        return result 

    #check the above function 
    text = "ATTACKATONCE"
    s = 4
    print("Text  : " + text) 
    print("Shift : " + str(s)) 
    print("Cipher: " + encrypt(text,s))
    print("Original text: " + decrypt(encrypt(text,s),s))

优点:它满足您的要求,并且简单且可以执行编码操作。

缺点:可以通过简单的蛮力算法破解(极不可能有人会尝试查看所有额外的结果)。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用