Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 加密&;使用PyCrypto AES 256解密_Python_Encryption_Padding_Pycrypto_Initialization Vector - Fatal编程技术网

Python 加密&;使用PyCrypto AES 256解密

Python 加密&;使用PyCrypto AES 256解密,python,encryption,padding,pycrypto,initialization-vector,Python,Encryption,Padding,Pycrypto,Initialization Vector,我正在尝试使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后对消息进行加密/解密 我在网上找到了几个帮助我解决问题的链接,但每个链接都有缺陷: 使用os.Uradom,PyCrypto不鼓励使用它 此外,我给函数的键不能保证具有预期的精确长度。我能做些什么来实现这一点 此外,还有几种模式,建议使用哪种模式?我不知道该用什么:/ 最后,什么是静脉注射?我可以为加密和解密提供一个不同的IV吗,或者这将以不同的结果返回 编辑:删除了不安全的代码部分。您可以使用加密哈希函数(而不是Py

我正在尝试使用PyCrypto构建两个函数,它们接受两个参数:消息和密钥,然后对消息进行加密/解密

我在网上找到了几个帮助我解决问题的链接,但每个链接都有缺陷:

使用os.Uradom,PyCrypto不鼓励使用它

此外,我给函数的键不能保证具有预期的精确长度。我能做些什么来实现这一点

此外,还有几种模式,建议使用哪种模式?我不知道该用什么:/

最后,什么是静脉注射?我可以为加密和解密提供一个不同的IV吗,或者这将以不同的结果返回


编辑:删除了不安全的代码部分。

您可以使用加密哈希函数(而不是Python的内置哈希函数,如SHA-1或SHA-256)从任意密码中获取密码短语。Python在其标准库中包括对以下两个方面的支持:

import hashlib

hashlib.sha1("this is my awesome password").digest() # => a 20 byte string
hashlib.sha256("another awesome password").digest() # => a 32 byte string

只需使用
[:16]
[:24]
即可截断加密哈希值,它将在您指定的长度内保持其安全性。

您可能需要以下两个函数:
pad
-到pad(加密时)和
unpad
-到unpad(解密时)当输入长度不是块大小的倍数时

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 
unpad = lambda s : s[:-ord(s[len(s)-1:])]
你问的是钥匙的长度?您可以使用键的md5sum,而不是直接使用它

此外,根据我使用PyCrypto的一点经验,当输入相同时,IV用于混合加密的输出,因此选择IV作为随机字符串,并将其用作加密输出的一部分,然后使用它解密消息

以下是我的实现,希望对您有用:

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

class AESCipher:
    def __init__( self, key ):
        self.key = key

    def encrypt( self, raw ):
        raw = pad(raw)
        iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_CBC, iv )
        return base64.b64encode( iv + cipher.encrypt( raw ) ) 

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return unpad(cipher.decrypt( enc[16:] ))

为了其他人的利益,这里是我通过结合@Cyril和@Marcus的答案得到的解密实现。这假设这是通过HTTP请求传入的,带有带引号的encryptedText和base64编码

import base64
import urllib2
from Crypto.Cipher import AES


def decrypt(quotedEncodedEncrypted):
    key = 'SecretKey'

    encodedEncrypted = urllib2.unquote(quotedEncodedEncrypted)

    cipher = AES.new(key)
    decrypted = cipher.decrypt(base64.b64decode(encodedEncrypted))[:16]

    for i in range(1, len(base64.b64decode(encodedEncrypted))/16):
        cipher = AES.new(key, AES.MODE_CBC, base64.b64decode(encodedEncrypted)[(i-1)*16:i*16])
        decrypted += cipher.decrypt(base64.b64decode(encodedEncrypted)[i*16:])[:16]

    return decrypted.strip()

这是我的实现,它为我提供了一些修复,并增强了密钥和密码短语与32字节和iv到16字节的对齐:

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

class AESCipher(object):

    def __init__(self, key): 
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode()))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

对于希望使用urlsafe_b64encode和urlsafe_b64decode的人,以下是我的工作版本(在花了一些时间处理unicode问题之后)


