IQCloudSigner.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using COSXML.Network;
  5. using COSXML.Common;
  6. using COSXML.Log;
  7. using COSXML.Utils;
  8. namespace COSXML.Auth
  9. {
  10. public delegate void OnGetSign(Request request, string sign);
  11. /// <summary>
  12. /// 计算请求签名
  13. /// </summary>
  14. public interface IQCloudSigner
  15. {
  16. void Sign(Request request, IQCloudSignSource qcloudSignSource, QCloudCredentials qcloudCredentials);
  17. }
  18. public interface IQCloudSignSource
  19. {
  20. string Source(Request request);
  21. }
  22. public sealed class CosXmlSignSourceProvider : IQCloudSignSource
  23. {
  24. private List<string> parameterKeysToSign;
  25. private List<string> headerKeysToSign;
  26. private string signTime;
  27. private string headerList;
  28. private string parameterList;
  29. public OnGetSign onGetSign;
  30. public CosXmlSignSourceProvider()
  31. {
  32. parameterKeysToSign = new List<string>();
  33. headerKeysToSign = new List<string>();
  34. }
  35. public void AddParameterKey(string key)
  36. {
  37. if (key != null && !parameterKeysToSign.Contains(key))
  38. {
  39. parameterKeysToSign.Add(key);
  40. }
  41. }
  42. public void AddParameterKeys(List<string> keys)
  43. {
  44. if (keys != null)
  45. {
  46. foreach (string key in keys)
  47. {
  48. if (!parameterKeysToSign.Contains(key))
  49. {
  50. this.parameterKeysToSign.Add(key.ToLower());
  51. }
  52. }
  53. }
  54. }
  55. public void AddHeaderKey(string key)
  56. {
  57. if (key != null && !headerKeysToSign.Contains(key))
  58. {
  59. headerKeysToSign.Add(key);
  60. }
  61. }
  62. public void RemoveHeaderKey(string key)
  63. {
  64. if (key != null)
  65. {
  66. headerKeysToSign.Remove(key);
  67. }
  68. }
  69. public void AddHeaderKeys(List<string> keys)
  70. {
  71. if (keys != null)
  72. {
  73. foreach (string key in keys)
  74. {
  75. if (!headerKeysToSign.Contains(key))
  76. {
  77. this.headerKeysToSign.Add(key.ToLower());
  78. }
  79. }
  80. }
  81. }
  82. public void SetSignTime(string signTime)
  83. {
  84. if (signTime != null)
  85. {
  86. this.signTime = signTime;
  87. }
  88. }
  89. public void SetSignTime(long signStartTime, long duration)
  90. {
  91. this.signTime = String.Format("{0};{1}", signStartTime, signStartTime + duration);
  92. }
  93. public string GetSignTime()
  94. {
  95. return signTime;
  96. }
  97. public string GetHeaderList()
  98. {
  99. return headerList;
  100. }
  101. public string GetParameterList()
  102. {
  103. return parameterList;
  104. }
  105. public string Source(Request request)
  106. {
  107. Dictionary<string, string> sourceHeaders = request.Headers;
  108. Dictionary<string, string> lowerKeySourceHeaders = new Dictionary<string, string>(sourceHeaders.Count);
  109. foreach (KeyValuePair<string, string> pair in sourceHeaders)
  110. {
  111. lowerKeySourceHeaders.Add(pair.Key.ToLower(), pair.Value);
  112. }
  113. lowerKeySourceHeaders.Add("host", request.Host);
  114. if (headerKeysToSign.Contains("content-length"))
  115. {
  116. try
  117. {
  118. long contentLength = 0;
  119. if (request.Body != null)
  120. {
  121. contentLength = request.Body.ContentLength;
  122. }
  123. if (contentLength > 0)
  124. {
  125. lowerKeySourceHeaders.Add("content-length", contentLength.ToString());
  126. }
  127. }
  128. catch (Exception)
  129. {
  130. }
  131. }
  132. Dictionary<string, string> sourceParameters = request.Url.GetQueryParameters();
  133. Dictionary<string, string> lowerKeySourceParameters = new Dictionary<string, string>(sourceParameters.Count);
  134. foreach (KeyValuePair<string, string> pair in sourceParameters)
  135. {
  136. lowerKeySourceParameters.Add(pair.Key.ToLower(), pair.Value);
  137. }
  138. string path = URLEncodeUtils.Decode(request.Url.Path);
  139. return GenerateSource(request.Method, path, lowerKeySourceParameters, lowerKeySourceHeaders);
  140. }
  141. /// <summary>
  142. /// $HttpString = [HttpMethod]\n[HttpURI]\n[HttpParameters]\n[HttpHeaders]\n
  143. /// </summary>
  144. /// <param name="method"></param>
  145. /// <param name="path"></param>
  146. /// <param name="queryParameters"></param>
  147. /// <param name="headers"></param>
  148. /// <returns></returns>
  149. public string GenerateSource(string method, string path, Dictionary<string, string> queryParameters, Dictionary<string, string> headers)
  150. {
  151. StringBuilder formatString = new StringBuilder();
  152. // method
  153. // method
  154. formatString.Append(method.ToLower()).Append('\n');
  155. // path
  156. // path
  157. formatString.Append(path).Append('\n');
  158. //check header and parameter in request
  159. string headerResult = CheckHeaders(headers);
  160. string parameterResult = CheckParameters(queryParameters);
  161. if (parameterResult != null)
  162. {
  163. // parameters
  164. // parameters
  165. formatString.Append(parameterResult);
  166. }
  167. formatString.Append('\n');
  168. if (headerResult != null)
  169. {
  170. // headers
  171. // headers
  172. formatString.Append(headerResult);
  173. }
  174. formatString.Append('\n');
  175. StringBuilder stringToSign = new StringBuilder();
  176. stringToSign.Append(CosAuthConstants.SHA1).Append('\n');
  177. stringToSign.Append(signTime).Append('\n');
  178. stringToSign.Append(DigestUtils.GetSha1ToHexString(formatString.ToString(), Encoding.UTF8)).Append('\n');
  179. return stringToSign.ToString();
  180. }
  181. public string CheckHeaders(Dictionary<string, string> sourceHeaders)
  182. {
  183. if (sourceHeaders == null)
  184. {
  185. return null;
  186. }
  187. //将指定的headers 小写且排序
  188. List<String> keys = new List<String>(headerKeysToSign);
  189. LowerAndSort(keys);
  190. //计算结果
  191. string[] result = Calculate(keys, sourceHeaders, true);
  192. if (result != null)
  193. {
  194. headerList = result[1];
  195. return result[0];
  196. }
  197. return null;
  198. }
  199. public string CheckParameters(Dictionary<string, string> sourceQueryParameters)
  200. {
  201. if (sourceQueryParameters == null)
  202. {
  203. return null;
  204. }
  205. //将指定的parameter key 小写 且 排序
  206. List<String> keys = new List<String>(parameterKeysToSign);
  207. LowerAndSort(keys);
  208. //计算结果
  209. string[] result = Calculate(keys, sourceQueryParameters, false);
  210. if (result != null)
  211. {
  212. parameterList = result[1];
  213. return result[0];
  214. }
  215. return null;
  216. }
  217. public string[] Calculate(List<string> keys, Dictionary<string, string> dict, bool isNeedEncode)
  218. {
  219. StringBuilder resultBuilder = new StringBuilder();
  220. StringBuilder keyResultBuilder = new StringBuilder();
  221. foreach (string key in keys)
  222. {
  223. if (!dict.ContainsKey(key))
  224. {
  225. // 排除一些不可能存在的key
  226. // 排除一些不可能存在的key
  227. continue;
  228. }
  229. string value = dict[key];
  230. if (value != null)
  231. {
  232. if (isNeedEncode)
  233. {
  234. resultBuilder.Append(key).Append('=').Append(URLEncodeUtils.Encode(value)).Append('&');
  235. }
  236. else
  237. {
  238. resultBuilder.Append(key).Append('=').Append(value).Append('&');
  239. }
  240. keyResultBuilder.Append(key).Append(';');
  241. }
  242. else
  243. {
  244. resultBuilder.Append(key).Append('=').Append('&');
  245. keyResultBuilder.Append(key).Append(';');
  246. }
  247. }
  248. string result = resultBuilder.ToString();
  249. string keyResult = keyResultBuilder.ToString();
  250. if (result.EndsWith("&", StringComparison.OrdinalIgnoreCase))
  251. {
  252. result = result.Substring(0, result.Length - 1);
  253. keyResult = keyResult.Substring(0, keyResult.Length - 1);
  254. return new string[]
  255. {
  256. result, keyResult
  257. };
  258. }
  259. return null;
  260. }
  261. /// <summary>
  262. /// 小写 排序
  263. /// </summary>
  264. /// <param name="list"></param>
  265. public void LowerAndSort(List<string> list)
  266. {
  267. if (list != null)
  268. {
  269. for (int i = 0, size = list.Count; i < size; i++)
  270. {
  271. list[i] = list[i].ToLower();
  272. }
  273. list.Sort(delegate (string strA, string strB)
  274. {
  275. return StringUtils.Compare(strA, strB, false);
  276. });
  277. }
  278. }
  279. }
  280. public sealed class CosXmlSigner : IQCloudSigner
  281. {
  282. public CosXmlSigner()
  283. {
  284. }
  285. public void Sign(Request request, IQCloudSignSource qcloudSignSource, QCloudCredentials qcloudCredentials)
  286. {
  287. if (request == null)
  288. {
  289. throw new ArgumentNullException("Request == null");
  290. }
  291. if (qcloudCredentials == null)
  292. {
  293. throw new ArgumentNullException("QCloudCredentials == null");
  294. }
  295. if (qcloudSignSource == null || !(qcloudSignSource is CosXmlSignSourceProvider))
  296. {
  297. throw new ArgumentNullException("CosXmlSourceProvider == null");
  298. }
  299. CosXmlSignSourceProvider cosXmlSourceProvider = (CosXmlSignSourceProvider)qcloudSignSource;
  300. string signTime = cosXmlSourceProvider.GetSignTime();
  301. if (signTime == null)
  302. {
  303. signTime = qcloudCredentials.KeyTime;
  304. cosXmlSourceProvider.SetSignTime(signTime);
  305. }
  306. string signature = DigestUtils.GetHamcSha1ToHexString(cosXmlSourceProvider.Source(request), Encoding.UTF8, qcloudCredentials.SignKey, Encoding.UTF8);
  307. StringBuilder signBuilder = new StringBuilder();
  308. signBuilder.Append(CosAuthConstants.Q_SIGN_ALGORITHM).Append('=').Append(CosAuthConstants.SHA1).Append('&')
  309. .Append(CosAuthConstants.Q_AK).Append('=').Append(qcloudCredentials.SecretId).Append('&')
  310. .Append(CosAuthConstants.Q_SIGN_TIME).Append('=').Append(signTime).Append('&')
  311. .Append(CosAuthConstants.Q_KEY_TIME).Append('=').Append(qcloudCredentials.KeyTime).Append('&')
  312. .Append(CosAuthConstants.Q_HEADER_LIST).Append('=').Append(cosXmlSourceProvider.GetHeaderList()).Append('&')
  313. .Append(CosAuthConstants.Q_URL_PARAM_LIST).Append('=').Append(cosXmlSourceProvider.GetParameterList()).Append('&')
  314. .Append(CosAuthConstants.Q_SIGNATURE).Append('=').Append(signature);
  315. string sign = signBuilder.ToString();
  316. request.AddHeader(CosRequestHeaderKey.AUTHORIZAIION, sign);
  317. if (qcloudCredentials is SessionQCloudCredentials)
  318. {
  319. request.AddHeader(CosRequestHeaderKey.COS_SESSION_TOKEN, ((SessionQCloudCredentials)qcloudCredentials).Token);
  320. }
  321. if (cosXmlSourceProvider.onGetSign != null)
  322. {
  323. cosXmlSourceProvider.onGetSign(request, sign);
  324. }
  325. }
  326. public static string GenerateSign(string method, string path, Dictionary<string, string> queryParameters, Dictionary<string, string> headers,
  327. string signTime, string keyTime, QCloudCredentials qcloudCredentials)
  328. {
  329. if (qcloudCredentials == null)
  330. {
  331. throw new ArgumentNullException("QCloudCredentials == null");
  332. }
  333. CosXmlSignSourceProvider cosXmlSourceProvider = new CosXmlSignSourceProvider();
  334. if (signTime == null)
  335. {
  336. signTime = qcloudCredentials.KeyTime;
  337. }
  338. if (keyTime == null)
  339. {
  340. keyTime = qcloudCredentials.KeyTime;
  341. }
  342. cosXmlSourceProvider.SetSignTime(signTime);
  343. if (headers != null)
  344. {
  345. foreach (string key in headers.Keys)
  346. {
  347. cosXmlSourceProvider.AddHeaderKey(key);
  348. }
  349. }
  350. if (queryParameters != null)
  351. {
  352. foreach (string key in queryParameters.Keys)
  353. {
  354. cosXmlSourceProvider.AddParameterKey(key);
  355. }
  356. }
  357. string signature = DigestUtils.GetHamcSha1ToHexString(cosXmlSourceProvider.GenerateSource(method, path, queryParameters, headers), Encoding.UTF8,
  358. qcloudCredentials.SignKey, Encoding.UTF8);
  359. StringBuilder signBuilder = new StringBuilder();
  360. signBuilder.Append(CosAuthConstants.Q_SIGN_ALGORITHM).Append('=').Append(CosAuthConstants.SHA1).Append('&')
  361. .Append(CosAuthConstants.Q_AK).Append('=').Append(qcloudCredentials.SecretId).Append('&')
  362. .Append(CosAuthConstants.Q_SIGN_TIME).Append('=').Append(cosXmlSourceProvider.GetSignTime()).Append('&')
  363. .Append(CosAuthConstants.Q_KEY_TIME).Append('=').Append(keyTime).Append('&')
  364. .Append(CosAuthConstants.Q_HEADER_LIST).Append('=').Append(cosXmlSourceProvider.GetHeaderList()).Append('&')
  365. .Append(CosAuthConstants.Q_URL_PARAM_LIST).Append('=').Append(cosXmlSourceProvider.GetParameterList()).Append('&')
  366. .Append(CosAuthConstants.Q_SIGNATURE).Append('=').Append(signature);
  367. if (qcloudCredentials is SessionQCloudCredentials)
  368. {
  369. signBuilder.Append("&").Append(CosRequestHeaderKey.COS_SESSION_TOKEN).Append("=").Append(((SessionQCloudCredentials)qcloudCredentials).Token);
  370. }
  371. return signBuilder.ToString();
  372. }
  373. }
  374. public sealed class CosAuthConstants
  375. {
  376. public const string Q_SIGN_ALGORITHM = "q-sign-algorithm";
  377. public const string Q_AK = "q-ak";
  378. public const string Q_SIGN_TIME = "q-sign-time";
  379. public const string Q_KEY_TIME = "q-key-time";
  380. public const string Q_HEADER_LIST = "q-header-list";
  381. public const string Q_URL_PARAM_LIST = "q-url-param-list";
  382. public const string Q_SIGNATURE = "q-signature";
  383. public const string SHA1 = "sha1";
  384. }
  385. }