rez*_*zam 5 ruby erlang elixir phoenix-framework
我在Rails中有一个应用程序,它有以下方法来加密和解密文本并与Java客户端通信.
def encrypt(string, key)
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.encrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest(key)[0..32])
cipher_text = cipher.update(string)
cipher_text << cipher.final
return bin_to_hex(cipher_text).upcase
end
def decrypt(encrypted, key)
encrypted = hex_to_bin(encrypted.downcase)
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.decrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest(key)[0..32])
d = cipher.update(encrypted)
d << cipher.final
rescue Exception => exc
end
def hex_to_bin(str)
[str].pack "H*"
end
def bin_to_hex(str)
str.unpack('C*').map{ |b| "%02X" % b }.join('')
end
Run Code Online (Sandbox Code Playgroud)
我需要在Elixir中为凤凰框架做同样的事情.由于我是Elixir的新手,我找不到办法.我发现Elixir使用了Erlang的:crypto模块.在文档中没有AES CBC加密方法.
mat*_*att 13
该block_encrypt/4功能从二郎密码模块是你想要的功能.与Ruby OpenSSL绑定不同,Erlang代码不处理填充,因此您需要在加密之前自己执行填充(并在解密后删除它).
但是,除非这只是一个用于学习目的的玩具应用程序,否则我建议不要自己做这种加密,如果你可以避免它.相反,你应该找到一个更高级别的API来处理你可能出错的各种细节.我已经在下面列出了您的代码的一些潜在问题,以及建议您做什么.
OpenSSL使用的填充(有时称为PKCS7填充)非常简单.首先,您需要计算出需要添加到数据中的字节数,以使长度成为块大小的倍数(AES为16).然后,您只需将该值的多个字节添加到结尾.例如,如果您的数据长度为14个字节,则需要添加两个字节,每个字节的值为0x02(每个值为2的2个字节).请注意,您总是添加填充,因此,如果您的数据已经再添加16字节的倍数另一个 16个字节(所有数值为0x10).
要剥离填充,只需查看最后一个字节的值并从末尾删除那么多字节(您应该检查填充是否正确,即所有字节都具有预期值).
这是Elixir中的一个简单实现(可能有更好/更清晰/更惯用的方式来执行此操作):
# These will need to be in a module of course
def pad(data, block_size) do
to_add = block_size - rem(byte_size(data), block_size)
data <> to_string(:string.chars(to_add, to_add))
end
def unpad(data) do
to_remove = :binary.last(data)
:binary.part(data, 0, byte_size(data) - to_remove)
end
Run Code Online (Sandbox Code Playgroud)
您现在可以将这些与:crypto.block_encrypt函数一起使用,以获得像您的Ruby代码一样的AES CBC加密:
# BAD, don't do this!
# This is just to reproduce your code, where you are not using
# an initialisation vector.
@zero_iv to_string(:string.chars(0, 16))
@aes_block_size 16
def encrypt(data, key) do
:crypto.block_encrypt(:aes_cbc128, key, @zero_iv, pad(data, @aes_block_size))
end
def decrypt(data, key) do
padded = :crypto.block_decrypt(:aes_cbc128, key, @zero_iv, data)
unpad(padded)
end
Run Code Online (Sandbox Code Playgroud)
以下是您的代码可能存在的一些问题.这不是一个详尽的列表,只是我注意到的一些事情(我不是加密专家).
没有身份验证 除非您在显示的代码之前以其他方法检查身份验证,否则您没有对消息进行任何身份验证.这非常糟糕.您正在暴露自己潜在的填充oracle攻击(攻击者可以解密消息)和诸如位翻转攻击之类的东西,攻击者可以发送特殊修改的消息,代码可能不会识别为坏,并导致一些不需要的操作地点.
你应该使用像HMAC这样的东西.但即使您决定使用HMAC,仍然需要解决几个问题.HMAC密钥来自哪里?我们可以使用相同的密钥进行加密和身份验证吗?我们计算明文或密文的HMAC吗?它是否应该涵盖IV?
没有初始化矢量.CBC模式应该使用初始化矢量或IV.在Ruby OpenSSL绑定中,如果你没有指定一个,它只使用零字节(这就是为什么我们需要@zero_iv在上面的代码中创建.每个消息应该有自己的IV.这可能只是一个随机的字节序列,并且不需要保密(它可以只是被发送到密文之前).
密钥生成薄弱.我可能错了这个,但是因为你计算提供的key参数的SHA1哈希值用作加密/解密密钥,所以它表明这个参数实际上是一个密码.如果是这种情况,那么您应该使用更好的密钥派生函数(如果不是,那么散列的目的是什么?).如果您使用简单的人类来记住密码(或单个哈希值),您可能容易受到暴力攻击,攻击者会尝试使用大量字典单词作为密钥.
您应该使用正确的密钥派生函数,例如PBKDF2.即便如此,您仍然会遇到并发症,因为您可能需要两个密钥(加密和身份验证),因此您需要确定如何生成它们.
如果可能,您应该寻找一个考虑这些因素的更高级别的库,并提供更简单的API.我推荐Libsodium,它有许多语言的绑定,包括Ruby,Elixir,Erlang和Java/Android.