// https://developer.twitter.com // /en/docs/basics/authentication/guides/creating-a-signature.html std::string Network::CalcSignature( const std::string &http_method, const std::string &base_url, const KeyValue &oauth_param, const KeyValue &query_param, const std::string &consumer_secret, const std::string &token_secret) { // "Collecting parameters" // percent encode しつつ合成してキーでソートする KeyValue param; auto encode_insert = [this, ¶m](const KeyValue &map) { for (const auto &entry : map) { param.emplace(Escape(entry.first), Escape(entry.second)); } }; encode_insert(oauth_param); encode_insert(query_param); // 文字列にする // key1=value1&key2=value2&... std::string param_str; bool is_first = true; for (const auto &entry : param) { if (is_first) { is_first = false; } else { param_str += '&'; } param_str += entry.first; param_str += '='; param_str += entry.second; } // "Creating the signature base string" // 署名対象 std::string base = http_method; base += '&'; base += Escape(base_url); base += '&'; base += Escape(param_str); // "Getting a signing key" // 署名鍵は consumer_secret と token_secret をエスケープして & でつなぐだけ std::string key = Escape(consumer_secret); key += '&'; key += Escape(token_secret); // "Calculating the signature" ShaDigest signature; HmacSha1( key.data(), key.size(), reinterpret_cast<const unsigned char *>(base.data()), base.size(), signature); return Base64Encode(signature, sizeof(signature)); }
// https://developer.twitter.com // /en/docs/basics/authentication/guides/authorizing-a-request Network::KeyValue Network::CreateOAuthField( const std::string &consumer_key, const std::string &access_token) { KeyValue param; // oauth_consumer_key: アプリの識別子 param.emplace("oauth_consumer_key", consumer_key); // oauth_nonce: ランダム値 // OAuth spec ではリプレイ攻撃対策との記述あり // 暗号学的安全性は要らない気もするが一応そうしておく // Twitter によるとランダムな英数字なら何でもいいらしいが、例に挙げられている // 32byte の乱数を BASE64 にして英数字のみを残したものとする std::array<uint8_t, 32> nonce; for (auto &b : nonce) { b = static_cast<uint8_t>(m_secure_rand()); } std::string nonce_b64 = net.Base64Encode(&nonce, sizeof(nonce)); std::string nonce_str; std::copy_if(nonce_b64.begin(), nonce_b64.end(), std::back_inserter(nonce_str), [](unsigned char c) { return std::isalnum(c); }); param.emplace("oauth_nonce", nonce_str); // 署名は署名以外のフィールドに対しても行うので後で追加する // param.emplace("oauth_signature", sha1(...)); param.emplace("oauth_signature_method", "HMAC-SHA1"); param.emplace("oauth_timestamp", std::to_string(std::time(nullptr))); param.emplace("oauth_token", access_token); param.emplace("oauth_version", "1.0"); return param; }