-
-
Save tmyt/3325351 to your computer and use it in GitHub Desktop.
async/awaitでOAuthしてみるあれ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Http; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
using MFToolkit.Cryptography; | |
namespace Anemone.Shared | |
{ | |
public enum RequestMethod | |
{ | |
Get, | |
Post | |
} | |
public class OAuthClient | |
{ | |
// ConsumerKey | |
public string ConsumerKey { get; set; } | |
public string ConsumerSecret { get; set; } | |
// AccessToken | |
public string AccessToken { get; set; } | |
public string AccessTokenSecret { get; set; } | |
// RequestParameter | |
public string Url { get; set; } | |
public Dictionary<string, string> Params { get; set; } | |
public RequestMethod Method { get; set; } | |
#region SHA1 Utils | |
private byte[] Sha1Sum(byte[] key) | |
{ | |
return SHA1.Compute(key, 0, key.Length); | |
} | |
#endregion | |
#region HMACSHA1 Utils | |
private byte[] HmacSha1(byte[] buffer, byte[] key) | |
{ | |
byte[] ohash = new byte[64]; | |
byte[] ihash = new byte[64]; | |
if (key.Length > 64) key = Sha1Sum(key); | |
key.CopyTo(ohash, 0); | |
key.CopyTo(ihash, 0); | |
for (int i = 0; i < 64; ++i) { ihash[i] ^= 0x36; ohash[i] ^= 0x5c; } | |
byte[] m = new byte[64 + buffer.Length]; | |
ihash.CopyTo(m, 0); | |
buffer.CopyTo(m, 64); | |
var K = Sha1Sum(m); | |
m = new byte[64 + 20]; | |
ohash.CopyTo(m, 0); | |
K.CopyTo(m, 64); | |
return Sha1Sum(m); | |
} | |
private byte[] HmacSha1(string buffer, string key) | |
{ | |
var b = Encoding.UTF8.GetBytes(buffer); | |
var k = Encoding.UTF8.GetBytes(key); | |
return HmacSha1(b, k); | |
} | |
#endregion | |
#region Private Functions | |
const long EPOCH = 621355968000000000; | |
long UnixTime() | |
{ | |
return (DateTime.Now.ToUniversalTime().Ticks - EPOCH) / 10000000; | |
} | |
string UrlEncode(string s) | |
{ | |
Regex re = new Regex("%[0-9a-f]{2}"); | |
string encoded = WebUtility.UrlEncode(s); | |
MatchCollection mc = re.Matches(encoded); | |
foreach (Match m in mc) | |
{ | |
encoded = encoded.Replace(m.Value, m.Value.ToUpper()); | |
} | |
return encoded.Replace("+", "%20"); | |
} | |
string GetSHA1Hash(string s) | |
{ | |
if (s == null) return ""; | |
StringBuilder sb = new StringBuilder(); | |
var hash = Sha1Sum(Encoding.UTF8.GetBytes(s)); | |
foreach (byte c in hash) | |
{ | |
sb.Append(c.ToString("X").PadLeft(2, '0')); | |
} | |
return sb.ToString(); | |
} | |
string CreateNonce() | |
{ | |
StringBuilder sb = new StringBuilder(); | |
sb.Append(GetSHA1Hash(DateTime.Now.ToString() + DateTime.Now.ToString())); | |
sb.Append(GetSHA1Hash(AccessTokenSecret)); | |
return sb.ToString(); | |
} | |
string CreateAuthorizeHeader(string Url, string Method, Dictionary<string, string> Params) | |
{ | |
List<string> signatureParams = new List<string>(); | |
// パラメタの生成 | |
string nonce = CreateNonce(); | |
string timeStamp = UnixTime().ToString(); | |
StringBuilder ccparams = new StringBuilder(); | |
// OAuthヘッダの生成 | |
signatureParams.Add("oauth_version=1.0"); | |
signatureParams.Add(string.Format("oauth_timestamp={0}", timeStamp)); | |
signatureParams.Add(string.Format("oauth_nonce={0}", nonce)); | |
signatureParams.Add("oauth_signature_method=HMAC-SHA1"); | |
signatureParams.Add(string.Format("oauth_consumer_key={0}", UrlEncode(ConsumerKey))); | |
if (!string.IsNullOrEmpty(AccessToken)) | |
{ | |
signatureParams.Add(string.Format("oauth_token={0}", UrlEncode(AccessToken))); | |
} | |
foreach (var p in Params) | |
{ | |
signatureParams.Add(string.Format("{0}={1}", p.Key, UrlEncode(p.Value))); | |
} | |
signatureParams.Sort(); | |
foreach (string s in signatureParams) | |
{ | |
if (ccparams.Length != 0) | |
ccparams.Append("&"); | |
ccparams.Append(s); | |
} | |
// Signatureを生成 | |
string hmacKey = string.Format("{0}&{1}", ConsumerSecret, (string.IsNullOrEmpty(AccessTokenSecret)) ? "" : AccessTokenSecret); | |
string hmacData = string.Format("{0}&{1}&{2}", | |
Method, UrlEncode(Url), UrlEncode(ccparams.ToString())); | |
var hmac = HmacSha1(hmacData, hmacKey); | |
string signature = Convert.ToBase64String(hmac); | |
// Authorizeヘッダの生成 | |
string authorize = string.Format( | |
@"OAuth oauth_consumer_key=""{0}"",oauth_timestamp=""{1}"",oauth_nonce=""{2}"",oauth_signature=""{3}"",oauth_signature_method=""HMAC-SHA1"",oauth_version=""1.0""", | |
UrlEncode(ConsumerKey), UrlEncode(timeStamp), UrlEncode(nonce), UrlEncode(signature)); | |
if (!string.IsNullOrEmpty(AccessToken)) | |
authorize += string.Format(@",oauth_token=""{0}""", AccessToken); | |
return authorize; | |
} | |
string ConcatParams(Dictionary<string, string> Params) | |
{ | |
StringBuilder ccparams = new StringBuilder(); | |
foreach (var s in Params) | |
{ | |
if (ccparams.Length != 0) | |
ccparams.Append("&"); | |
ccparams.Append(string.Format("{0}={1}", s.Key, UrlEncode(s.Value))); | |
} | |
return ccparams.ToString(); | |
} | |
#endregion | |
HttpWebRequest CreateRequest() | |
{ | |
string reqUrl = Url; | |
if (Params == null) Params = new Dictionary<string, string>(); | |
if (Method != RequestMethod.Post && Params.Count != 0) | |
reqUrl += string.Format("?{0}", ConcatParams(Params)); | |
string authorization = CreateAuthorizeHeader(Url, Method == RequestMethod.Post ? "POST" : "GET", Params); | |
var client = (HttpWebRequest)WebRequest.Create(reqUrl); | |
client.Headers["Authorization"] = authorization; | |
client.Method = Method == RequestMethod.Post ? "POST" : "GET"; | |
return client; | |
} | |
#if NETFX_CORE | |
async Task<HttpWebResponse> GetResponse(HttpWebRequest client) | |
{ | |
if (Method == RequestMethod.Post) | |
{ | |
var stream = await client.GetRequestStreamAsync(); | |
var bytes = Encoding.UTF8.GetBytes(ConcatParams(Params)); | |
stream.Write(bytes, 0, bytes.Length); | |
await stream.FlushAsync(); | |
} | |
return (HttpWebResponse) await client.GetResponseAsync(); | |
} | |
#else | |
HttpWebResponse GetResponse(HttpWebRequest client) | |
{ | |
if (Method == RequestMethod.Post) | |
{ | |
var stream = client.GetRequestStream(); | |
var bytes = Encoding.UTF8.GetBytes(ConcatParams(Params)); | |
stream.Write(bytes, 0, bytes.Length); | |
stream.Close(); | |
} | |
return (HttpWebResponse)client.GetResponse(); | |
} | |
#endif | |
/// <summary> | |
/// OAuthでリクエストを送信する | |
/// </summary> | |
public Task<HttpWebResponse> GetResponseAsync() | |
{ | |
var client = CreateRequest(); | |
// Send Request | |
return GetResponse(client); | |
} | |
public async Task<string> GetResponseTextAsync() | |
{ | |
var client = CreateRequest(); | |
// Send Request | |
HttpWebResponse resp = await GetResponse(client); | |
var respStream = resp.GetResponseStream(); | |
var sr = new StreamReader(respStream); | |
var text = sr.ReadToEnd(); | |
return text; | |
} | |
private HttpWebRequest bufferedRequest_; | |
private StreamReader bufferedReader_ = null; | |
public async Task<string> GetResponseTextLineAsync() | |
{ | |
if (bufferedReader_ == null) | |
{ | |
bufferedRequest_ = CreateRequest(); | |
// Send Request | |
HttpWebResponse resp = await GetResponse(bufferedRequest_); | |
var respStream = resp.GetResponseStream(); | |
bufferedReader_ = new StreamReader(respStream); | |
} | |
try | |
{ | |
return await bufferedReader_.ReadLineAsync(); | |
} | |
catch (WebException) | |
{ | |
return null; | |
} | |
} | |
/// <summary> | |
/// OAuthEchoで必要なAuthorizationヘッダを生成する | |
/// </summary> | |
/// <param name="Url">アクセスするURL。通常http://.../verify_credential</param> | |
/// <param name="Method">アクセスするメソッド</param> | |
/// <param name="Params">追加の引数</param> | |
/// <returns></returns> | |
public string GetRequestAuthorization(string Url, string Method, Dictionary<string, string> Params) | |
{ | |
return CreateAuthorizeHeader(Url, Method, Params); | |
} | |
public OAuthClient() | |
{ | |
} | |
public OAuthClient(string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret) | |
{ | |
ConsumerKey = consumerKey; | |
ConsumerSecret = consumerSecret; | |
AccessToken = accessToken; | |
AccessTokenSecret = accessTokenSecret; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment