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

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

就像这样:

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

因此字符串“ John Doe”被加密为‘ sjkl28cn2sx0’。要获得原始字符串,我需要用密钥‘ mypass’来“解锁”该字符串,这是我的源代码中的一个密码。我希望这是我用密码加密/解密 Word 文档的方法。

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

312093 次浏览

可以使用 AES 用密码对字符串进行加密。不过,您可能想选择一个足够强大的密码,这样人们就不能轻易猜到它是什么(对不起,我帮不上忙。)。我是个想当保安的胆小鬼)。

AES 具有很好的密钥大小,但是也很容易与 PyCrypto 一起使用。

由于您明确表示希望保持模糊性而不是安全性,因此我们将避免指责您的建议存在缺陷:)

因此,使用 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

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

例如,PyCrypto 中的 Cypher模块提供了许多加密算法的选择:

  • 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)。

这个工作,但密码长度应该正好是 8。这是很简单的,需要 派德斯

from pyDes import *


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


def decode(data,password):
k = des(password, CBC, "\0\0\0\0\0\0\0\0", 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

假设你是 只有寻找简单的混淆,将模糊的东西从 非常偶然的观察者,你不期待使用第三方库。我推荐维吉尼亚密码。这是最简单的古代密码之一。

维吉尼亚密码

它很容易实现,比如:

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(),而不是标准的参数。这将使用 URL 安全字符(顾名思义)替换 base64字母表中的一些字符。

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

如果需要安全加密:

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

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

这两个问题都将使用关键强化,这使得它们比这里的大多数其他答案更加安全。因为它们非常容易使用,所以即使在安全性不那么重要的情况下,你也可能想要使用它们... ..。

在@smehmood 的 维吉尼亚密码答案中提到的“ coding _ 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 加密有什么问题?

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

XOR 加密有什么问题?

Https://crypto.stackexchange.com/questions/56281/breaking-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!'

Base64 encode/decode 函数归功于 https://stackoverflow.com/a/2490376/241294(我是一个 Python 新手)。

在 python3中工作的 encode/decode 函数(很少参考 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)

@ 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

下面是@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 以反映这一点。.

如果您希望安全,可以使用 Fernet,它在密码学上是可靠的。如果你不想单独存储它,你可以使用一个静态的“ salt”——你只会丢失字典和彩虹攻击预防。我选择它,因为我可以选择长或短的密码,这是不那么容易与 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

如果这太复杂,有人建议简单加密

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

下面是使用 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

谢谢你的回答。没有什么原创的东西可以添加,但是这里有一些使用一些有用的 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

我给出四个解决方案:

1)使用带 cryptography库的 Fernet 加密

下面是一个使用 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')

您可以使用自己的 salt、迭代次数等来适应。这段代码离@HCLivess 的答案并不远,但是目标是拥有现成的 encryptdecrypt函数。资料来源: https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet

注意: 如果您想要字符串 'John Doe'而不是像 b'John Doe'那样的字节,那么可以在任何地方使用 .encode().decode()


2)简单 AES 加密与 Crypto

这适用于 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)使用更好的密码密钥导出函数和 Crypto库测试是否输入了错误密码

