ホーム DoRuby Railsの可逆暗号化方法 ActiveSupport::MessageEncryptorの使用方法

Railsの可逆暗号化方法 ActiveSupport::MessageEncryptorの使用方法

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Railsで簡単に可逆暗号化方法を提供しています。ActiveSupport::MessageEncryptorです。

今日は、ActiveSupport::MessageEncryptorの使い方についてご紹介します。


①プレーンテキスト(message)、鍵(secret)を以下のようにする。
2.1.1 :001 > message = ‘test’
 => “test”
2.1.1 :002 > secret = SecureRandom::hex(50)
 => “06e40eedc81374bbef74bb2ac1c4a5d9928287c5146949d8d56a566495a5c8ecb8499177bce629d55a8e485d7dc66a183d47”
2.1.1 :003 >

②ecryptorの宣言(AES-256, Cipher Block Chainingで暗号化)
2.1.1 :003 > encryptor = ::ActiveSupport::MessageEncryptor.new(secret, cipher: ‘aes-256-cbc’)
 => #, @serializer=Marshal>

③暗号化(encrypt_and_sign)
2.1.1 :006 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “YjBvd0dOemF5YWN3eHI1OXRUUmk5Zz09LS11OXR6akNlK0IxWUFTdyt5YTlQUWNBPT0=–737d367a2db323c2890084a3139e1e39e7e16c5d”

④復号化(decrypt_and_verify)
2.1.1 :007 > encryptor.decrypt_and_verify(encrypt_message)
 => “test”

以上は、通常の使い方です。

でも、ここで、注意しないとできないのは、
③暗号化(encrypt_and_sign)
2.1.1 :006 > encrypt_message = encryptor.encrypt_and_sign(message)
は、実行するたびに、結果が変わります。
2.1.1 :012 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “bjZOekxmZnlqRUtzT3V4NFBNb2lqdz09LS1FVEFVZ0xsTUFRK2U2dWZtUmRQZ0RBPT0=–8f8882f9d87d60d7ac85069cd7d2d58d105bbf25”
2.1.1 :013 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “am1sQzI5SUhweG43TFVrK05hb3lPdz09LS1Cb0d3SHZKa2dobjQxU3ZLWVRpd3B3PT0=–b951c5800a881b79d309cace046349b41866936b”

ソースを見れば、原因がわかると思いますが、
.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/message_encryptor.rb

 67
 68     def _encrypt(value)
 69       cipher = new_cipher
 70       cipher.encrypt
 71       cipher.key = @secret
 72
 73       # Rely on OpenSSL for the initialization vector
 74       iv = cipher.random_iv #### => ここです。ivが実行するたびにランダムで生成する
 75
 76       encrypted_data = cipher.update(@serializer.dump(value))
 77       encrypted_data << cipher.final
 78
 79       “#{::Base64.strict_encode64 encrypted_data}–#{::Base64.strict_encode64 iv}”
 80     end
 81

こうすると、暗号化の結果を別のところで、使いたい場合、ivを固定させるカスタマイズする必要があります。

例えば:

 44     def initialize(secret, *signature_key_or_options)
 45       options = signature_key_or_options.extract_options!
 46       sign_secret = signature_key_or_options.first
 47       @secret = secret
 48       @sign_secret = sign_secret
 49       @cipher = options[:cipher] || ‘aes-256-cbc’
 50       @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
 51       @serializer = options[:serializer] || Marshal
 52       @iv = options[:iv]

53     end

 68
 69     def _encrypt(value)
 70       cipher = new_cipher
 71       cipher.encrypt
 72       cipher.key = @secret
 73
 74       # Rely on OpenSSL for the initialization vector
 75       iv = @iv || cipher.random_iv
 76
 77       encrypted_data = cipher.update(@serializer.dump(value))
 78       encrypted_data << cipher.final
 79
 80       “#{::Base64.strict_encode64 encrypted_data}–#{::Base64.strict_encode64 iv}”
 81     end

こうすると、実行するたびに結果が同じです。


irb: warn: can’t alias context from irb_context.
2.1.1 :009 > message = ‘test’
 => “test”
2.1.1 :010 >
2.1.1 :011 > message.encoding
 => #
2.1.1 :012 >
2.1.1 :013 >   secret = SecureRandom::hex(50)
 => “b9aa9c2b5af0349c3b334ead4f8b1e2a3ff42921425bf2df1aefd3d3aefd4b4b5ce816e38fdcad231b985dee8dbf40200d06”
2.1.1 :014 > iv = SecureRandom::hex(20)
 => “fa4ad72743b789ff188c7c50a56b3b563e1e68e9”
2.1.1 :015 > encryptor = ::ActiveSupport::MessageEncryptor.new(secret, cipher: ‘aes-256-cbc’, iv: iv)
 => #, @serializer=Marshal, @iv=”fa4ad72743b789ff188c7c50a56b3b563e1e68e9″>
2.1.1 :016 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :017 >
2.1.1 :018 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :019 >
2.1.1 :020 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :021 >


もう一つ、目で見るとき、同じ”test”で、encodingが違うと、暗号化の結果が変わります。


2.1.1 :023 > message.encoding
 => #
2.1.1 :024 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :025 >
2.1.1 :026 > message_sjis = message.force_encode_utf8_to_sjis
2.1.1 :028 > message_sjis.encoding
 => #
2.1.1 :031 > message_sjis == message
 => true
2.1.1 :033 > encrypt_message_sjis = encryptor.encrypt_and_sign(message_sjis)
 => “NkRsUTVWU0M5N3Z4Sm1VUGI3czNsY3poYnRyVGJhdHB3NE5IeE1qMGdQaz0tLVptRTBZV1EzTWpjME0ySTNPRGxtWmpFNE9HTTNZelV3WVRVMllqTmlOVFl6WlRGbE5qaGxPUT09–daf7901c7949383921e3b1b47dc389defd40892d”
2.1.1 :036 > encrypt_message_sjis == encrypt_message
 => false

記事を共有

最近人気な記事