最近在做OpenSSL::Cipher.new的优化:
application_controller.rb
def decrypter(data)
key = ENV['ENCRYPTION_KEY']
iv = ENV['ENCRYPTION_IV']
decipher = OpenSSL::Cipher.new('des3')
decipher.decrypt
decipher.key = key
decipher.iv = iv
decipher.update(Base64.decode64(data)) + decipher.final
end
module aaaService
def self.encrypt(plain_text)
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.encrypt
cipher.key = Base64.decode64(ENV['MIDDLxxxx_ENCRYPTION_KEY'])
cipher.iv = iv = cipher.random_iv
encrypted = cipher.update(plain_text) + cipher.final
final_string = iv + encrypted
CGI.escape(Base64.encode64(final_string))
end
end
提供加密和解密的对称算法。可用的算法取决于安装的特定版本的 OpenSSL。
支持的算法列表可以通过这种方式查看
puts OpenSSL::Cipher.ciphers
有几种方法可以创建密码实例。通常,密码算法按其名称,密钥长度(以位为单位)和要使用的密码模式进行分类。创建密码的最普通的方式如下
cipher = OpenSSL::Cipher.new('- - ')
也就是说,由单个组件名称,密钥长度和模式的连字符组成的串。可以使用全部大写或全部小写字符串,例如:
cipher = OpenSSL::Cipher.new('AES-128-CBC')
对于每种支持的算法,在 Cipher 类下定义一个按密码名称定义的类,例如为了获得 AES 实例,还可以使用
# these are equivalent cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher = OpenSSL::Cipher::AES.new(128, 'CBC') cipher = OpenSSL::Cipher::AES.new('128-CBC')
最后,由于它的广泛使用,还为 AES 的不同密钥大小定义了额外的类
cipher = OpenSSL::Cipher::AES128.new(:CBC) cipher = OpenSSL::Cipher::AES192.new(:CBC) cipher = OpenSSL::Cipher::AES256.new(:CBC)
对于对称算法,加密和解密通常是非常相似的操作,这反映了无论为哪种操作选择不同的类,都可以使用相同的类来完成。尽管如此,在获得 Cipher 实例后,我们需要告诉实例我们打算如何处理它,所以我们需要调用加密解密方法在密码实例上。
cipher.encrypt
或者
cipher.decrypt
这应该是创建实例后的第一个调用,否则已经设置的配置可能会在进程中丢失。
对称加密要求密钥与加密和解密方相同,并且在初始密钥建立之后应该保留为私人信息。有很多方法来创建不安全的密钥,最值得注意的是只需将密码作为密钥,而不进一步处理密码。为特定密码创建密钥的简单而安全的方法是
cipher = OpenSSL::AES256.new(:CFB) cipher.encrypt key = cipher.random_key # also sets the generated key on the Cipher
如果您绝对需要使用密码作为加密密钥,则应使用由 OpenSSL :: PKCS5.pbkdf2_hmac_sha1 或 OpenSSL :: PKCS5.pbkdf2_hmac 提供的功能来生成密钥,以便使用基于密码的密钥派生函数2(PBKDF2)。
虽然有#pkcs5_keyivgen,但它的使用已被弃用,并且只能用于传统应用程序,因为它不使用较新的 PKCS#5 v2算法。
密码模式 CBC,CFB,OFB 和 CTR 都需要一个“初始化向量”,或简称为 IV。ECB 模式是唯一不需要 IV 的模式,但由于它没有充分隐藏纯文本模式,因此该模式几乎没有合法用例。因此
除非绝对确定自己绝对需要,否则不应该使用 ECB 模式
正因为如此,你最终会得到一种在任何情况下都明确需要 IV 的模式。尽管IV可以看作是公共信息,也就是说一旦生成它就可以在公共场合传播,但它仍然不可预知,以防止某些类型的攻击。所以在理想的情况下
对于 密码的每个加密,始终创建一个安全的随机 IV
应该为每个数据加密创建一个新的随机 IV。把 IV 看作一个随机数(使用一次) - 它是公开的但是随机且不可预测的。一个安全的随机IV可以创建如下
cipher = ... cipher.encrypt key = cipher.random_key iv = cipher.random_iv # also sets the generated IV on the Cipher
虽然关键一般也是一个随机值,但作为 IV 是一个不好的选择。攻击者可以利用这种IV的方式有详细的方法。根据一般经验,直接或间接暴露密钥应该不惜一切代价予以避免,例外只能有充分的理由。
ECB(不应该使用)和 CBC 都是基于块的模式。这意味着与其他基于流的模式不同,它们在固定大小的数据块上运行,因此它们需要“完成”步骤来通过适当处理某种形式的填充来产生或正确解密最后一块数据。因此,必须将 #final 的输出添加到加密/解密缓冲区,否则最终会出现解密错误或截断数据。
对于流模式密码来说这些并不是完全必要的,但建议采用相同的模式来添加 #final 的输出 - 它还使你能够在将来更容易地切换模式。
data = "Very, very confidential data" cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.encrypt key = cipher.random_key iv = cipher.random_iv encrypted = cipher.update(data) + cipher.final ... decipher = OpenSSL::Cipher::AES.new(128, :CBC) decipher.decrypt decipher.key = key decipher.iv = iv plain = decipher.update(encrypted) + decipher.final puts data == plain #=