6

いくつかの背景情報、実際の質問のためにこの部分をスキップすることができます

これは、stackoverflowでのこのトピックに関する私の3番目の質問です。完全を期すために、これらはcrypt-jsとPyCryptoを使用したAESと、pythonおよびjavascriptでのAES de/encryptionとの一致に関する他の質問です。残念ながら、私の最後の試みでは、元の質問に対して2つの反対票がありました。問題は、私の本当の質問が何であるかを私でさえ知らなかったということでした。探していた本当の質問を見つけるために掘り下げました。コメントのフィードバックといくつかの追加情報を読んで、質問を更新しました。私は正しい質問を発掘していると思います。しかし、私の問題は、私の更新後にそれ以上のビューを取得しませんでした。ですから、この質問がより明確で理解しやすくなることを心から願っています-私の問題が今何であるかを知っていても:D
このクールなコミュニティにstackoverflowを作成していただき、ありがとうございます。ここで問題の解決策を見つけることがよくあります。悪い質問にはフィードバックを送り続けてください。そうすれば、それらを改善および更新して、この膨大な知識とソリューションのデータベースを増やすことができます。そして、私の英語の文法とスペルを自由に修正してください。

問題

JavascriptのAES

このJavascriptによるAES256CTRモードの実装で復号化できる暗号化された文字列があります

password = "myPassphrase"
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
origtext = Aes.Ctr.decrypt(ciphertext, password, 256);
alert(origtext)

これにより、文字列が復号化され、アラートボックスがThis is a test Textポップアップ表示されます。

PyCryptoを使用したAES

今、私はこの文字列をpythonとPyCryptoで復号化したい

password = 'myPassphrase'
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
ctr = Counter.new(nbits=128)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
origtext = encryptor.decrypt(base64.b64decode(ciphertext))
print origtext

このコードは実行されません。取得しValueError: AES key must be either 16, 24, or 32 bytes longます。PyCryptoでさらに多くのことを実行し、次に復号化メソッドを呼び出す必要があることを認識したとき、私は自分が何をしなければならないかを理解するために調査を開始しました。

調査

私が最初に理解した基本的なことは次のとおりです。

  • AES 256ビット(?)。ただし、AES標準は128ビットです。パスフレーズを32バイトに増やすだけで十分ですか?
  • カウンターモード。AES.MODE_CTRを使用してPyCryptoに簡単に設定できます。ただし、counter()メソッドを指定する必要があります。そこで、PyCryptoが提供する基本的なバイナリカウンターを使用しました。これはJavascriptの実装と互換性がありますか?彼らが何をしているのか理解できません。
  • 文字列はbase64でエンコードされています。大きな問題ではありません。
  • 一般的なパディング。パスフレーズと暗号化された文字列の両方。

パスフレーズの場合、彼らはこれを行います:

for (var i=0; i<nBytes; i++) {
    pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}

それから私はPythonでこれをしました

l = 32
key = key + (chr(0)*(l-len(key)%l))

しかし、これは役に立ちませんでした。私はまだ? A???B??d9= ,?h????'次のコードで奇妙な文字列を取得します

l = 32
key = 'myPassphrase'
key = key + (chr(0)*(l-len(key)%l))
ciphertext = "bQJdJ1F2Y0+uILADqEv+/SCDV1jAb7jwUBWk"
ctr = Counter.new(nbits=128)
encryptor = AES.new(key, AES.MODE_CTR, counter=ctr)
origtext = encryptor.decrypt(base64.b64decode(ciphertext))
print origtext

それから私はJavascriptの実装についてもっと読みましたそしてそれは言います

[...]この実装では、最初のブロックは最初の8バイトにナンスを保持し、次の8バイトにブロックカウントを保持します。[...]

これが解決策の鍵になると思います。そこで、Javascriptで空の文字列を暗号化するとどうなるかをテストしました。

origtext = ""
var ciphertext =Aes.Ctr.encrypt(origtext, password, 256);
alert(ciphertext)

警告ボックスに/gEKb+N3Y08=(12文字)が表示されます。しかし、なぜ12?8 + 8 = 16バイトではないでしょうか?とにかく、andまたはで復号化をテストすることにより、Python復号化でブルートフォース方式を試しました。これは非常に恥ずかしい試みであることを私は知っていますが、私はますます必死になりました。そしてそれもうまくいきませんでした。for i in xrange(0,20):ciphertext[i:]base64.b64decode(ciphertext)[i:]

