12

対称暗号化を使用してテキストを暗号化するコードを書いています。しかし、それは正しい結果で戻ってこない...

from Crypto.Cipher import AES
import os

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

ここで、復号化されたテキストは元のテキストとは異なります。

暗号についてはあまりよくわかりませんので、ご容赦ください。CTRモードでは、毎回ランダムなカウンターを提供するために「カウンター」機能が必要であることを理解していますが、キーが32バイトで、メッセージも16バイトの倍数であると主張するのに、なぜ16バイトである必要があるのでしょうか。これは正常ですか?

暗号化と復号化の間でカウンターが変わったため、元のメッセージに戻らないと思います。しかし、それでは、とにかく理論的にはどのように機能するのでしょうか?私は何が間違っているのですか?とにかく、私はこれを理解するまでECBに頼ることを余儀なくされています:(

4

5 に答える 5

13

counter、あなたが直感的に理解しているように、暗号化の場合と同じように復号化の際に返す必要があります。したがって、それを行う1つの(まったく安全ではない)方法は次のとおりです。

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

CTRはブロック暗号であるため、驚くような「一度に16個」の制約は非常に自然なものです。

もちろん、各呼び出しで同じ値を返すいわゆる「カウンター」は非常に安全ではありません。より良いことをするのにそれほど時間はかかりません、例えば...:

import array

class Secret(object):
  def __init__(self, secret=None):
    if secret is None: secret = os.urandom(16)
    self.secret = secret
    self.reset()
  def counter(self):
    for i, c in enumerate(self.current):
      self.current[i] = c + 1
      if self.current: break
    return self.current.tostring()
  def reset(self):
    self.current = array.array('B', self.secret)

secret = Secret()
crypto = AES.new(os.urandom(32), AES.MODE_CTR, counter=secret.counter)
encrypted = crypto.encrypt(16*'a' + 16*'b' + 16*'c')
secret.reset()
print crypto.decrypt(encrypted)
于 2010-07-01T04:55:32.807 に答える
9

AESはブロック暗号です。これは、キーとメッセージブロックを受け取り、ブロックを暗号化または復号化するアルゴリズム(より正確には、アルゴリズムのペア)です。ブロックのサイズは、キーサイズに関係なく、常に16バイトです。

CTRは動作モードです。これは、ブロック暗号に基づいて構築され、任意の長さのメッセージを暗号化および復号化できるストリーム暗号を生成するアルゴリズムのペアです。

CTRは、連続するメッセージブロックを、カウンターの連続する値の暗号化と組み合わせることによって機能します。ブロック暗号の有効な入力となるように、カウンターのサイズは1ブロックである必要があります。

  • 機能的には、暗号化側と復号化側が同じシーケンスを使用している限り、カウンターの連続する値が何であるかは問題ではありません。通常、カウンタは256ビットの数値として扱われ、連続するブロックごとに増分され、初期値がランダムに選択されます。したがって、通常、インクリメント方式はコードに組み込まれますが、復号化側は初期値を知る必要があるため、暗号化側は暗号化されたメッセージの先頭に初期カウンター値を送信または保存します。
  • セキュリティのために、特定のキーで同じカウンター値を繰り返さないことが重要です。したがって、使い捨てキーの場合は、で始めてもかまいません'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'。ただし、キーが複数回使用されている場合、2番目のメッセージは最初のメッセージで使用されているカウンター値を再利用できません。これを確実にする最も簡単な方法は、初期カウンター値をランダムに生成することです(2 ^ 128スペース、衝突の可能性は許容できるほど無視できます)。

PyCryptoライブラリは、発信者にカウンター関数を選択させることで、首を吊るすのに十分なロープを提供します。Crypto.Util.Counterドキュメントに記載されているように「パフォーマンスを向上させる」だけでなく、自分で思いつくものよりも安全なものを作成する方が簡単なので、を使用する必要があります。それでも、デフォルトではないランダムな初期値を使用するように注意してください。

import binascii
import os
from Crypto.Cipher import AES
from Crypto.Util import Counter
def int_of_string(s):
    return int(binascii.hexlify(s), 16)
def encrypt_message(key, plaintext):
    iv = os.urandom(16)
    ctr = Counter.new(128, initial_value=int_of_string(iv))
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    return iv + aes.encrypt(plaintext)
def decrypt_message(key, ciphertext):
    iv = ciphertext[:16]
    ctr = Counter.new(128, initial_value=int_of_string(iv))
    aes = AES.new(key, AES.MODE_CTR, counter=ctr)
    return aes.decrypt(ciphertext[16:])
于 2017-08-11T19:08:07.570 に答える
2

キーが32バイトのときに16バイトにする必要があるのはなぜですか

暗号のブロックサイズと同じ長さである必要があります。CTRモードは、カウンターを暗号化し、暗号化されたカウンターブロックとプレーンテキストをXORします。

ノート:

  1. カウンター値は一意である必要があります。同じカウンター値を使用して同じキーで2つの異なるプレーンテキストを暗号化する場合は、キーを渡しただけです。
  2. IVのように、カウンターは秘密ではありません-暗号文と一緒に送信するだけです。コードを秘密にしておくことでコードをより複雑にすると、おそらく足を踏み入れてしまうでしょう。
  3. カウンター値は予測不可能である必要はありません。ゼロから始めて、ブロックごとに1を追加することはまったく問題ありません。ただし、複数のメッセージを暗号化する場合は、すでに消費されているカウンター値を追跡する必要があります。つまり、そのキーですでに暗号化されているブロックの数を追跡する必要があります(同じものを使用することはできません)。プログラムのさまざまなインスタンスまたはさまざまなマシンでキー入力します)。
  4. プレーンテキストは任意の長さにすることができます-CTRモードは、ブロック暗号をストリーム暗号に変換します。

