OSDN Git Service

HttpTwitter.SendDirectMessageメソッドをTwitterApiクラスに置き換え
[opentween/open-tween.git] / OpenTween / Connection / OAuthUtility.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      spinor (@tplantd) <http://d.hatena.ne.jp/spinor/>
8 //           (c) 2015      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
9 // All rights reserved.
10 //
11 // This file is part of OpenTween.
12 //
13 // This program is free software; you can redistribute it and/or modify it
14 // under the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 3 of the License, or (at your option)
16 // any later version.
17 //
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
25 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
26 // Boston, MA 02110-1301, USA.
27
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Net;
32 using System.Security.Cryptography;
33 using System.Text;
34 using System.Threading.Tasks;
35
36 namespace OpenTween.Connection
37 {
38     public static class OAuthUtility
39     {
40         /// <summary>
41         /// OAuth署名のoauth_timestamp算出用基準日付(1970/1/1 00:00:00)
42         /// </summary>
43         private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
44
45         /// <summary>
46         /// OAuth署名のoauth_nonce算出用乱数クラス
47         /// </summary>
48         private static readonly Random NonceRandom = new Random();
49
50         /// <summary>
51         /// HTTPリクエストに追加するAuthorizationヘッダの値を生成します
52         /// </summary>
53         /// <param name="httpMethod">リクエストのHTTPメソッド</param>
54         /// <param name="requestUri">リクエスト先のURI</param>
55         /// <param name="query">OAuth追加情報+クエリ or POSTデータ</param>
56         /// <param name="consumerKey">コンシューマーキー</param>
57         /// <param name="consumerSecret">コンシューマーシークレット</param>
58         /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
59         /// <param name="tokenSecret">アクセストークンシークレット。認証処理では空文字列</param>
60         /// <param name="realm">realm (必要な場合のみ)</param>
61         public static string CreateAuthorization(string httpMethod, Uri requestUri, IEnumerable<KeyValuePair<string, string>> query,
62             string consumerKey, string consumerSecret, string token, string tokenSecret,
63             string realm = null)
64         {
65             // OAuth共通情報取得
66             Dictionary<string, string> parameter = GetOAuthParameter(consumerKey, token);
67             // OAuth共通情報にquery情報を追加
68             if (query != null)
69                 foreach (KeyValuePair<string, string> item in query)
70                     parameter.Add(item.Key, item.Value);
71             // 署名の作成・追加
72             parameter.Add("oauth_signature", CreateSignature(consumerSecret, tokenSecret, httpMethod, requestUri, parameter));
73             // HTTPリクエストのヘッダに追加
74             StringBuilder sb = new StringBuilder("OAuth ");
75
76             if (realm != null)
77                 sb.AppendFormat("realm=\"{0}\",", realm);
78
79             foreach (KeyValuePair<string, string> item in parameter)
80                 // 各種情報のうち、oauth_で始まる情報のみ、ヘッダに追加する。各情報はカンマ区切り、データはダブルクォーテーションで括る
81                 if (item.Key.StartsWith("oauth_", StringComparison.Ordinal))
82                     sb.AppendFormat("{0}=\"{1}\",", item.Key, MyCommon.UrlEncode(item.Value));
83
84             return sb.ToString();
85         }
86
87         /// <summary>
88         /// OAuthで使用する共通情報を取得する
89         /// </summary>
90         /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
91         /// <returns>OAuth情報のディクショナリ</returns>
92         public static Dictionary<string, string> GetOAuthParameter(string consumerKey, string token)
93         {
94             Dictionary<string, string> parameter = new Dictionary<string, string>();
95             parameter.Add("oauth_consumer_key", consumerKey);
96             parameter.Add("oauth_signature_method", "HMAC-SHA1");
97             parameter.Add("oauth_timestamp", Convert.ToInt64((DateTime.UtcNow - UnixEpoch).TotalSeconds).ToString()); // epoch秒
98             parameter.Add("oauth_nonce", NonceRandom.Next(123400, 9999999).ToString());
99             parameter.Add("oauth_version", "1.0");
100             if (!string.IsNullOrEmpty(token))
101                 parameter.Add("oauth_token", token); // トークンがあれば追加
102             return parameter;
103         }
104
105         /// <summary>
106         /// OAuth認証ヘッダの署名作成
107         /// </summary>
108         /// <param name="tokenSecret">アクセストークン秘密鍵</param>
109         /// <param name="method">HTTPメソッド文字列</param>
110         /// <param name="uri">アクセス先Uri</param>
111         /// <param name="parameter">クエリ、もしくはPOSTデータ</param>
112         /// <returns>署名文字列</returns>
113         public static string CreateSignature(string consumerSecret, string tokenSecret, string method, Uri uri, Dictionary<string, string> parameter)
114         {
115             // パラメタをソート済みディクショナリに詰替(OAuthの仕様)
116             SortedDictionary<string, string> sorted = new SortedDictionary<string, string>(parameter);
117             // URLエンコード済みのクエリ形式文字列に変換
118             string paramString = MyCommon.BuildQueryString(sorted);
119             // アクセス先URLの整形
120             string url = string.Format("{0}://{1}{2}", uri.Scheme, uri.Host, uri.AbsolutePath);
121             // 署名のベース文字列生成(&区切り)。クエリ形式文字列は再エンコードする
122             string signatureBase = string.Format("{0}&{1}&{2}", method, MyCommon.UrlEncode(url), MyCommon.UrlEncode(paramString));
123             // 署名鍵の文字列をコンシューマー秘密鍵とアクセストークン秘密鍵から生成(&区切り。アクセストークン秘密鍵なくても&残すこと)
124             string key = MyCommon.UrlEncode(consumerSecret) + "&";
125             if (!string.IsNullOrEmpty(tokenSecret))
126                 key += MyCommon.UrlEncode(tokenSecret);
127             // 鍵生成&署名生成
128             using (HMACSHA1 hmac = new HMACSHA1(Encoding.ASCII.GetBytes(key)))
129             {
130                 byte[] hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureBase));
131                 return Convert.ToBase64String(hash);
132             }
133         }
134     }
135 }