问题:根据密码对字符串编码的简单方法?
Python是否有内置的,使用密码对字符串进行编码/解码的简单方法?
像这样:
>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'
因此,字符串“ John Doe”被加密为“ sjkl28cn2sx0”。要获取原始字符串,我将使用密钥“ mypass”“解锁”该字符串,这是我的源代码中的密码。我希望这是我可以使用密码加密/解密Word文档的方法。
我想将这些加密的字符串用作URL参数。我的目标是混淆,而不是强大的安全性;没有关键任务被编码。我意识到我可以使用数据库表存储键和值,但是我试图做到极简。
回答 0
假设您只是在寻找简单的混淆方法,以免使非常随意的观察者感到困惑,并且您没有在使用第三方库。我建议您使用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()
,而不是标准参数。这用URL安全字符替换了base64字母中的一些字符(顾名思义)。
然而,你应该是绝对肯定的是,这非常薄混淆层就足够了使用在此之前您的需求。我链接到的Wikipedia文章提供了有关破解密码的详细说明,因此,只要有足够的决心,任何人都可以轻松破解它。
回答 1
当您明确声明您想要模糊而不是安全性时,我们将避免因您所建议的缺点而谴责您:)
因此,使用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
!
回答 2
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
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'
将盐包含在输出中可以使用一个随机的盐值,这又可以确保无论密码重用或消息重复如何,加密的输出都可以保证是完全随机的。包含迭代计数可确保您可以适应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支持的相同的生存时间用例。
本页的其他方法,使用Python 3
AES CFB- 类似于CBC,但无需填充
这是万事俱备的方法,尽管有误。这是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 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
可以处理常见的密码构造块,甚至您实际上不应该使用的那些。
回答 3
@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)
免责声明:正如评论所暗示的那样,除非您已阅读并且不介意与律师交谈,否则不应将其用于保护实际应用程序中的数据:
回答 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
免责声明:如评论中所述,不应将其用于保护实际应用程序中的数据。
/crypto/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!'
归功于https://stackoverflow.com/a/2490376/241294,以获得base64编码/解码功能(我是python新手)。
回答 6
这是使用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
回答 7
在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)
回答 8
感谢您的出色回答。无需添加任何原始内容,但此处使用一些有用的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
回答 9
如果您想安全起见,可以使用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)
回答 10
谁来了这里(和最富裕的人)似乎都在寻找没有太多设置的单线,而其他答案没有提供。所以我提出了base64。
现在,请记住,这仅是基本的混淆,并且在**不能保证安全**中使用,但是这里有一些内容:
from base64 import urlsafe_b64encode, urlsafe_b64decode
def encode(data, key):
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'
回答 11
我将给出4种解决方案:
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')
您可以使用自己的盐,迭代次数等进行调整。这段代码与@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
库测试“是否输入了错误密码”的能力
带有AES“ CFB模式”的解决方案2)可以,但是有两个缺点:SHA256(password)
使用查找表可以很容易地对它进行暴力破解,并且无法测试是否输入了错误的密码。如AES中所讨论的,这可以通过在“ GCM模式”中使用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
(无法pip install pycrypto
在Windows上)或pycryptodome
(此处的答案from Crypto.Cipher import XOR
失败,因为XOR
此pycrypto
fork 不支持此答案)时遇到问题;以及使用的解决方案也... AES
失败了TypeError: Object type <class 'str'> cannot be passed to C code
)。另外,该库simple-crypt
具有pycrypto
as依赖关系,因此不是一种选择。
回答 12
此方法有效,但密码长度应正确8
。这很简单,需要pyDes。
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
回答 13
@qneill代码的另一种实现,包括原始消息的CRC校验和,如果校验失败,则会抛出异常:
import hashlib
import struct
import zlib
def vigenere_encode(text, key):
text = '{}{}'.format(text, struct.pack('i', zlib.crc32(text)))
enc = []
for i in range(len(text)):
key_c = key[i % len(key)]
enc_c = chr((ord(text[i]) + ord(key_c)) % 256)
enc.append(enc_c)
return base64.urlsafe_b64encode("".join(enc))
def vigenere_decode(encoded_text, key):
dec = []
encoded_text = base64.urlsafe_b64decode(encoded_text)
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]
assert zlib.crc32(dec) == struct.unpack('i', checksum)[0], 'Decode Checksum Error'
return dec
回答 14
您可以使用AES使用密码来加密您的字符串。但是,您需要选择一个足够强大的密码,以使人们无法轻易猜出它是什么(对不起,我帮不上忙。我想成为一个安全的中间人)。
AES强度高,密钥大小合适,但与PyCrypto结合使用也很容易。
回答 15
外部库提供密钥加密算法。
例如,Cypher
PyCrypto中的模块提供了许多加密算法的选择:
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是OpenSSL的Python
包装器,并(除其他功能外)提供完整的通用加密库。其中包括对称密码(如AES)。
回答 16
如果要安全加密:
对于python 2,您应该使用keyczar http://www.keyczar.org/
对于python 3,直到keyczar可用为止,我已经编写了simple-crypt http://pypi.python.org/pypi/simple-crypt
这两个都将使用密钥加强,这使其比此处的其他大多数答案更安全。而且由于它们非常易于使用,即使在安全性不是很关键的情况下,您也可能希望使用它们。
回答 17
所以,没有任何关键任务被编码,而你只是想为加密obsfuscation。
我来介绍一下凯撒的密码
Caesar的密码或Caesar移位是最简单且最广为人知的加密技术之一。这是一种替换密码,其中明文中的每个字母都由一个字母固定下来,位于字母下方一定数量的位置。例如,左移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’。
缺点:可以通过简单的蛮力算法来破解(几乎不可能有人尝试通过所有额外的结果)。
回答 18
添加更多代码并进行解码和编码以供参考
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()