虽然有点晚了,但我认为这会很有帮助。没有人提到像PKCS#7填充这样的使用方案。您可以使用它来代替前面的函数pad(加密时)和unpad(解密时)

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
import pkcs7
class Encryption:

    def __init__(self):
        pass

    def Encrypt(self, PlainText, SecurePassword):
        pw_encode = SecurePassword.encode('utf-8')
        text_encode = PlainText.encode('utf-8')

        key = hashlib.sha256(pw_encode).digest()
        iv = Random.new().read(AES.block_size)

        cipher = AES.new(key, AES.MODE_CBC, iv)
        pad_text = pkcs7.encode(text_encode)
        msg = iv + cipher.encrypt(pad_text)

        EncodeMsg = base64.b64encode(msg)
        return EncodeMsg

    def Decrypt(self, Encrypted, SecurePassword):
        decodbase64 = base64.b64decode(Encrypted.decode("utf-8"))
        pw_encode = SecurePassword.decode('utf-8')

        iv = decodbase64[:AES.block_size]
        key = hashlib.sha256(pw_encode).digest()

        cipher = AES.new(key, AES.MODE_CBC, iv)
        msg = cipher.decrypt(decodbase64[AES.block_size:])
        pad_text = pkcs7.decode(msg)

        decryptedString = pad_text.decode('utf-8')
        return decryptedString

这是另一种观点(主要源自上述解决方案),但

  • 使用null作为填充
  • 不使用lambda(从未使用过风扇)
  • 使用python 2.7和3.6.5进行测试

    #!/usr/bin/python2.7
    # you'll have to adjust for your setup, e.g., #!/usr/bin/python3
    
    
    import base64, re
    from Crypto.Cipher import AES
    from Crypto import Random
    from django.conf import settings
    
    class AESCipher:
        """
          Usage:
          aes = AESCipher( settings.SECRET_KEY[:16], 32)
          encryp_msg = aes.encrypt( 'ppppppppppppppppppppppppppppppppppppppppppppppppppppppp' )
          msg = aes.decrypt( encryp_msg )
          print("'{}'".format(msg))
        """
        def __init__(self, key, blk_sz):
            self.key = key
            self.blk_sz = blk_sz
    
        def encrypt( self, raw ):
            if raw is None or len(raw) == 0:
                raise NameError("No value given to encrypt")
            raw = raw + '\0' * (self.blk_sz - len(raw) % self.blk_sz)
            raw = raw.encode('utf-8')
            iv = Random.new().read( AES.block_size )
            cipher = AES.new( self.key.encode('utf-8'), AES.MODE_CBC, iv )
            return base64.b64encode( iv + cipher.encrypt( raw ) ).decode('utf-8')
    
        def decrypt( self, enc ):
            if enc is None or len(enc) == 0:
                raise NameError("No value given to decrypt")
            enc = base64.b64decode(enc)
            iv = enc[:16]
            cipher = AES.new(self.key.encode('utf-8'), AES.MODE_CBC, iv )
            return re.sub(b'\x00*$', b'', cipher.decrypt( enc[16:])).decode('utf-8')
    

让我来回答你关于“模式”的问题。AES256是一种分组密码。它将一个32字节的键和一个16字节的字符串(称为块)作为输入,并输出一个块。我们在操作模式下使用AES进行加密。上述解决方案建议使用CBC,这是一个例子。另一种称为CTR,使用起来比较简单:

from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random

# AES supports multiple key sizes: 16 (AES128), 24 (AES192), or 32 (AES256).
key_bytes = 32

# Takes as input a 32-byte key and an arbitrary-length plaintext and returns a
# pair (iv, ciphtertext). "iv" stands for initialization vector.
def encrypt(key, plaintext):
    assert len(key) == key_bytes

    # Choose a random, 16-byte IV.
    iv = Random.new().read(AES.block_size)

    # Convert the IV to a Python integer.
    iv_int = int(binascii.hexlify(iv), 16) 

    # Create a new Counter object with IV = iv_int.
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Encrypt and return IV and ciphertext.
    ciphertext = aes.encrypt(plaintext)
    return (iv, ciphertext)

