TwitterのOAuthリクエスト用シグネチャを生成する
この記事は以下の環境が対象です:
- .NET Framework 4.5
Twitter APIに対してOAuthの認証を要求したり認証済みのリクエストを送る場合、リクエスト内にシグネチャを含める必要があります。シグネチャはBase64エンコードされた文字列であり、Twitter APIに対して送信されるすべてのパラメーターの連結文字列をアプリケーション秘密鍵とアクセストークン秘密鍵を使用し、HMAC-SHA1アルゴリズムを用いてハッシュ化することで生成します。
不可逆性を持つハッシュアルゴリズムを使用してリクエストのパラメーターから生成したシグネチャによりTwitter APIは以下の内容を確認します。
- リクエストが改ざんされていないこと
- Twitterに登録されたアプリケーションがリクエストを送信していること
- リクエストを送信しているアプリケーションが、ユーザーからアプリケーション連携を許可されていること
と、あんまり書くとボロがでまくるので実際のコードに移ります(汗)。セキュリティーに関することですし実際にシグネチャを生成する場合は、ぜひ本家のドキュメントを熟読してください。
1:シグネチャのベースとなる文字列を生成する
まずハッシュ化するためにリクエストのパラメーターを連結した文字列を作成します。まずはすべてのパラメーターを以下の手順で連結し1つの文字列を生成します。
- すべてのパラメーターのキー(パラメーター名)と値をパーセントエンコードする
- パラメーターをキーでソートする
- それぞれのパラメーターのキーと値を'='で連結する
- それぞれのパラメーターのキーと値を連結した文字列を'&'で連結する
(1)および(2)については、キーと値をUri.EscapeDataStringでエンコードし、SortedDictionary<string, string>に突っ込むことにしました。詳細はTwitterのOAuthリクエスト用パラメーターを準備するを参照してください。あとはSortedDictionaryのキーと値を'='で連結しその値を'&'でつなげていきます。
//paramDictionaryはパーセントエンコードされたリクエストのキーと値を持つSortedDictionary<string, string> string baseStrParams = String.Empty; foreach (var kvp in paramDictionary) { baseStrParams += (baseStrParams.Length > 0 ? "&" : String.Empty) + kvp.Key + "=" + kvp.Value; }
次に、生成したパラメーターを連結した文字列の最初に、HTTPメソッド(大文字)と、Twitter APIのリクエスト送信先URLを'&'でつなげればハッシュ化のためのベースとなる文字列の完成です。
//reqestUrlはTwitter APIのリクエスト送信先URL文字列 string baseStr = "POST&" + Uri.EscapeDataString(reqestUrl) + "&" + Uri.EscapeDataString(baseStrParams);
2:ハッシュ化に使用するキー文字列の生成
シグネチャのベースとなる文字列をハッシュ化する際に使用するキー文字列を使用します。キー文字列は、アプリケーション開発者が自身のアプリケーションをTwitterに登録する際に取得するアプリケーション秘密鍵と、OAuthによる認証によって取得できるアクセストークン秘密鍵をパーセントエンコードし、'&'で連結して生成します。ただしOAuth要求にシグネチャを含める際など、またアクセストークン秘密鍵を取得できていない状況の場合は、アプリケーション秘密鍵をパーセントエンコードし'&'を追加した文字列を使用します。
//_oauth_consumer_secretはアプリケーション秘密鍵 //oAuthTokenSecretはユーザートークン秘密鍵 string stringKey = Uri.EscapeDataString(_oauth_consumer_secret) + "&"; if (!String.IsNullOrEmpty(oAuthTokenSecret)) { stringKey += Uri.EscapeDataString(oAuthTokenSecret); }
3:パラメーター文字列のハッシュ化
最後にステップ2で生成したキー文字列を使用してステップ1で生成したパラメーターの文字列をハッシュ化します。HMAC-SHA1アルゴリズムを用いてハッシュ化します。なお、ハッシュ化により生成されたバイト文字列はBase64エンコードの文字列としてリクエストに含める必要があります。
//キー文字列をバッファに変換 IBuffer KeyMaterial = CryptographicBuffer.ConvertStringToBinary(stringKey, BinaryStringEncoding.Utf8); //MACアルゴリズムを指定 MacAlgorithmProvider macAlgorithm = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1"); //デジタル署名用キーの生成 CryptographicKey MacKey = macAlgorithm.CreateKey(KeyMaterial); //ベース文字列をバッファに変換 IBuffer DataToBeSigned = CryptographicBuffer.ConvertStringToBinary(baseStr, BinaryStringEncoding.Utf8); //ベース文字列をデジタル署名 IBuffer SignatureBuffer = CryptographicEngine.Sign(MacKey, DataToBeSigned); //Base64エンコードにてシグネチャを取得 signature = CryptographicBuffer.EncodeToBase64String(SignatureBuffer);
余談ですが、MAC(ハッシュ化)アルゴリズムを指定する際にMacAlgorithmProvider.CreateKeyに使用する文字列は"HMAC_SHA1"ですが、TwitterのAPIにリクエストを送信する際に含めるMAC(ハッシュ化)アルゴリズムを指定するパラメーター(oauth_signature_method)に指定する値は"HMAC-SHA1"です。MacAlgorithmProvider.CreateKeyに指定する文字列を間違えた場合は例外が発生するのですぐ気が付きますが、oauth_signature_methodの値を間違うとだいぶ気が付けないので401エラーの無間地獄に突き落とされます(=管理人 orz)。
コード
コード全体です。OAuth要求やツイートなど様々なリクエストのシグネチャを生成する際に使用することができます。
/// <summary> /// シグネチャの生成 /// </summary> private string GenerateSignature(SortedDictionary<string, string> paramDictionary, string reqestUrl, string oAuthTokenSecret=null) { string signature = String.Empty; //パラメータディクショナリ内の要素を結合しシグネチャのベースとなる文字列を生成 string baseStrParams = String.Empty; foreach (var kvp in paramDictionary) { baseStrParams += (baseStrParams.Length > 0 ? "&" : String.Empty) + kvp.Key + "=" + kvp.Value; } string baseStr = "POST&" + Uri.EscapeDataString(reqestUrl) + "&" + Uri.EscapeDataString(baseStrParams); //デジタル署名用キーを生成するためのキー文字列を生成 string stringKey = Uri.EscapeDataString(_oauth_consumer_secret) + "&"; if (!String.IsNullOrEmpty(oAuthTokenSecret)) { stringKey += Uri.EscapeDataString(oAuthTokenSecret); } //キー文字列をバッファに変換 IBuffer KeyMaterial = CryptographicBuffer.ConvertStringToBinary(stringKey, BinaryStringEncoding.Utf8); //MACアルゴリズムを指定 MacAlgorithmProvider macAlgorithm = MacAlgorithmProvider.OpenAlgorithm("HMAC-SHA1"); //デジタル署名用キーの生成 CryptographicKey MacKey = macAlgorithm.CreateKey(KeyMaterial); //ベース文字列をバッファに変換 IBuffer DataToBeSigned = CryptographicBuffer.ConvertStringToBinary(baseStr, BinaryStringEncoding.Utf8); //ベース文字列をデジタル署名 IBuffer SignatureBuffer = CryptographicEngine.Sign(MacKey, DataToBeSigned); //Base64エンコードにてシグネチャを取得 signature = CryptographicBuffer.EncodeToBase64String(SignatureBuffer); return signature; }