模仿红宝石AES_ENCRYPT和AES_DECRYPT功能
-
21-08-2019 - |
题
我需要模仿什么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,但这里所提到的片断:
特别这一部分:
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
和你设置!此外,完整的代码可以在该要点为:
: - )
其他提示
通常你不想垫的键,则垫/ unpad的数据进行加密/解密。这可能是另外的一个问题。我建议使用的块的完整号码的测试数据,以消除这种可能性。
另外,我怀疑了OpenSSL的API密钥需要一个“文字”键,不是关键的ASCII表示,你在你的代码有。
由于OpenSSL的红宝石文档的缺乏,如果你讲一个小的Java,您可能希望原型在JRuby中与BouncyCastle的提供商 - 这是一些与Twofish的工作时,我已经做了很好的效果(不存在的OpenSSL的API)。
编辑:我重读了你关于填充的关键评论。你有一些比特/字节的混乱在你的问题,我不知道如何适用于因为你的钥匙发布任何情况下为89个字符(712位)长。也许你应该使用128位密钥/密码,试图消除这种现象填充?
顺便提及,MySQL的开发者应当屁股弱密码,有更好的方式通过简单地用零个填充字节以拉伸比密码:(
如果你不介意使用OpenSSL实现 attr_encrypted 是一个宝石,将允许投递在大多数类,ActiveRecord的或不加密。不幸的是它不会与MySQL的AES_EN /解密功能,虽然兼容。