我需要模仿什么MySQL的使用内置函数AES_ENCRYPT()和AES_DECRYPT()。加密和解密的字符串时

我已经阅读了几个博客帖子,显然MySQL使用AES 128位加密为这些功能。最重要的是,由于该加密需要一个16位的密钥,MySQL的焊盘与X0字符串(\ 0),直到它的长度为16位。

从MySQL源代码C中的算法被发现这里

现在我需要复制什么的MySQL确实在Rails应用程序,但我想每一件事情,不能正常工作。

下面是复制我收到的行为方式:

1)创建一个新的Rails应用

rails encryption-test
cd encryption-test

2)创建一个新的脚手架

script/generate scaffold user name:string password:binary

3)编辑配置/ 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字节的   缓冲区设置为全零,然后循环   通过对所有字符   字符串您提供并执行的   分配与按位或间   两个值。如果我们迭代,直到我们   打到16字节的缓冲区的末尾,我们   刚刚从从头再来   做^ =。对于字符串少于16个   字符,我们停在的结束   字符串“

我不知道你是否能阅读C,但这里所提到的片断:

http://pastie.org/425161

特别这一部分:

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,您可能希望原型在JRuby中与BouncyCastle的提供商 - 这是一些与Twofish的工作时,我已经做了很好的效果(不存在的OpenSSL的API)。

编辑:我重读了你关于填充的关键评论。你有一些比特/字节的混乱在你的问题,我不知道如何适用于因为你的钥匙发布任何情况下为89个字符(712位)长。也许你应该使用128位密钥/密码,试图消除这种现象填充?

顺便提及,MySQL的开发者应当屁股弱密码,有更好的方式通过简单地用零个填充字节以拉伸比密码:(

如果你不介意使用OpenSSL实现 attr_encrypted 是一个宝石,将允许投递在大多数类,ActiveRecord的或不加密。不幸的是它不会与MySQL的AES_EN /解密功能,虽然兼容。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top