PyCrypto를 사용하여 메시지와 키라는 두 가지 매개 변수를 허용하고 메시지를 암호화 / 해독하는 두 가지 기능을 작성하려고합니다.
웹에서 도움이되는 몇 가지 링크를 찾았지만 각 링크에 결함이 있습니다.
codekoala에서 이것은 os.urandom을 사용하는데, 이는 PyCrypto에 의해 권장되지 않습니다.
또한 함수에 제공하는 키가 정확한 길이를 보장하지는 않습니다. 그렇게하려면 어떻게해야합니까?
또한 몇 가지 모드가 있는데 어떤 모드가 권장됩니까? 나는 무엇을 사용 해야할지 모르겠다 : /
마지막으로 IV는 정확히 무엇입니까? 암호화 및 암호 해독을 위해 다른 IV를 제공 할 수 있습니까? 그렇지 않으면 다른 결과가 나옵니까?
편집 : 코드 부분이 안전하지 않아 제거되었습니다.
답변
다음은 내 구현이며 일부 수정 사항으로 작동하며 키 및 비밀 문구의 정렬을 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:])]
답변
입력 길이가 BLOCK_SIZE의 배수가 아닌 경우- pad
암호화 (패딩시) 및 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:] ))
답변
“모드”에 대한 질문을하겠습니다. 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이라고합니다. PyCrypto와 함께 AES-CBC를 사용할 때는주의를 기울여야 합니다. 그 이유는 주어진 다른 솔루션으로 예시 된 것처럼 패딩 체계 를 지정해야하기 때문입니다 . 당신이하지 않은 경우 일반적으로, 매우 패딩주의, 거기에 공격 을 완전히 암호화 휴식!
이제 키는 임의의 32 바이트 문자열 이어야 합니다 . 암호로 는 충분 하지 않습니다 . 일반적으로 키는 다음과 같이 생성됩니다.
# Nominal way to generate a fresh key. This calls the system's random number
# generator (RNG).
key1 = Random.new().read(key_bytes)
키 도 password에서 파생 될 수 있습니다 .
# 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를 사용하려는 사람은 다음과 같습니다 (유니 코드 문제와 시간을 보낸 후)
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:]))
답변
당신은 암호 해시 함수 (사용하여 임의의 암호에서 암호를 얻을 수 없습니다 파이썬의 내장 hash
SHA-1, SHA-256 등). 파이썬은 표준 라이브러리에서 두 가지를 모두 지원합니다.
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]
그것을 당신이 지정한 길이로 보안을 유지합니다.
답변
영감을 얻었지만 효과가 없었던 다른 답변에 감사드립니다.
어떻게 작동하는지 알아 내려고 시간을 보내고 난 후에, 나는 최신와 아래의 구현 해낸 PyCryptodomex의 라이브러리 (그것은 내가 VIRTUALENV .. 휴에, Windows에서 프록시 뒤에를 설정하는 관리 방법 또 다른 이야기이다)
에 작업이 구현시 패딩, 인코딩, 암호화 단계 (및 그 반대로)를 기록해야합니다. 순서를 염두에두고 포장을 풀고 포장을 풀어야합니다.
수입 base64 수입 해시 Cryptodome.Cipher import AES에서 Cryptodome.Random 가져 오기 get_random_bytes에서 __key__ = hashlib.sha256 (b'16 자 키 ') .digest () 데프 암호화 (원시) : BS = AES.block_size 패드 = λs : s + (BS-len (s) % BS) * chr (BS-len (s) % BS) raw = base64.b64encode (패드 (raw) .encode ( 'utf8')) iv = get_random_bytes (AES.block_size) 암호 = AES.new (키 = __key__, 모드 = AES.MODE_CFB, iv = iv) base64.b64encode (iv + cipher.encrypt (raw))를 반환 데프 해독 (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') 반환
답변
다른 사람들을 위해 @Cyril과 @Marcus의 답변을 결합하여 얻은 해독 구현이 있습니다. 이것은 암호화 된 텍스트가 인용되고 base64로 인코딩 된 HTTP 요청을 통해 들어오는 것으로 가정합니다.
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()