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

Rex*_*exE 109 python encryption passwords

Python是否有使用密码编码/解码字符串的内置简单方法?

像这样的东西:

>>> encode('John Doe', password = 'mypass')
'sjkl28cn2sx0'
>>> decode('sjkl28cn2sx0', password = 'mypass')
'John Doe'
Run Code Online (Sandbox Code Playgroud)

所以字符串"John Doe"被加密为'sjkl28cn2sx0'.要获取原始字符串,我将使用密钥'mypass'"解锁"该字符串,这是我的源代码中的密码.我希望这是我用密码加密/解密Word文档的方式.

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

Wil*_*ill 66

当你明确表示你想要默默无闻而不是安全时,我们会避免因你建议的弱点而谴责你:)

所以,使用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)
Run Code Online (Sandbox Code Playgroud)

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

  • 填充示例:http://paste.ubuntu.com/11024555/它适用于任意密码和消息长度. (6认同)
  • @Ethan不,这个特殊的`encrypt`函数是有状态的https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.blockalgo.BlockAlgo-class.html#encrypt所以你不应该尝试重用它. (3认同)
  • 重新 - "永远不要在强大的系统中使用ECB":我只是为了自己的乐趣而尝试这个.但我在你的代码中看到了上面的评论.对于我们这些具有极低安全性/加密/信息理论背景的人来说,为什么"永远不会使用"?也许需要另一个问题......或者可能有一个链接. (3认同)
  • 我不认为这将起作用,除非msg_text的长度是16个字节的倍数,因为AES加密需要长度为16的倍数的块.任意长度的msg_text的工作实现都需要在字符串中添加填充以使其长度为16的倍数. (2认同)

sme*_*ood 64

假设您只是在寻找简单的模糊处理方法,这些模糊方法会使非常随意的观察者模糊不清,而您并不打算使用第三方库.我推荐像Vigenere密码这样的东西.它是最简单的古代密码之一.

Vigenère密码

它的实施快速简便.就像是:

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)
Run Code Online (Sandbox Code Playgroud)

除了你减去密钥之外,解码几乎是一样的.

如果您编码的字符串很短,并且/或者很难猜测所使用的密码的长度,则更难打破.

如果您正在寻找加密的东西,PyCrypto可能是您最好的选择,虽然之前的答案忽略了一些细节:PyCrypto中的ECB模式要求您的消息长度为16个字符的倍数.所以,你必须垫.此外,如果要将它们用作URL参数,请使用base64.urlsafe_b64_encode()而不是标准参数.这将使用URL安全字符替换base64字母表中的一些字符(顾名思义).

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

  • 我修复了smehmood的脚本,并添加了解码功能https://gist.github.com/ilogik/6f9431e4588015ecb194 (6认同)
  • 注意!smehmood的代码和阿德里安·梅斯特的修正都只适用于字符串来自较低的ascii范围!请参阅%operator,unicode input等的优先顺序.请参阅qneill的工作代码答案 (3认同)
  • "for x in xrange(string)"可能需要转换为"for x in xrange(len(string))" (3认同)
  • python 2和3的编码器和解码器:https://gist.github.com/gowhari/fea9c559f08a310e5cfd62978bc86a1a (3认同)
  • @smehmood我得到以下错误`'str'对象不能被解释为整数` (2认同)

qne*_*ill 48

@ 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)
Run Code Online (Sandbox Code Playgroud)

  • 大!此版本也适用于带重音的字符串,而@ smehmood的版本则不适用.用法示例:`encodedmsg = encode('mypassword','这是消息éçàèç"')``print encodedmsg``打印解码('mypassword',encodedmsg)`,它工作正常. (3认同)
  • 非常有用,谢谢.我在下面发布了一个Python 3版本(在评论中看起来很难看) (2认同)
  • [这是一个Sublime文本插件](https://gist.github.com/josephernest/ea79e7ef7ef652194d4db66d1ae17010)基于此代码,允许使用CTRL + SHIFT + P然后"Eeencode"或"Dddecode"轻松编码/解码文本. (2认同)
  • @basj谢谢你的例子 (2认同)

Rya*_*ett 46

这是@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)
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢Ryan,你错了@qniell (4认同)
  • 对于那些想知道,差异是`.encode()).decode()`.在`decode()`的第二行中返回`encode()`和`.decode()`. (3认同)
  • 嗯,编码的代码没有真正独一无二的,我跑了测试,它显示的11,22,33,44每个代码键,...,88,99,111,222 ......总是像以前那样有另一个相同的代码.但我很感激 (2认同)

Mar*_*ers 46

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())
Run Code Online (Sandbox Code Playgroud)

要加密或解密消息,请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)
Run Code Online (Sandbox Code Playgroud)

演示:

>>> 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'
Run Code Online (Sandbox Code Playgroud)

带有密码的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)
Run Code Online (Sandbox Code Playgroud)

演示:

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

将盐包含在输出中可以使用随机盐值,这又可以确保无论密码重用或消息重复如何,加密输出都可以保证是完全随机的。包含迭代计数可确保您可以适应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))
Run Code Online (Sandbox Code Playgroud)

