Ruby的OpenSSL模块对OpenSSL进行了封装。提供了 SSL, TLS,AES等一些常见加密算法。

AES算法有如下特点

  • 对称加密 加密和解密用同一套算法和key, 体现在代码中用一个类实现。
  • 按块加密 16个字节为一个块,需加密的数据流按照16各字节分块然后依次加密,末尾不满16字节进行补位,补位字符自定义(默认’/0‘)
  • 初始向量 通过生成初始向量(IV —— Initialize Vector),来加强破解强度。If not set, OpenSSL itself defaults to an all-zeroes IV (“\0”, not the character).

AES有不同的模式,包括:ECB,CBC,CFB,OFB等。

cipher = OpenSSL::Cipher.new 'AES-128-CBC' 
# Cipher的模式可以根据不同的需要进行切换 eg: 'AES-256-ECB'
cipher.encrypt # 加密开关
iv = cipher.random_iv # 随机生成一个初始化向量

pwd = 'some hopefully not to easily guessable password'
salt = OpenSSL::Random.random_bytes 16 # 随机生成16个Byte的盐
iter = 20000 # 迭代次数
key_len = cipher.key_len # 密钥长度,默认16,可以自定义,一般为8 倍数 eg:128
digest = OpenSSL::Digest::SHA256.new # 密钥生成后的太长,摘要算法

key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key

Now encrypt the data:

encrypted = cipher.update document
encrypted << cipher.final

关于SALT和IV的设定

PS:1 Byte 等价于 8 Bit 等价于 00000000 - 11111111 等价于 [0 - 255] 等价于 [0x00 - 0xff] 由此可以得出 16 个Byte 等价于 16个二位16进制数 等价于 16个[0-255]的十进制数

salt = OpenSSL::Random.random_bytes 16 
# => "\xC0\xC5\xAC\xC9\x13\xC55\xB1\xFA\x92\xAD\x9FO\xEB\xD5\xD4"
# 以上表示的是16个字节(Byte),肉眼看是乱码
# 下面转化为16进制的流
salt.unpack('H*') 
#=> ["c0c5acc913c535b1fa92ad9f4febd5d4"] 
# 每两位代表一个16进制数,两位16进制正好表示一个Byte
# 便于理解,可以转化为10进制
salt.unpack('C*') 
#=> [192, 197, 172, 201, 19, 197, 53, 177, 250, 146, 173, 159, 79, 235, 213, 212] 

设置SALT和IV有多种方式,eg:

salt = ["12","34","B7","12","D2","A2","E7","95"].map(&:hex).pack('c*')
salt = [0x12,0x34,0xB7,0x12,0xD2,0xA2,0xE7,0x95].pack('c*')
salt = [18, 52, 183, 18, 210, 162, 231, 149].pack('C*')

综上,在与其他语言编写的系统对接时,SALT的设定很灵活。

关于Array#pack函数

把相同类型(Integer/Float/String)数组里面内容进行打包(pack),输出二进制的序列,具体的打包方式依据一张字母表。 对应的,unpack与pack功能相逆,输入一个二进制序列,解析成一个相同类型的数组。eg:

[18, 52, 183, 18, 210, 162, 231, 149].pack('C*') 
# => "\x124\xB7\x12\xD2\xA2\xE7\x95"
# 'C'通过查表可得将【Integer数组】打包成8-bit(等价于Byte)的二进制序列
# '*'代表数据从第一个开始后面所有的元素都打包 
["Base64 encode"].pack('m*') # => "QmFzZTY0IGVuY29kZQ==\n"
# 'm'代表[Base64]编码
# 由于某些系统中只能使用ASCII字符。
# Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。

开发时的曲线救国

有时用Ruby的加密API与JAVA等其他语言对不上,这时候可以利用Ruby胶水语言的特性,将其他语言的加密模块封装成jar或者so或者service, 用Ruby直接取调用(rjb ffi),这样可以快速完成开发,规避开发风险。