2016-11-26 20 views
1

PythonアプリケーションとPHP Webサイトを使用して、特定のネットワーク層からメッセージを送信しています。私の仕事は、そのチャンネルを使ってAESで暗号化され、base64でエンコードされたすべてのメッセージを送信することです。暗号化キーは、両者のために手動で事前共有されます。私のPHPでopenssl_encryptを使用したPython-to-PHP互換AES暗号化AES-CBC

、私は、最終的なメッセージテキストを作成するには、このコードを使用し$payloadと呼ばれる:

$key = substr('abdsbfuibewuiuizasbfeuiwhfashgfhj56urfgh56rt7856rh', 0, 32); 
$magic = 'THISISANENCRYPTEDMESSAGE'; 

function crypted($data) { 
     global $key, $magic; 

     // serialize 
     $payload = json_encode($data); 

     // encrypt and get base64 string with padding (==): 
     $payload = @openssl_encrypt($payload, 'AES-192-CBC', $key); 

     // prepend with magic 
     $payload = $magic.$payload; 
     return $payload; 
    } 

そして私は、base64バイトデータを取得し、魔法を剥ぎ取り、私のPythonアプリケーションでは、このようなメッセージが表示されます。このメッセージを解読するための互換性のあるAES暗号を作成するサンプルが見つかりません。

キーと「マジック」はあらかじめ共有され、両側で既知の値のみですが、これは正しいですか? IVが必要ですか?

私の暗号化されたメッセージでは機能しないので、ここのPythonソリューションがあります。

from base64 import b64encode, b64decode 
from Crypto.Cipher import AES 


class AESCipher: 

    class InvalidBlockSizeError(Exception): 
     """Raised for invalid block sizes""" 
     pass 

    def __init__(self, key): 
     self.key = key 
     self.iv = bytes(key[0:16], 'utf-8') 

    def __pad(self, text): 
     text_length = len(text) 
     amount_to_pad = AES.block_size - (text_length % AES.block_size) 
     if amount_to_pad == 0: 
      amount_to_pad = AES.block_size 
     pad = chr(amount_to_pad) 
     return text + pad * amount_to_pad 

    def __unpad(self, text): 
     pad = ord(text[-1]) 
     return text[:-pad] 

    def encrypt(self, raw): 
     raw = self.__pad(raw) 
     cipher = AES.new(self.key, AES.MODE_CBC, self.iv) 
     return b64encode(cipher.encrypt(raw)) 

    def decrypt(self, enc): 
     enc = b64decode(enc) 
     cipher = AES.new(self.key, AES.MODE_CBC, self.iv) 
     r = cipher.decrypt(enc) # type: bytes 
     return self.__unpad(r.decode("utf-8", errors='strict')) 

デコードの問題で最後の行に失敗します。 "ignore"デコードモードは空文字列を返します。

# with magic: "THISISANENCRYPTEDMESSAGE8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI=" 
# contains: {'test': 'hello world'} 
payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI=' 

aes = AESCipher('abdsbfuibewuiuizasbfeuiwhfashgfh') 
print(aes.decrypt(payload)) 

は発生させます:

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "../test.py", line 36, in decrypt 
    return self.__unpad(cipher.decrypt(enc).decode("utf-8")) 
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 0: invalid start byte 

私は何をしないのですか?

+1

暗号化されたデータ文字列の長さを判断するには、なぜ 'text [-1]'( 'x02'バイトなので、最後の2バイトは無視しますか? –

+0

キーをIVとして使用することは、実際には賢明ではありません。本当にスマートではありません。また、ASCIIの文字と数字でキーを作成すると、キー空間が大幅に縮小されます。 –

答えて

2

Cipher Block Chainingを使用していますが、IVをopenssl_encrypt()に渡しませんでした。つまり、IVはNULバイトの16倍です。しかし、あなたのPythonコードはキーを代わりに使用します。そのため、異なる復号結果が生成されます。

次に、AES-256-CBCではなくAES-192-CBCを選択したので、192ビットのみがキーとして使用されます。 192ビット== 24バイト、 32ではありません。

また、__unpad()コールを完全にドロップする必要があります。暗号化されたデータにパディングはなく、復号化の前にデータを削除しないと復号化に失敗します。

ので、Pythonの側で復号化キーの24個の文字を使用し、16倍\x00であるIVを与え、そしてにあなたはBase64でからデコードすべてデータ渡すために:あなたが望む場合は

>>> from Crypto.Cipher import AES 
>>> from base64 import b64decode 
>>> key = 'abdsbfuibewuiuizasbfeuiwhfashgfh'[:24] 
>>> key 
'abdsbfuibewuiuizasbfeuiw' 
>>> payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI=' 
>>> enc = b64decode(payload) 
>>> cipher = AES.new(key, AES.MODE_CBC, '\x00' * 16) 
>>> cipher.decrypt(enc) 
b'{"test":"hello world"}\n\n\n\n\n\n\n\n\n\n' 

をキーの全部で32文字を使用するには、代わりにAES-256-CBCを使用してください。

実際には、トラフィックを詮索している人が同じペイロードが毎回同じ暗号化メッセージを生成するパターンを判別できないように、ランダムIVを生成する必要があります。 IVを生成し、それを送信データに組み込み、Python側で抽出してAES.new()関数に渡します。

+0

偉大な、非常に明確で有益な。 – Johny99

関連する問題