標準の免責事項:暗号は難しいです。あなたが何をしているのか理解していないなら、あなたはそれを間違えるでしょう。

セッション間でいくつかのパスワードを保存したいだけです。

scryptを使用します。 scryptには、パスワードから派生したキーでAES-CTRを使用するものが含まれencryptています。decrypt

$ pip install scrypt

$ python
>>> import scrypt
>>> import getpass
>>> pw = getpass.getpass("enter password:")
enter password:
>>> encrypted = scrypt.encrypt("Guido is a space alien.",pw)
>>> out = scrypt.decrypt(encrypted,pw)
>>> out
'Guido is a space alien.'
于 2013-10-07T17:54:23.847 に答える
2

私は間違いなく遅れている可能性があり、以前の回答を見落としている可能性がありますが、PyCryptoパッケージに従ってこれを(少なくとも私見で)どのように行うべきかについての明確な声明は見つかりませんでした。

Crypto.Util.Counterパッケージは、呼び出し可能なステートフルカウンターを提供します。これは非常に便利ですが、少なくとも私にとっては、それらを不適切に使用するのは簡単でした。

たとえば、カウンターを作成する必要がありますctr = Counter.new('parameters here')。メッセージを暗号化するためにカウンターモード暗号オブジェクトによってカウンターが呼び出されるたびに、カウンターはインクリメントされます。これは、優れた暗号化手法に必要です。そうしないと、等しいブロックに関する情報が暗号文から漏洩する可能性があります。

これで、同じ暗号オブジェクトで復号化関数を呼び出すことはできなくなります。これは、その間に、場合によっては数回インクリメントされた同じカウンターを再度呼び出すためです。あなたがする必要があるのは、同じパラメータで初期化された異なるカウンタで新しい暗号オブジェクトを作成することです。このようにして、暗号化が行われたのと同じポイントからカウンターを開始して、復号化が適切に機能します。

以下の作業例:

# Import modules
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Util import Counter


# Pad for short keys
pad = '# constant pad for short keys ##'

# Generate a random initialization vector, to be used by both encryptor and decryptor
# This may be sent in clear in a real communication

random_generator = Random.new()
IV = random_generator.read(8)


# Encryption steps

# Ask user for input and pad or truncate to a 32 bytes (256 bits) key
prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: '
user_keye = raw_input(prompt)
keye = (user_keye + pad)[:32]

# Create counter for encryptor
ctr_e = Counter.new(64, prefix=IV)

# Create encryptor, ask for plaintext to encrypt, then encrypt and print ciphertext
encryptor = AES.new(keye, AES.MODE_CTR, counter=ctr_e)
plaintext = raw_input('Enter message to cipher: ')
ciphertext = encryptor.encrypt(plaintext)
print ciphertext
print


# Decryption steps

# Ask user for key: it must be equal to that used for encryption
prompt = 'Input your key. It will padded or truncated at 32 bytes (256 bits).\n-: '
user_keyd = raw_input(prompt)
keyd = (user_keyd + pad)[:32]

# Create counter for decryptor: it is equal to the encryptor, but restarts from the beginning

ctr_d = Counter.new(64, prefix=IV)

# Create decryptor, then decrypt and print decoded text
decryptor = AES.new(keyd, AES.MODE_CTR, counter=ctr_d)
decoded_text = decryptor.decrypt(ciphertext)
print decoded_text
于 2013-10-31T15:59:07.353 に答える
1

初期化ベクトル(「カウンター」)は、暗号化と復号化の間で、キーと同じように同じままである必要があります。これは、同じテキストを100万回エンコードし、毎回異なる暗号文を取得できるようにするために使用されます(既知の平文攻撃やパターンマッチング/攻撃を防止します)。復号化するときも、暗号化するときと同じIVを使用する必要があります。通常、ストリームの復号化を開始するときは、そのストリームの暗号化を開始したときに開始したのと同じ値にIVを初期化します。

初期化ベクトルの詳細については、 http://en.wikipedia.org/wiki/Initialization_vectorを参照してください。

os.urandom(16)は、カウンター関数の要件である「決定論的」ではないことに注意してください。CTRモードの設計方法として、増分関数を使用することをお勧めします。初期カウンター値はランダムである必要がありますが、連続する値は初期値から完全に予測可能である必要があります(決定論的)。初期値はあなたのために世話をするかもしれません(私は詳細を知りません)

キー、IV、および入力サイズについては、選択した暗号のブロックサイズが16バイトのようです。あなたが説明することはすべてそれに適合し、私には普通のようです。

于 2010-07-01T04:45:00.363 に答える