3

CTRモードでAESを使用してsthを暗号化したいpython 2.7.1を使用しています。Python用のPyCryptoライブラリをインストールしました。次のコードを書きました。

secret = os.urandom(16)
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=lambda: secret)
encrypted = crypto.encrypt("asdk")
print crypto.decrypt(encrypted)

復号化されたデータを正しく取得するには、平文のバイトサイズと同じ回数 crypto.decrypt を実行する必要があります。すなわち:

encrypted = crypto.encrypt("test")
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)
print crypto.decrypt(encrypted)

復号化の最後の呼び出しで、平文が返されます。復号化からの他の出力は、意味不明な文字列です。これが正常なのかどうか疑問に思っていますか?毎回平文と同じサイズのループに含める必要がありますか、それとも間違っていますか?

4

4 に答える 4

2

新しい操作のための新しい暗号オブジェクトから始めます

これが質問で説明したように動作する理由は、プレーンテキスト (4 バイト / 32 ビット) が、選択した AES モード (128 ビット) で暗号化エンジンが動作するサイズの 4 倍であり、同じものを再利用するためです。暗号オブジェクトのインスタンス。新しいデータ ストリームに対して操作 (または別の操作) を実行している場合は、同じオブジェクトを再利用しないでください。この問題は、次のように、復号化用の新しいcryptoオブジェクトをインスタンス化することで解決されます。

# *NEVER* USE A FIXED LIKE COUNTER BELOW IN PRODUCTION CODE. READ THE DOCS.
counter = os.urandom(16)
key = os.urandom(32)  # 256 bits key

# Instantiate a crypto object first for encryption
encrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
encrypted = encrypto.encrypt("asdk")

# Instantiate a new crypto object for decryption
decrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
print decrypto.decrypt(encrypted) # prints "asdk"

AES-CTR でパディングしない理由

この回答は、Marcus による回答に対する回答として始まりました。その中で、彼は最初にパディングの使用がそれを解決することを示しました。パディングの問題の症状のように見えることは理解していますが、そうではありません。

AES-CTR の要点は、(ECB/CBC などとは異なり)ストリーム暗号であるため、padding が必要ないことです! ストリーム暗号は、データをブロックにチャンクし、実際の暗号計算でチェーン化するのではなく、データのストリームで機能します。

于 2013-07-03T14:35:10.223 に答える
1

マーカスの言うことに加えて、このCrypto.Util.Counterクラスを使用してカウンター ブロック機能を構築できます。

于 2012-10-03T06:06:03.743 に答える
0

@gertvdijk によると、AES_CTR はパディングを必要としないストリーム暗号です。そのため、関連するコードを削除しました。

ここに私が知っていることがあります。

  1. AES.new(...)暗号化と復号化で同じキー ( の最初のパラメーター) を使用し、キーを非公開にする必要があります。

  2. 暗号化/復号化の方法はステートフルです。つまり、常に真であるとcrypto.en(de)crypt("abcd")==crypto.en(de)crypt("abcd")限りません。CTR では、カウンター コールバックは常に同じものを返すため、暗号化するとステートレスになります (それが理由であるとは 100% 確信していません) が、復号化ではややステートフルであることがわかります。結論として、それらを行うには常に新しいオブジェクトを使用する必要があります。

  3. counter callback暗号化と復号化の両方の関数は同じように動作する必要があります。あなたの場合、両方が同じシークレットを返すようにすることです。secretしかし、私はそれが「秘密」だとは思いません。生成された乱数を使用して、暗号化せずに通信ピア間で渡すことができるため、予測できない"secret"限り、反対側がそれを直接使用secretできます。

だから私はこのように私の暗号を書くでしょう、それが助けになることを願っています.

import os
import hashlib
import Crypto.Cipher.AES as AES

class Cipher:

        @staticmethod
        def md5sum( raw ):
                m = hashlib.md5()
                m.update(raw)
                return m.hexdigest()

        BS = AES.block_size

        @staticmethod 
        def pad( s ):
                """note that the padding is no necessary"""
                """return s + (Cipher.BS - len(s) % Cipher.BS) * chr(Cipher.BS - len(s) % Cipher.BS)"""
                return s

        @staticmethod
        def unpad( s ):
                """return s[0:-ord(s[-1])]"""
                return s

        def __init__(self, key):
                self.key = Cipher.md5sum(key)
                #the state of the counter callback 
                self.cnter_cb_called = 0 
                self.secret = None

        def _reset_counter_callback_state( self, secret ):
                self.cnter_cb_called = 0
                self.secret = secret

        def _counter_callback( self ):
                """
                this function should be stateful
                """
                self.cnter_cb_called += 1
                return self.secret[self.cnter_cb_called % Cipher.BS] * Cipher.BS


        def encrypt(self, raw):
                secret = os.urandom( Cipher.BS ) #random choose a "secret" which is not secret
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                raw_padded = Cipher.pad( raw )
                enc_padded = cipher.encrypt( raw_padded )
                return secret+enc_padded #yes, it is not secret

        def decrypt(self, enc):
                secret = enc[:Cipher.BS]
                self._reset_counter_callback_state( secret )
                cipher = AES.new( self.key, AES.MODE_CTR, counter = self._counter_callback )
                enc_padded = enc[Cipher.BS:] #we didn't encrypt the secret, so don't decrypt it
                raw_padded = cipher.decrypt( enc_padded )
                return Cipher.unpad( raw_padded )

いくつかのテスト:

>>> from Cipher import Cipher
>>> x = Cipher("this is key")
>>> "a"==x.decrypt(x.encrypt("a"))
True
>>> "b"==x.decrypt(x.encrypt("b"))
True
>>> "c"==x.decrypt(x.encrypt("c"))
True
>>> x.encrypt("a")==x.encrypt("a")
False #though the input is same, the outputs are different

参照: http://packages.python.org/pycrypto/Crypto.Cipher.blockalgo-module.html#MODE_CTR

于 2012-10-02T15:44:10.393 に答える