# Takes as input a 32-byte key, a 16-byte IV, and a ciphertext, and outputs the
# corresponding plaintext.
def decrypt(key, iv, ciphertext):
    assert len(key) == key_bytes

    # Initialize counter for decryption. iv should be the same as the output of
    # encrypt().
    iv_int = int(iv.encode('hex'), 16) 
    ctr = Counter.new(AES.block_size * 8, initial_value=iv_int)

    # Create AES-CTR cipher.
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)

    # Decrypt and return the plaintext.
    plaintext = aes.decrypt(ciphertext)
    return plaintext

(iv, ciphertext) = encrypt(key, 'hella')
print decrypt(key, iv, ciphertext)
这通常被称为AES-CTR我建议将AES-CBC与PyCrypto一起使用时要小心。原因是它要求您指定填充方案,如给出的其他解决方案所示。一般来说,如果你对填充不太小心,有完全破坏加密的

现在,需要注意的是,密钥必须是一个随机的32字节字符串;密码不够用。通常,密钥的生成方式如下:

# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
密钥也可以从密码派生而来:

# It's also possible to derive a key from a password, but it's important that
# the password have high entropy, meaning difficult to predict.
password = "This is a rather weak password."

# For added # security, we add a "salt", which increases the entropy.
#
# In this example, we use the same RNG to produce the salt that we used to
# produce key1.
salt_bytes = 8 
salt = Random.new().read(salt_bytes)

# Stands for "Password-based key derivation function 2"
key2 = PBKDF2(password, salt, key_bytes)
上面的一些解决方案建议使用SHA256来派生密钥,但通常会考虑这一点。
查看更多关于操作模式的信息。

感谢其他的答案,这些答案启发了我,但对我来说并不奏效

在花了几个小时试图弄清楚它是如何工作的之后,我用最新的PyCryptodomex库想出了下面的实现(这是另一个故事,我是如何在代理之后,在Windows上,在virtualenv..phew中设置它的)

在您的实现上工作的,记住写下填充、编码、,加密步骤(反之亦然)。你必须记住顺序来包装和解包

import base64 import hashlib from Cryptodome.Cipher import AES from Cryptodome.Random import get_random_bytes __key__ = hashlib.sha256(b'16-character key').digest() def encrypt(raw): BS = AES.block_size pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) raw = base64.b64encode(pad(raw).encode('utf8')) iv = get_random_bytes(AES.block_size) cipher = AES.new(key= __key__, mode= AES.MODE_CFB,iv= iv) return base64.b64encode(iv + cipher.encrypt(raw)) def decrypt(enc): unpad = lambda s: s[:-ord(s[-1:])] enc = base64.b64decode(enc) iv = enc[:AES.block_size] cipher = AES.new(__key__, AES.MODE_CFB, iv) return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8')) 导入base64 导入hashlib 从Cryptodome.Cipher导入AES 从Cryptodome.Random导入获取\u随机\u字节 __key=hashlib.sha256(b'16个字符的key').digest() def加密(原始): BS=AES.块大小 pad=λs:s+(BS-长度%BS)*chr(BS-长度%BS) raw=base64.b64编码(pad(raw).encode('utf8')) iv=获取随机字节(AES.块大小) 密码=AES.new(key=\uuuuu key\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 返回base64.b64编码(iv+密码加密(原始)) def解密(enc): unpad=lambda s:s[:-ord(s[-1:])] enc=base64.b64解码(enc) iv=附件[:AES.块大小] 密码=AES.new(_密钥_uu,AES.MODE CFB,iv) 返回unpad(base64.b64解码(cipher.decrypt(enc[AES.block\u size:])).decode('utf8'))

兼容utf-8编码

def _pad(self, s):
    s = s.encode()
    res = s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs).encode()
    return res

我已经使用了
Crypto
PyCryptodomex
库,它的速度非常快

import base64
import hashlib
from Cryptodome.Cipher import AES as domeAES
from Cryptodome.Random import get_random_bytes
from Crypto import Random
from Crypto.Cipher import AES as cryptoAES

BLOCK_SIZE = AES.block_size

key = "my_secret_key".encode()
__key__ = hashlib.sha256(key).digest()
print(__key__)

