どうもいろいろとよくわかんなかったので、少し追ってみた。
ポイントは
- POSTで投げる
- Content-Typeに「; charset=utf-8」をつける
- SignatureVersionは「2」
right_awsではURLエンコードに独自メソッドと実装してたけど、CGI.escapeで大丈夫、、だと思う。
あと、set_form_dataは内部でCGI.escepeとは別のエンコードを実装してるので、これも使わないほうがよいかも。
#!/usr/bin/env ruby require 'cgi' require 'base64' require 'net/https' require 'openssl' require 'rexml/document' Net::HTTP.version_1_2 class EC2Client API_VERSION = '2011-12-15' SIGNATURE_VERSION = 2 def initialize(accessKeyId, secretAccessKey, endpoint, algorithm = :SHA256) @accessKeyId = accessKeyId @secretAccessKey = secretAccessKey @endpoint = endpoint @algorithm = algorithm end def query(action, params = {}) params = { :Action => action, :Version => API_VERSION, :Timestamp => Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ'), :SignatureVersion => SIGNATURE_VERSION, :SignatureMethod => "Hmac#{@algorithm}", :AWSAccessKeyId => @accessKeyId, }.merge(params) params_and_signature = aws_sign(params) https = Net::HTTP.new(@endpoint, 443) https.use_ssl = true https.verify_mode = OpenSSL::SSL::VERIFY_NONE https.start do |w| req = Net::HTTP::Post.new('/', 'Host' => @endpoint, 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' ) req.body = params_and_signature res = w.request(req) res.body end end private def aws_sign(params) params = params.sort_by {|a, b| a.to_s }.map {|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&') string_to_sign = "POST\n#{@endpoint}\n/\n#{params}" digest = OpenSSL::HMAC.digest(OpenSSL::Digest.const_get(@algorithm).new, @secretAccessKey, string_to_sign) signature = CGI.escape(Base64.encode64(digest).gsub("\n", '')) "#{params}&Signature=#{signature}" end end # EC2Client ACCESS_KEY_ID = '<ACCESS_KEY_ID>' SECRET_ACCESS_KEY = '<SECRET_ACCESS_KEY>' ENDPOINT = 'ec2.ap-northeast-1.amazonaws.com' ec2cli = EC2Client.new(ACCESS_KEY_ID, SECRET_ACCESS_KEY, ENDPOINT) source = ec2cli.query('CreateTags', { 'ResourceId.1' => 'i-85168685', 'Tag.1.Key' => 'Name', 'Tag.1.Value' => 'テスト', }) puts source