这变成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)
Run Code Online (Sandbox Code Playgroud)

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

key = secrets.token_bytes(32)
Run Code Online (Sandbox Code Playgroud)

您可能想看一下itsdangerous,该通过各种格式的序列化和反序列化将所有内容打包在一起。

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

Fernet建立在具有HMAC签名的AEC-CBC上,以确保加密数据的完整性。恶意攻击者无法输入您的系统废话数据,以使您的服务在输入错误的情况下仍无法正常运行,因为密文已签名。

所述伽罗瓦/计数器模式块密码产生密文和标签服务于相同的目的,因此可用于服务于相同的目的。缺点是,与Fernet不同,没有一种易于使用的“一刀切”的配方可以在其他平台上重复使用。AES-GCM也不使用填充,因此此加密密文与输入消息的长度匹配(而Fernet / AES-CBC将消息加密为固定长度的块,从而使消息长度有些模糊)。

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

key = secrets.token_bytes(32)
Run Code Online (Sandbox Code Playgroud)

然后使用

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()
Run Code Online (Sandbox Code Playgroud)

我提供了一个时间戳,以支持Fernet支持的相同的生存时间用例。

此页面上的其他方法,使用Python 3

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

这就是所有的做法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()
Run Code Online (Sandbox Code Playgroud)

这缺少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()
Run Code Online (Sandbox Code Playgroud)

同样,这缺少HMAC签名,因此您无论如何都不应使用ECB。以上只是为了说明cryptography可以处理常见的密码构造块,甚至您实际上不应该使用的那些。

  • 我知道这个问题已经很老了,但这确实需要成为公认的答案。 (4认同)

poi*_*ida 24

如前所述,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))
Run Code Online (Sandbox Code Playgroud)

即使它只提供最小的安全性,我仍然建议使用一个没有任何空格字符的随机查找键(因为XOR'ing ASCII [a-zA-Z]字符,空格只是翻转案例).

密码的工作方式如下,无需填充明文:

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

>>> decrypt('notsosecretkey', encrypt('notsosecretkey', 'Attack at dawn!'))
'Attack at dawn!'
Run Code Online (Sandbox Code Playgroud)

感谢/sf/answers/174326351/为以base64编码/解码功能(我是一个新手的Python).

  • 注意:pycrypto最终无法在herokuapp上安装。我发现此帖子..似乎表明pycrypto软件包已被另一个名为pycryptodome insteal的软件包所替代,并且不赞成使用XOR方法:https://github.com/digitalocean/netbox/issues/1527 (2认同)
  • ***永远不要使用此方法***,请注意[文档]中对“密码”的描述(https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.XOR -module.html):* XOR玩具密码,XOR是最简单的流密码之一。加密和解密是通过对数据进行XOR处理(通过对密钥进行污染而生成的密钥流)来执行的。不要在实际应用中使用它!*。 (2认同)

小智 13

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

pip install cryptocode
Run Code Online (Sandbox Code Playgroud)

加密消息(示例代码):

import cryptocode

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

文档可以在这里找到


All*_*ітy 12

这是使用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))
Run Code Online (Sandbox Code Playgroud)

如果你遇到这样的问题https://bugs.python.org/issue4329(TypeError:字符映射必须返回整数,无或unicode)使用str(cipher)解码时如下

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

return obj2.decrypt(base64.urlsafe_b64decode(str(cipher)))
Run Code Online (Sandbox Code Playgroud)

  • 不要生成 IV 并将其存储在模块级别,您需要将 IV 包含在返回的密文消息中!您现在引入了两个问题:重新启动Python进程会为您提供一个新的IV,使其无法解密以前加密的消息,同时您将IV重新用于多个消息,这有效地将安全性降低回ECB级别。 (2认同)

小智 8

在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)
Run Code Online (Sandbox Code Playgroud)


HCL*_*ess 7

如果你想安全,你可以使用 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
Run Code Online (Sandbox Code Playgroud)

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

from simplecrypt import encrypt, decrypt
ciphertext = encrypt('password', plaintext)
plaintext = decrypt('password', ciphertext)
Run Code Online (Sandbox Code Playgroud)


Nic*_*ick 6

感谢您的出色回答。无需添加任何原始内容,但这是使用一些有用的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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

使用列表理解构建列表

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)
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

和一些测试...

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
Run Code Online (Sandbox Code Playgroud)


Bas*_*asj 6

我将给出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')
Run Code Online (Sandbox Code Playgroud)

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

注意:使用.encode().decode()无处不在,如果你想字符串'John Doe',而不是像字节b'John Doe'


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
Run Code Online (Sandbox Code Playgroud)

注意:您可以删除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_\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")
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

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

  • 您不想对盐和迭代计数进行硬编码;生成随机盐并使迭代计数在加密时可配置,将该信息包含在加密结果中,并在解密时提取和使用这些值。盐可以保护您免受给定消息上重复使用的密码的琐碎识别,迭代计数可以使算法面向未来。 (2认同)