私はPyCryptoを使って2つのパラメータを受け取る2つの関数を構築しようとしています:メッセージとキー、そしてメッセージを暗号化/復号化します。
私は私を助けるためにウェブ上にいくつかのリンクを見つけました、しかしそれらのそれぞれに欠陥があります:
codekoala のこれはos.urandomを使用します。これはPyCryptoによって推奨されていません。
さらに、私が関数に与えるキーが期待される正確な長さを持つことが保証されていません。それを実現するために私は何ができますか?
また、いくつかのモードがありますが、どれをお勧めしますか。私は何を使うべきかわからない:/
最後に、IVは何ですか?暗号化と復号化に別のIVを提供できますか?それとも別の結果になりますか?
これが私がこれまでにやったことです:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BLOCK_SIZE=32
def encrypt(message, passphrase):
# passphrase MUST be 16, 24 or 32 bytes long, how can I do that ?
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return base64.b64encode(aes.encrypt(message))
def decrypt(encrypted, passphrase):
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return aes.decrypt(base64.b64decode(encrypted))
これが私の実装であり、いくつかの修正を加えて私のために働き、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 = 32
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))
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:])]
入力の長さがBLOCK_SIZEの倍数ではない場合に、パディング(暗号化するとき)とアンパッド(復号化するとき)には、次の2つの関数が必要になるかもしれません。
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:] ))
「モード」についてのあなたの質問に答えましょう。 AES256は一種の ブロック暗号 です。入力として32バイトの key と16バイトの文字列( block と呼ばれる)を取り、ブロックを出力します。暗号化するために、AESを 動作モード で使用します。上記の解決策はCBCの使用を示唆していますが、これは一例です。もう1つは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と呼ばれます。 PyCrypto と共にAES-CBCを使用する際の注意をお勧めします。その理由は、与えられた他の解決策で例示されているように、 パディング方式 を指定する必要があるからです。一般的に、もしあなたが 非常に パディングについて注意していないのであれば、 暗号化を完全に破る attack があります!
キーは ランダム、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を使用することをお勧めしますが、これは一般的に 悪い暗号慣行 と考えられています。動作モードの詳細については wikipedia を調べてください。
Urlsafe_b64encodeとurlsafe_b64decodeを使用したい人のために、ここに私のために働いているバージョンがあります(Unicode問題にしばらく時間を費やした後)
BS = 16
key = hashlib.md5(settings.SECRET_KEY).hexdigest()[:BS]
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[:-ord(s[len(s)-1:])]
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.urlsafe_b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.urlsafe_b64decode(enc.encode('utf-8'))
iv = enc[:BS]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(enc[BS:]))
SHA-1やSHA-256のような暗号化ハッシュ関数(NOTPythonのhash
)を使うことで、任意のパスワードからパスフレーズを得ることができます。 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]
を使用するだけで暗号化ハッシュ値を切り捨てることができ、指定した長さまでセキュリティが保持されます。
他の人の利益のために、ここに私が@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()
これについての別の見方(上記の解決策から大きく派生)
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')
それは少し遅れています、しかし、私はこれが非常に役に立つと思います。 PKCS#7パディングのような使用方法については誰も言及していません。以前の関数を代わりに使用してパディング(暗号化を行うとき)とアンパッド(復号化を行うとき)を行うことができます。以下に完全なソースコードが表示されます。
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
import StringIO
import binascii
def decode(text, k=16):
nl = len(text)
val = int(binascii.hexlify(text[-1]), 16)
if val > k:
raise ValueError('Input is not padded or padding is corrupt')
l = nl - val
return text[:l]
def encode(text, k=16):
l = len(text)
output = StringIO.StringIO()
val = k - (l % k)
for _ in xrange(val):
output.write('%02x' % val)
return text + binascii.unhexlify(output.getvalue())
影響を与えたが私にはうまくいかなかった他の答えに感謝します。
それがどのように機能するのかを理解しようと何時間も費やした後、私は最新のPyCryptodomexライブラリ(それは別のものです)で以下の実装を思い付きました。どのように私はどうやってプロキシの背後に、Windowsの上で、virtualenvの中でそれを設定することができたか.. phew)
あなたの実装に取り組んで、パディング、エンコード、暗号化のステップを書き留めておくことを忘れないでください(そしてその逆も同様です)。順番を念頭に置いて梱包および開梱する必要があります。
import base64 Cryptodome.Cipherからimport hashlib import Cryptodome.AndからAES import get_random_bytes __ key__ = hashlib。 sha256(b'16文字のキー ')digest() def encrypt(raw): BS = AES.block_size pad =λ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) 暗号= AES.new(キー= __key__、モード= AES.MODE_CFB、iv = iv) return base64.b64encode( iv + cipher.encrypt(raw)) def decrypt(enc): unpad =ラムダs:s [: - ord(s [-1:])] [ [=] [=] enc = base64.b64decode(enc) iv = enc [:AES.block_size] 暗号= AES.new(__ key__、AES.MODE_CFB、iv) unpadを返す(base64.b64decode(cipher.decrypt(enc [AES.block_size:]))。decode( 'utf8'))
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=16
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)
from Crypto import Random
from Crypto.Cipher import AES
import base64
BLOCK_SIZE=16
def trans(key):
return md5.new(key).digest()
def encrypt(message, passphrase):
passphrase = trans(passphrase)
IV = Random.new().read(BLOCK_SIZE)
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return base64.b64encode(IV + aes.encrypt(message))
def decrypt(encrypted, passphrase):
passphrase = trans(passphrase)
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(passphrase, AES.MODE_CFB, IV)
return aes.decrypt(encrypted[BLOCK_SIZE:])
互換性のある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