2012-09-21 46 views
112

का उपयोग करके एन्क्रिप्ट और डिक्रिप्ट करें मैं दो पैरामीटर स्वीकार करने वाले दो कार्यों को बनाने की कोशिश कर रहा हूं जो संदेश और कुंजी को स्वीकार करते हैं, और फिर संदेश को एन्क्रिप्ट/डिक्रिप्ट करते हैं।पीईक्रिप्टो एईएस 256

मैं मेरी मदद करने के लिए वेब पर कई लिंक मिल गया, लेकिन उनमें से हर एक खामियां हैं:

This one at 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)) 
+10

[os.urandom] (http://docs.python.org/3/library/os.html) [Pycrypto] पर https://encouraged_ है (https://www.dlitz.net/software/pycrypto/) वेबसाइट। यह माइक्रोसॉफ्ट के [CryptGenRandom] का उपयोग करता है (http://msdn.microsoft.com/en-us/library/windows/desktop/aa379942 (v = vs.85) .aspx) फ़ंक्शन जो एक [सीएसपीआरएनजी] है (http: // en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) –

+4

या यूनिक्स –

+1

पर '/ dev/urandom' बस स्पष्ट करने के लिए, इस उदाहरण में ** passphrase ** _key_ है जो 128, 1 9 2, या 256 बिट्स (16, 24 हो सकता है , या 32 बाइट्स) – Mark

उत्तर

84

यहाँ मेरी कार्यान्वयन है और कुछ सुधारों के साथ मेरे लिए काम करता है और 16 बाइट्स 32 बाइट्स और iv साथ कुंजी और रहस्य वाक्यांश के संरेखण को बढ़ाता है:

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:])] 
+8

मुझे पता है कि यह थोड़ी देर के लिए रहा है, लेकिन मुझे लगता है कि यह प्रतिक्रिया कुछ भ्रम फैल सकती है। यह फ़ंक्शन पैड इनपुट डेटा के लिए 32 बाइट (256 बाइट) के block_size का उपयोग करता है लेकिन एईएस 128 बिट ब्लॉक आकार का उपयोग करता है। एईएस 256 में * कुंजी * 256 बिट है, लेकिन ब्लॉक आकार नहीं है। – Tannin

+4

इसे एक और तरीका रखने के लिए, "self.bs" को हटाया जाना चाहिए और "AES.block_size" द्वारा प्रतिस्थापित किया गया है – Alexis

+0

आप कुंजी क्यों हैं? यदि आप उम्मीद कर रहे हैं कि यह पासवर्ड की तरह कुछ है, तो आपको SHA256 का उपयोग नहीं करना चाहिए; एक प्रमुख व्युत्पन्न समारोह का उपयोग करने के लिए बेहतर, जैसे पीबीकेडीएफ 2, जो कि पिक्रीपेटो प्रदान करता है। – tweaksp

6

आप एक क्रिप्टोग्राफिक हैश समारोह (नहीं पायथन के builtin 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] का उपयोग कर और यह लंबाई आपके द्वारा निर्दिष्ट करने के लिए अपनी सुरक्षा बनी रहेगी द्वारा: अजगर अपने मानक पुस्तकालय में दोनों के लिए समर्थन शामिल है।

+10

आपको पासवर्ड से कुंजी उत्पन्न करने के लिए SHA-Family हैश फ़ंक्शन का उपयोग नहीं करना चाहिए - देखें [विषय पर कोडा हैले का निबंध] (http://codahale.com/how-to-safely-store-a-password/) । एक वास्तविक [कुंजी व्युत्पन्न फ़ंक्शन] (https://en.wikipedia.org/wiki/Key_derivation_function) का उपयोग करने पर विचार करें [scrypt] (https://pypi.python.org/pypi/scrypt/) इसके बजाए। (कोडा हैले का निबंध स्क्रिप के प्रकाशन से पहले लिखा गया था।) –

+5

भावी पाठकों के लिए, यदि आप पासफ्रेज से एक कुंजी प्राप्त करना चाहते हैं, तो पीबीकेडीएफ 2 देखें। पायथन (https://pypi.python.org/pypi/pbkdf2) में उपयोग करना काफी आसान है। यदि आप हैश पासवर्ड देख रहे हैं, हालांकि, bcrypt एक बेहतर विकल्प है। –

130

इनपुट की लंबाई BLOCK_SIZE के एकाधिक नहीं होने पर आपको पैड के लिए निम्नलिखित दो फ़ंक्शन (एन्क्रिप्शन कब करते हैं) और अनपैड (डिक्रिप्शन करते समय) की आवश्यकता हो सकती है।

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:])) 
+1

क्या होता है यदि आपके पास कोई इनपुट है जो वास्तव में BLOCK_SIZE का एक बहु है? मुझे लगता है कि अनपैड फ़ंक्शन थोड़ा उलझन में आ जाएगा ... – Kjir

+1

@Kjir, फिर BLOCK_SIZE की लंबाई में मूल्य chr (बीएस) का अनुक्रम मूल डेटा में जोड़ा जाएगा। – Marcus

