RubyでAES_ENCRYPTとAES_DECRYPT機能を模倣します
-
21-08-2019 - |
質問
私は)組み込み関数にAES_ENCRYPT()およびAES_DECRYPTを(使用して文字列を暗号化および復号化する際のMySQLが何を模倣する必要があります。
私は、ブログの記事のカップルを読んだことがあると明らかにMySQLは、これらの機能のためのAES 128ビット暗号化を使用しています。 (\ 0)は、16ビットの大きさになるまでその上で、この暗号化は16ビットの鍵を必要とするため、MySQLはX0の文字で文字列をパッドます。
MySQLソースコードからCにおけるアルゴリズムはここでスポットしますのます。
今、私は、MySQLがRailsアプリケーションで何を複製する必要があるが、私が試した一つ一つは、動作しません。
ここで私が取得しています動作を複製する方法です
1)新しいRailsのアプリを作成します。
rails encryption-test
cd encryption-test
2)新しい足場を作成します。
script/generate scaffold user name:string password:binary
3)あなたのconfig / database.ymlを編集し、テストMySQLデータベースを追加します。
development:
adapter: mysql
host: localhost
database: test
user: <<user>>
password: <<password>>
4)移行を実行
rake db:migrate
5)、コンソールを入力し、ユーザーを作成し、MySQLのクエリからそのパスワードを更新
script/console
Loading development environment (Rails 2.2.2)
>> User.create(:name => "John Doe")
>> key = "82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
>> ActiveRecord::Base.connection.execute("UPDATE users SET password = AES_ENCRYPT('password', '#{key}') WHERE name='John Doe'")
私が捕まってしまった。ここで、です。私はMySQLを使用して、それを解読しようとするとそれが動作します:
>> loaded_user = User.find_by_sql("SELECT AES_DECRYPT(password, '#{key}') AS password FROM users WHERE id=1").first
>> loaded_user['password']
=> "password"
しかし、私はOpenSSLライブラリを使用しようとすると、私はそれを動作させることができる方法はありません。
cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB")
cipher.padding = 0
cipher.key = key
cipher.decrypt
user = User.find(1)
cipher.update(user.password) << cipher.final #=> "########gf####\027\227"
私はキーをパディングしようとしています:
desired_length = 16 * ((key.length / 16) + 1)
padded_key = key + "\0" * (desired_length - key.length)
cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB")
cipher.key = key
cipher.decrypt
user = User.find(1)
cipher.update(user.password) << cipher.final #=> ""|\e\261\205:\032s\273\242\030\261\272P##"
しかし、それは実際に動作しません。
誰でも私は、MySQL AES_ENCRYPT()およびAES_DECRYPT()Rubyで関数の挙動を模倣することができます方法についての手掛かりを持っていますか?
ありがとう!
解決
今後の参考のために:
私は前に送られたブログ記事によると、ここでは、MySQLがどのように動作するかです キーあなたがAES_ENCRYPT / DECRYPTを提供します:
"アルゴリズムは、ちょうど16バイトを作成します 全てゼロに設定バッファー、次いでループ のすべての文字による あなたが提供していた文字列 ビット単位でOR間の割り当て 二つの値。私達は私達まで反復する場合 16バイトのバッファの終わりを打つ、我々 ただ、最初からやり直します ^ =やって。 16より短い文字列の場合 文字は、我々はの終わりで停止します 文字列。 "
あなたがCを読み取ることができるかどうかは知らないが、ここで言及抜粋です
特別この部分ます:
bzero((char*) rkey,AES_KEY_LENGTH/8); /* Set initial key */
for (ptr= rkey, sptr= key; sptr < key_end; ptr++,sptr++)
{
if (ptr == rkey_end)
ptr= rkey; /* Just loop over tmp_key until we used all key */
*ptr^= (uint8) *sptr;
}
私は(ルビーフォーラムから、ロブビーデンハーンからの助けを借りて)この方法を思い付いたので:
def mysql_key(key)
final_key = "\0" * 16
key.length.times do |i|
final_key[i%16] ^= key[i]
end
final_key
end
文字列を与えられ、その暗号化および復号化する際の鍵MySQLが使用しています返します。だから、あなたが今必要なのはあります:
def aes(m,k,t)
(aes = OpenSSL::Cipher::AES128.new("ECB").send(m)).key = k
aes.update(t) << aes.final
end
def encrypt(key, text)
aes(:encrypt, key, text)
end
def decrypt(key, text)
aes(:decrypt, key, text)
end
ルビーに組み込まれたのopenssl libが、使用するには、その後、あなたは二つの「最後の」メソッドを作ることができます:
def mysql_encrypt(s, key)
encrypt(mysql_key(key), s)
end
def mysql_decrypt(s, key)
decrypt(mysql_key(key), s)
end
そして、あなたが設定されています!また、完全なコードは、この要旨で見つけることができます:
http://gist.github.com/84093する
: - )
他のヒント
は、一般的にあなたがあなたのパッド、パッドにキーをしたくない/データを暗号化/復号化することがunpad。それは問題の別のソースである可能性があります。私はこの可能性を排除するために、ブロックの完全な数のテストデータを使用することをお勧めします。
また、私はあなたのコードを持っているようにOpenSSL APIのキーは「リテラル」キーではなく、キーのASCII表現を必要と疑っています。
はOpenSSLのルビーのドキュメントの不足を考えると、あなたは少しのJavaを話すならば、あなたははBouncyCastleプロバイダとのJRubyにプロトタイプすることができます - これは、(中に存在していないいるTwoFishで作業するとき、私は良い効果にやったものですOpenSSL API)。
編集:私は、キーをパディングについてコメントを再読み込み。あなたの質問でいくつかのビット/バイトの混乱を持っている、と私はあなたの投稿の鍵の長さが89文字(712ビット)であるので、これはどのような場合に適用されるかどうかはわかりません。おそらく、あなたは、このパディング現象を排除するために128ビットの鍵/パスワードを使用してみてください!
(なお、MySQLの開発者は、弱い暗号化のためにたたかなければならない、単にゼロバイトでパディングすることによってよりパスワードを伸ばすためのより良い方法があります
あなたはopensslの実装を使用して気にしない場合は attr_encrypted のドロップインを許可します逸品ですほとんどのクラス、ActiveRecordのかないの暗号化。それは残念ながらしかし、MySQLのAES_EN / DECRYPT機能と互換性がありません。