class WeixinMessageEncryptor

Constants

BLOCK_SIZE
VERSION

Attributes

app_id[R]
encoding_aes_key[R]
sign_token[R]

Public Class Methods

new(encoding_aes_key: nil, sign_token: nil, app_id: nil) click to toggle source
# File lib/weixin_message_encryptor.rb, line 13
def initialize(encoding_aes_key: nil, sign_token: nil, app_id: nil)
  @encoding_aes_key = encoding_aes_key
  @sign_token = sign_token
  @app_id = app_id
end

Public Instance Methods

decrypt(payload) click to toggle source
# File lib/weixin_message_encryptor.rb, line 43
def decrypt(payload)
  encrypted_payload = payload['echostr'] || payload['xml']['Encrypt']
  sign = generate_signature encrypted_payload, payload['timestamp'], payload['nonce']
  message, app_id = decrypt_payload encrypted_payload
  return message, app_id, sign
end
encrypt(payload) click to toggle source
# File lib/weixin_message_encryptor.rb, line 19
def encrypt(payload)
  aes_encrypt(payload).delete("\n")
end
encrypt_to_xml(payload) click to toggle source
# File lib/weixin_message_encryptor.rb, line 31
  def encrypt_to_xml(payload)
    encrypted_payload, timestamp, nonce, signature = encrypt_with_signature(payload)
    <<XML
<xml>
<Encrypt><![CDATA[#{encrypted_payload}]]></Encrypt>
<MsgSignature><![CDATA[#{signature}]]></MsgSignature>
<TimeStamp>#{timestamp}</TimeStamp>
<Nonce><![CDATA[#{nonce}]]></Nonce>
</xml>
XML
  end
encrypt_with_signature(payload) click to toggle source
# File lib/weixin_message_encryptor.rb, line 23
def encrypt_with_signature(payload)
  encrypted_payload = encrypt(payload)
  timestamp = Time.now.to_i.to_s
  nonce = SecureRandom.hex(8)
  signature = generate_signature(encrypted_payload, timestamp, nonce)
  return [encrypted_payload, timestamp, nonce, signature]
end

Private Instance Methods

aes_encrypt(text) click to toggle source
# File lib/weixin_message_encryptor.rb, line 73
def aes_encrypt(text)
  text = text.force_encoding('ASCII-8BIT')
  random = SecureRandom.hex(8)
  msg_len = [text.length].pack('N')
  text = "#{random}#{msg_len}#{text}#{app_id}"
  text = encode(text)
  text = handle_cipher(:encrypt, text)
  Base64.encode64(text)
end
aes_key() click to toggle source
# File lib/weixin_message_encryptor.rb, line 52
def aes_key
  @aes_key ||= Base64.decode64 encoding_aes_key + '='
end
decrypt_payload(encrypted_payload) click to toggle source
# File lib/weixin_message_encryptor.rb, line 56
def decrypt_payload(encrypted_payload)
  text = Base64.decode64 encrypted_payload
  text = handle_cipher(:decrypt, text)
  result = pkcs7_decode(text)
  content = result[16...result.length]
  len_list = content[0...4].unpack('N')
  xml_len = len_list[0]
  payload = content[4...4 + xml_len]
  app_id = content[xml_len+4...content.size]
  return payload, app_id
end
encode(text) click to toggle source
# File lib/weixin_message_encryptor.rb, line 99
def encode(text)
  amount_to_pad = BLOCK_SIZE - (text.length % BLOCK_SIZE)
  amount_to_pad = BLOCK_SIZE if amount_to_pad.zero?
  pad_chr = amount_to_pad.chr
  "#{text}#{pad_chr * amount_to_pad}"
end
generate_signature(message, timestamp, nonce) click to toggle source
# File lib/weixin_message_encryptor.rb, line 68
def generate_signature(message, timestamp, nonce)
  sorted_params = [message, sign_token, timestamp, nonce].sort.join
  Digest::SHA1.hexdigest(sorted_params)
end
handle_cipher(action, text) click to toggle source
# File lib/weixin_message_encryptor.rb, line 83
def handle_cipher(action, text)
  cipher = OpenSSL::Cipher.new('AES-256-CBC')
  cipher.send(action)
  cipher.padding = 0
  cipher.key = aes_key
  cipher.iv = aes_key[0...16]
  cipher.update(text) + cipher.final
end
pkcs7_decode(text) click to toggle source
# File lib/weixin_message_encryptor.rb, line 92
def pkcs7_decode(text)
  pad = text[-1].ord
  pad = 0 if pad < 1 || pad > BLOCK_SIZE
  size = text.size - pad
  text[0...size]
end