+0

आप सही हैं, 'पैड' और 'अनपैड' फ़ंक्शन अब मेरे लिए स्पष्ट हैं, धन्यवाद! – Kjir

5

अन्य लोगों के लाभ के लिए, यहाँ मेरी डिक्रिप्शन कार्यान्वयन जो मैं @Cyril और @Marcus के जवाब के संयोजन के द्वारा करने के लिए मिल गया है। यह मानता है कि यह HTTP अनुरोध के माध्यम से एन्क्रिप्टेड टेक्स्ट उद्धृत और बेस 64 एन्कोडेड के साथ आ रहा है।

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() 
6

किसी चाहेंगे के लिए करने के लिए उपयोग urlsafe_b64encode और urlsafe_b64decode, यहाँ संस्करण मेरे लिए काम कर (यूनिकोड मुद्दे के साथ कुछ समय बिताने के बाद) that're

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:])) 
+0

यह कोड तोड़ देगा यदि बीएस = 32' –

0
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:]) 
हैं
+6

कृपया न केवल कोड प्रदान करें बल्कि यह भी समझाएं कि आप क्या कर रहे हैं और यह बेहतर क्यों है मौजूदा उत्तरों में अंतर है। –

2

यह बहुत देर हो चुकी है लेकिन मुझे लगता है कि यह बहुत उपयोगी होगा। पीकेसीएस # 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()) 

+0

मुझे नहीं पता कि किसने जवाब कम किया लेकिन मुझे जानना उत्सुक होगा कि क्यों। शायद यह विधि सुरक्षित नहीं है? एक स्पष्टीकरण महान होगा। –

+0

कम से कम मैं वह नहीं हूं जो इसे कम करता है :) – xXxpRoGrAmmErxXx

+1

@CyrilN। यह उत्तर बताता है कि एसएचए -256 के एक ही आमंत्रण के साथ पासवर्ड हैश करना पर्याप्त है। यह नहीं है आपको वास्तव में बड़ी पुनरावृत्ति गणना का उपयोग करके पासवर्ड से मुख्य व्युत्पन्न के लिए पीबीकेडीएफ 2 या समान उपयोग करना चाहिए। –

2

एक और इस (भारी ऊपर समाधान से प्राप्त) लेकिन

  • पर ले गद्दी के लिए अशक्त का उपयोग करता
  • लैम्ब्डा कभी नहीं का उपयोग नहीं करता (एक प्रशंसक रहा)

    #!/usr/bin/env python 
    
    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) 
         iv = Random.new().read(AES.block_size) 
         cipher = AES.new(self.key, 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, AES.MODE_CBC, iv) 
         return re.sub(b'\x00*$', b'', cipher.decrypt(enc[16:])).decode('utf-8') 
    
+0

यह इनपुट नहीं करेगा अगर इनपुट बाइट [] ने नल का पीछा किया है क्योंकि डिक्रिप्ट() फ़ंक्शन में आप अपने पैडिंग नल को किसी भी पीछे की ओर खींचेंगे। –

+0

हां, जैसा कि मैंने ऊपर बताया है, इस तर्क पैड को नल के साथ। यदि आप जिन आइटमों को एन्कोड/डीकोड चाहते हैं, वे पिछली नलियां हो सकती हैं, तो यहां अन्य समाधानों में से एक का बेहतर उपयोग करें – MIkee

5

मेरे बारे में आपके प्रश्न को हल करने दें "मोड।" एईएस 256 एक प्रकार का ब्लॉक सिफर है। यह 32-बाइट कुंजी और 16-बाइट स्ट्रिंग इनपुट के रूप में लेता है, जिसे ब्लॉक कहा जाता है और एक ब्लॉक आउटपुट करता है। एन्क्रिप्ट करने के लिए हम ऑपरेशन मोड में एईएस का उपयोग करते हैं। ऊपर दिए गए समाधान सीबीसी का उपयोग करने का सुझाव देते हैं, जो एक उदाहरण है। एक और सीटीआर कहा जाता है, और यह कुछ हद तक उपयोग करने के लिए आसान है:

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) 

इस बार एईएस सीटीआर के रूप में जाना जाता है। मैं एईएस-सीबीसी का उपयोग PyCrypto के साथ सावधानी बरतने की सलाह दूंगा। इसका कारण यह है कि आपको पैडिंग योजना निर्दिष्ट करने के लिए आवश्यक अन्य समाधानों के उदाहरण के रूप में निर्दिष्ट करने की आवश्यकता है। आम तौर पर, यदि आप नहीं हैं पैडिंग के बारे में सावधान, attacks हैं जो पूरी तरह से एन्क्रिप्शन तोड़ते हैं!

अब, यह ध्यान रखना महत्वपूर्ण है कि कुंजी यादृच्छिक, 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 उपयोग करने का सुझाव है, लेकिन यह है आमतौर पर bad cryptographic practice माना जाता है। ऑपरेशन के तरीके पर अधिक के लिए wikipedia देखें।