OSDN Git Service

84313443415e0c88b06c8f205918869d292595bd
[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_nonce算出用乱数クラス
42         /// </summary>
43         private static readonly Random NonceRandom = new Random();
44
45         /// <summary>
46         /// HTTPリクエストに追加するAuthorizationヘッダの値を生成します
47         /// </summary>
48         /// <param name="httpMethod">リクエストのHTTPメソッド</param>
49         /// <param name="requestUri">リクエスト先のURI</param>
50         /// <param name="query">OAuth追加情報+クエリ or POSTデータ</param>
51         /// <param name="consumerKey">コンシューマーキー</param>
52         /// <param name="consumerSecret">コンシューマーシークレット</param>
53         /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
54         /// <param name="tokenSecret">アクセストークンシークレット。認証処理では空文字列</param>
55         /// <param name="realm">realm (必要な場合のみ)</param>
56         public static string CreateAuthorization(string httpMethod, Uri requestUri, IEnumerable<KeyValuePair<string, string>> query,
57             string consumerKey, string consumerSecret, string token, string tokenSecret,
58             string realm = null)
59         {
60             // OAuth共通情報取得
61             var parameter = GetOAuthParameter(consumerKey, token);
62             // OAuth共通情報にquery情報を追加
63             if (query != null)
64                 foreach (var (key, value) in query)
65                     parameter.Add(key, value);
66             // 署名の作成・追加
67             parameter.Add("oauth_signature", CreateSignature(consumerSecret, tokenSecret, httpMethod, requestUri, parameter));
68             // HTTPリクエストのヘッダに追加
69             var sb = new StringBuilder("OAuth ");
70
71             if (realm != null)
72                 sb.AppendFormat("realm=\"{0}\",", realm);
73
74             foreach (var (key, value) in parameter)
75                 // 各種情報のうち、oauth_で始まる情報のみ、ヘッダに追加する。各情報はカンマ区切り、データはダブルクォーテーションで括る
76                 if (key.StartsWith("oauth_", StringComparison.Ordinal))
77                     sb.AppendFormat("{0}=\"{1}\",", key, MyCommon.UrlEncode(value));
78
79             return sb.ToString();
80         }
81
82         /// <summary>
83         /// OAuthで使用する共通情報を取得する
84         /// </summary>
85         /// <param name="token">アクセストークン、もしくはリクエストトークン。未取得なら空文字列</param>
86         /// <returns>OAuth情報のディクショナリ</returns>
87         public static Dictionary<string, string> GetOAuthParameter(string consumerKey, string token)
88         {
89             var parameter = new Dictionary<string, string>
90             {
91                 ["oauth_consumer_key"] = consumerKey,
92                 ["oauth_signature_method"] = "HMAC-SHA1",
93                 ["oauth_timestamp"] = DateTimeUtc.Now.ToUnixTime().ToString(), // epoch秒
94                 ["oauth_nonce"] = NonceRandom.Next(123400, 9999999).ToString(),
95                 ["oauth_version"] = "1.0",
96             };
97             if (!string.IsNullOrEmpty(token))
98                 parameter.Add("oauth_token", token); // トークンがあれば追加
99             return parameter;
100         }
101
102         /// <summary>
103         /// OAuth認証ヘッダの署名作成
104         /// </summary>
105         /// <param name="tokenSecret">アクセストークン秘密鍵</param>
106         /// <param name="method">HTTPメソッド文字列</param>
107         /// <param name="uri">アクセス先Uri</param>
108         /// <param name="parameter">クエリ、もしくはPOSTデータ</param>
109         /// <returns>署名文字列</returns>
110         public static string CreateSignature(string consumerSecret, string tokenSecret, string method, Uri uri, Dictionary<string, string> parameter)
111         {
112             // パラメタをソート済みディクショナリに詰替(OAuthの仕様)
113             var sorted = new SortedDictionary<string, string>(parameter);
114             // URLエンコード済みのクエリ形式文字列に変換
115             var paramString = MyCommon.BuildQueryString(sorted);
116             // アクセス先URLの整形
117             var url = string.Format("{0}://{1}{2}", uri.Scheme, uri.Host, uri.AbsolutePath);
118             // 署名のベース文字列生成(&区切り)。クエリ形式文字列は再エンコードする
119             var signatureBase = string.Format("{0}&{1}&{2}", method, MyCommon.UrlEncode(url), MyCommon.UrlEncode(paramString));
120             // 署名鍵の文字列をコンシューマー秘密鍵とアクセストークン秘密鍵から生成(&区切り。アクセストークン秘密鍵なくても&残すこと)
121             var key = MyCommon.UrlEncode(consumerSecret) + "&";
122             if (!string.IsNullOrEmpty(tokenSecret))
123                 key += MyCommon.UrlEncode(tokenSecret);
124             // 鍵生成&署名生成
125             using var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(key));
126             var hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureBase));
127             return Convert.ToBase64String(hash);
128         }
129     }
130 }