def encrypt(raw):
    BS = cryptoAES.block_size
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    raw = base64.b64encode(pad(raw).encode('utf8'))
    iv = get_random_bytes(cryptoAES.block_size)
    cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
    a= base64.b64encode(iv + cipher.encrypt(raw))
    IV = Random.new().read(BLOCK_SIZE)
    aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
    b = base64.b64encode(IV + aes.encrypt(a))
    return b

def decrypt(enc):
    passphrase = __key__
    encrypted = base64.b64decode(enc)
    IV = encrypted[:BLOCK_SIZE]
    aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
    enc = aes.decrypt(encrypted[BLOCK_SIZE:])
    unpad = lambda s: s[:-ord(s[-1:])]
    enc = base64.b64decode(enc)
    iv = enc[:cryptoAES.block_size]
    cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
    b=  unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
    return b

encrypted_data =encrypt("Hi Steven!!!!!")
print(encrypted_data)
print("=======")
decrypted_data = decrypt(encrypted_data)
print(decrypted_data)

如果您的输入正好是块大小的倍数,会发生什么情况?我认为unpad函数会有点混乱…@Kjir,然后一个长度为BLOCK_SIZE的值chr(BS)序列将被追加到原始数据中。@Marcus
pad
函数被破坏(至少在Py3中),替换为
s[:-ord(s[len(s)-1:])
它可以跨版本工作。@带pycryptodome(pycrypto followup)的CryptoUtil.Padding.pad()中可以使用Torxed pad函数,为什么不将字符常量作为 import base64 import hashlib from Cryptodome.Cipher import AES from Cryptodome.Random import get_random_bytes __key__ = hashlib.sha256(b'16-character key').digest() def encrypt(raw): BS = AES.block_size pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) raw = base64.b64encode(pad(raw).encode('utf8')) iv = get_random_bytes(AES.block_size) cipher = AES.new(key= __key__, mode= AES.MODE_CFB,iv= iv) return base64.b64encode(iv + cipher.encrypt(raw)) def decrypt(enc): unpad = lambda s: s[:-ord(s[-1:])] enc = base64.b64decode(enc) iv = enc[:AES.block_size] cipher = AES.new(__key__, AES.MODE_CFB, iv) return unpad(base64.b64decode(cipher.decrypt(enc[AES.block_size:])).decode('utf8'))
def _pad(self, s):
    s = s.encode()
    res = s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs).encode()
    return res
import base64
import hashlib
from Cryptodome.Cipher import AES as domeAES
from Cryptodome.Random import get_random_bytes
from Crypto import Random
from Crypto.Cipher import AES as cryptoAES

BLOCK_SIZE = AES.block_size

key = "my_secret_key".encode()
__key__ = hashlib.sha256(key).digest()
print(__key__)

def encrypt(raw):
    BS = cryptoAES.block_size
    pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
    raw = base64.b64encode(pad(raw).encode('utf8'))
    iv = get_random_bytes(cryptoAES.block_size)
    cipher = cryptoAES.new(key= __key__, mode= cryptoAES.MODE_CFB,iv= iv)
    a= base64.b64encode(iv + cipher.encrypt(raw))
    IV = Random.new().read(BLOCK_SIZE)
    aes = domeAES.new(__key__, domeAES.MODE_CFB, IV)
    b = base64.b64encode(IV + aes.encrypt(a))
    return b

def decrypt(enc):
    passphrase = __key__
    encrypted = base64.b64decode(enc)
    IV = encrypted[:BLOCK_SIZE]
    aes = domeAES.new(passphrase, domeAES.MODE_CFB, IV)
    enc = aes.decrypt(encrypted[BLOCK_SIZE:])
    unpad = lambda s: s[:-ord(s[-1:])]
    enc = base64.b64decode(enc)
    iv = enc[:cryptoAES.block_size]
    cipher = cryptoAES.new(__key__, cryptoAES.MODE_CFB, iv)
    b=  unpad(base64.b64decode(cipher.decrypt(enc[cryptoAES.block_size:])).decode('utf8'))
    return b

encrypted_data =encrypt("Hi Steven!!!!!")
print(encrypted_data)
print("=======")
decrypted_data = decrypt(encrypted_data)
print(decrypted_data)