使用 AES“ CFB 模式”的解决方案2是可以的,但是有两个缺点: 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_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\xa50', b'mypass'))
try:
print(decrypt3(b'\xbaN_\x90R\xdf\xa9\xc7\xd6\x16/\xbb!\xf5Q\xa9]\xe5\xa5\xaf\x81\xc3\n2e/("I\xb4\xab5\xa6ezu\x8c%\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'\x88\xaf\xc1\x04\x8b\x98\x18\x9a'
print(encryptRC4(b'\x88\xaf\xc1\x04\x8b\x98\x18\x9a', b'mypass'))   # b'John Doe'

(自从最近的编辑以来已经过时了,但是保留了以备将来参考) : 我在使用 Windows + Python 3.6 + 时遇到了问题,所有的答案都涉及到 pycrypto(在 Windows 上不能解决 pip install pycrypto)或者 pycryptodome(这里的 from Crypto.Cipher import XOR的答案失败了,因为 pycrypto分支不支持 XOR; 而使用 ... AES的解决方案在使用 TypeError: Object type <class 'str'> cannot be passed to C code时也失败了)。而且,库 pip install pycrypto0有 pycrypto作为依赖项,所以它不是一个选项。

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

然而,实现一个正确的加密方案并不需要做太多工作。首先,不要重新发明密码学的轮子使用可信的加密库来处理这个问题。对于 Python3,这个受信任的库是 cryptography

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

最后但并非最不重要的,当加密和解密,我们谈论 钥匙,而不是密码。一把钥匙不应该让人记忆深刻,它应该存放在一个秘密的地方,但机器可以读取,而密码通常可以让人读取和记忆。您需要小心地从密码中派生一个密钥。

但是,对于在集群中运行的 Web 应用程序或进程,如果没有人工关注来继续运行它,则需要使用一个密钥。密码只适用于最终用户需要访问特定信息的情况。即使这样,您也通常使用密码保护应用程序,然后使用密钥(可能是附加到用户帐户的密钥)交换加密信息。

对称密钥加密

Fernet-AES CBC + HMAC 强烈建议

cryptography库包括 Fernet 的食谱,这是使用密码学的最佳实践配方, 它为您打包了 AES CBC 加密和版本信息、时间戳和 HMAC 签名,以防止消息篡改。

Fernet 使加密和解密消息 还有变得非常容易,从而保证了您的安全。它是使用秘密加密数据的理想方法。

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

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

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'
>>> encrypt(message.encode(), key)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> token = _
>>> decrypt(token, key).decode()
'John Doe'

带密码的 Fernet-从密码派生的密钥,有些削弱了安全性

如果您使用 使用强密钥派生方法,则可以使用密码而不是密钥。然后您必须在消息中包含 salt 和 HMAC 迭代计数,所以如果不先分离 salt、 count 和 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'

在输出中包含 salt 可以使用随机 salt 值,这反过来又可以确保加密的输出是完全随机的,无论密码重用或消息重复如何。包含迭代计数可以确保您可以根据 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='

只要正直

如果所有你需要的是一种方法,以确保数据可以被信任为 原封不动后,已被发送到一个不受信任的客户端和接收回来,然后你想签署的数据,你可以使用 hmac为此与 SHA1(仍然是 认为 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 使用 HMAC 签名建立在 AEC-CBC 的基础上,以确保加密数据的完整性; 恶意攻击者不能向您的系统提供无意义的数据,以使您的服务在错误输入的情况下忙于运行,因为加密文本是签名的。

伽罗瓦/计数模式分组密码生成密文和 标签用于相同的目的,因此可以用于相同的目的。不足之处在于,与 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 所支持的相同的 time-to-live 用例。

本页中的其他方法,在 Python3中

AES CFB-但不需要垫

这是 都是 V 星人遵循的方法,尽管是错误的。这是 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 签名的装甲,也没有时间戳; 您必须自己添加它们。

上面还说明了错误地组合基本加密构建块是多么容易; 所有 V y 对 IV 值的不正确处理都可能导致数据泄漏或所有加密消息因 IV 丢失而无法读取。相反,使用 Fernet 可以保护您免受这些错误的影响。

AES 欧洲央行 -不安全

如果您以前实现过 欧洲央行加密,并且需要在 Python3中仍然支持它,那么也可以在 cryptography中这样做。同样的警告也适用于欧洲央行,它对于现实生活中的应用程序来说不够安全.重新实现 Python3的答案,添加自动处理填充:

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 的签名,而且你无论如何也不应该使用欧洲央行的签名。上面只是说明 cryptography可以处理常见的加密构建块,甚至是那些您实际上不应该使用的构建块。

这个答案对于安全是很糟糕的。不要用于任何敏感的东西

无论是谁来到这里(还是赏金猎人) ,似乎都在寻找没有太多设置的俏皮话,而其他答案没有提供这种设置。所以我提出64进制。

现在,请记住,这只是基本的模糊处理,并且是在 没办法确保安全 中,但是这里有一些一行程序:

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'

因此,作为 没有任何关键任务被加密,你只需要对 混淆视听进行加密。

让我来介绍一下 凯撒密码

enter image description here

凯撒密码或凯撒移位,是最简单和最广为人知的加密技术之一。这是一种替换式密码,其中明文中的每个字母都被字母表中一些固定位置的字母所替代。例如,如果左移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))

优点: 它满足您的需求,并且很简单,可以进行“ y”编码。

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

添加一个解码和编码的代码供参考

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("\n", " 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","\n")
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()

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

pip install cryptocode

加密消息(示例代码) :

import cryptocode


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

文档可以找到 给你