将来の見通しも同じ方法で暗号化を実装することです。

追加情報

暗号化された文字列は、もともとこのJavascript実装で暗号化されていませんでした。これは、別のソースからのものです。私は、Javascriptコードが正しいことをすることを認識しました。したがって、この種の実装は「標準」のようなものであると私は断言します。

質問

PyCryptoを使用した文字列からの暗号化と復号化がJavascript実装の場合と同じであるため、JavascriptとPythonの間でデータを交換できるようにするには、どうすればよいですか?また、別の暗号ライブラリを提案できる場合は、Pythonで別の暗号ライブラリに切り替えます。さらに、私はあらゆる種類のヒントやフィードバックに満足しています。

そして、すべては、暗号化された文字列にナンスとブロックカウントを含めるにはどうすればよいかということになります。復号化のためにこの情報を抽出するにはどうすればよいですか?

4

1 に答える 1

6

ここではまだたくさんの質問に取り組んでいます。

復号化のためにナンスとカウンターを抽出するにはどうすればよいですか?

これは簡単。Javascriptの実装(この点で特定の標準に準拠していません)では、暗号化された結果の前に8バイトのナンスが付加されます。Pythonでは、次のように抽出します。

import base64
from_js_bin = base64.decode(from_js)
nonce = from_js_bin[:8]
ciphertext = from_js_bin[8:]

from_js受け取ったバイナリ文字列はどこにありますか。

JS実装がカウンターを送信しないため、カウンターを抽出できません。ただし、初期値は(通常発生するように)0です。

ナンスとカウンターを使用してPythonで文字列を復号化するにはどうすればよいですか?

まず、カウンターブロックを取得するためにナンスとカウンターを組み合わせる方法を確立する必要があります。JSの実装はNIST800-38A標準に準拠しているようです。ここでは、左半分がナンス、右半分がカウンターです。より正確には、カウンターはビッグエンディアン形式です(LSBは右端のバイトです)。これは、ウィキペディアが示していることでもあります AESCTRモード

残念ながら、CTRモードはPyCryptoで十分に文書化されていません(よく知られている問題)。基本的に、counterパラメーターは、後続の呼び出しごとに、正しい16バイト(AESの場合)のカウンターブロックを返す 呼び出し可能なオブジェクトである必要があります。Crypto.Util.Counterそれを行いますが、あいまいな方法で。

ただし、パフォーマンスの目的でのみ存在します。次のように、自分で簡単に実装できます。

from Crypto.Cipher import AES
import struct

class MyCounter:

  def __init__(self, nonce):
    """Initialize the counter object.

    @nonce      An 8 byte binary string.
    """
    assert(len(nonce)==8)
    self.nonce = nonce
    self.cnt = 0

  def __call__(self):
    """Return the next 16 byte counter, as binary string."""
    righthalf = struct.pack('>Q',self.cnt)
    self.cnt += 1
    return self.nonce + righthalf

cipher_ctr = AES.new(key, mode=AES.MODE_CTR, counter=MyCounter(nonce))
plaintext = cipher_ctr.decrypt(ciphertext)

AESのキーはどのくらいですか?

AES-128のキーの長さは16バイトです。AES-192のキーの長さは24バイトです。AES-256のキーの長さは32バイトです。各アルゴリズムは異なりますが、実装の多くは共有されています。すべての場合において、アルゴリズムは16バイトのデータブロックで動作します。簡単にするために、AES-128(nBits=128)に固執します。

あなたのコードは機能しますか?

AESキーの計算方法が間違っているように見えるので、そうはならないと感じています。JSコードは、パスワードをUTF-8でエンコードし、それ自体で暗号化します。結果が実際のキーマテリアルです。これは16バイト長であるため、AES-192および-256の場合、実装はその一部を背面にコピーします。さらに、平文も暗号化の前にUTF-8でエンコードされます。

一般的に、私はあなたがこのアプローチに従うことを提案します:

  1. JS実装を再現可能にします(現在、暗号化は現在の時刻に依存しますが、これは頻繁に変更されます;-))。
  2. 各ステップでキーとデータの値を出力します(またはデバッガーを使用します)。
  3. Pythonで同じアルゴリズムを再現し、値も出力してみてください。
  4. それらがどこで異なり始めるかを調査します。

Pythonで暗号化アルゴリズムを複製すると、復号化は簡単になります。

于 2012-03-18T18:15:55.247 に答える