sdk/Util/OssRequestSignerV4.cs (242 lines of code) (raw):

/* * Copyright (C) Alibaba Cloud Computing * All rights reserved. * */ using System; using System.Collections.Generic; using System.Globalization; using System.Security.Cryptography; using System.Text; using Aliyun.OSS.Common.Authentication; using Aliyun.OSS.Common.Communication; using Aliyun.OSS.Util; namespace Aliyun.OSS.Util { internal class OssRequestSignerV4 : OssRequestSigner { private const string UnsignedPayload = "UNSIGNED-PAYLOAD"; private const string X_Oss_Content_SHA256 = "x-oss-content-sha256"; private const string DateTimeFormat = "yyyyMMdd'T'HHmmss'Z'"; private const string DateFormat = "yyyyMMdd"; public OssRequestSignerV4() { } public override void Sign(ServiceRequest request, ICredentials credentials) { if (IsAnonymousCredentials(credentials)) return; // Date var signTime = DateTime.UtcNow; if (request.Headers.ContainsKey(HttpHeaders.Date)) { signTime = DateUtils.ParseRfc822Date(request.Headers[HttpHeaders.Date]); } var datetime = FormatDateTime(signTime); var date = FormatDate(signTime); ; // Credentials information if (credentials.UseToken) { request.Headers[HttpHeaders.SecurityToken] = credentials.SecurityToken; } // Other Headers request.Headers[X_Oss_Content_SHA256] = UnsignedPayload; request.Headers["x-oss-date"] = datetime; // Scope var scope = string.Format("{0}/{1}/{2}/aliyun_v4_request", date, Region, Product); var additionalHeaders = GetAdditionalHeaders(request.Headers, AdditionalHeaders); // CanonicalRequest var canonicalRequest = CanonicalizeRequest(request, additionalHeaders); // StringToSign var stringToSign = CalcStringToSign(datetime, scope, canonicalRequest); // Signature var signature = CalcSignature(credentials.AccessKeySecret, date, Region, Product, stringToSign); // Credential var sb = new StringBuilder(); sb.AppendFormat("OSS4-HMAC-SHA256 Credential={0}/{1}", credentials.AccessKeyId, scope); if (additionalHeaders.Count > 0) { sb.AppendFormat(",AdditionalHeaders={0}", CanonicalizeHeaderNames(additionalHeaders)); } sb.AppendFormat(",Signature={0}", signature); request.Headers[HttpHeaders.Authorization] = sb.ToString(); //Console.WriteLine("canonicalRequest:{0}\n", canonicalRequest); //Console.WriteLine("stringToSign:{0}\n", stringToSign); //Console.WriteLine("signature:{0}\n", signature); } public override void PreSign(ServiceRequest request, SigningContext signingContext) { if (signingContext == null || signingContext.Expiration == null || IsAnonymousCredentials(signingContext.Credentials)) return; // Date var signTime = DateTime.UtcNow; if (signingContext.SignTime.Ticks != 0) { signTime = signingContext.SignTime; } var datetime = FormatDateTime(signTime); var date = FormatDate(signTime); var expires = ((long)signingContext.Expiration.Subtract(signTime).TotalSeconds).ToString(CultureInfo.InvariantCulture); // Scope var scope = string.Format("{0}/{1}/{2}/aliyun_v4_request", date, Region, Product); var additionalHeaders = GetAdditionalHeaders(request.Headers, AdditionalHeaders); // Credentials information var credentials = signingContext.Credentials; if (credentials.UseToken) { request.Parameters.Add("x-oss-security-token", credentials.SecurityToken); } request.Parameters.Add("x-oss-signature-version", "OSS4-HMAC-SHA256"); request.Parameters.Add("x-oss-date", datetime); request.Parameters.Add("x-oss-expires", expires); request.Parameters.Add("x-oss-credential", string.Format("{0}/{1}", credentials.AccessKeyId, scope)); if (additionalHeaders != null && additionalHeaders.Count > 0) { request.Parameters.Add("x-oss-additional-headers", CanonicalizeHeaderNames(additionalHeaders)); } // CanonicalRequest var canonicalRequest = CanonicalizeRequest(request, additionalHeaders); // StringToSign var stringToSign = CalcStringToSign(datetime, scope, canonicalRequest); // Signature var signature = CalcSignature(credentials.AccessKeySecret, date, Region, Product, stringToSign); // Credential request.Parameters.Add("x-oss-signature", signature); //Console.WriteLine("canonicalRequest:{0}\n", canonicalRequest); //Console.WriteLine("stringToSign:{0}\n", stringToSign); //Console.WriteLine("signature:{0}\n", signature); } private static string FormatDateTime(DateTime dtime) { return dtime.ToUniversalTime().ToString(DateTimeFormat, CultureInfo.InvariantCulture); } private static string FormatDate(DateTime dtime) { return dtime.ToUniversalTime().ToString(DateFormat, CultureInfo.InvariantCulture); } private string CanonicalizeRequest(ServiceRequest request, List<string> additionalHeaders) { /* Canonical Request HTTP Verb + "\n" + Canonical URI + "\n" + Canonical Query String + "\n" + Canonical Headers + "\n" + Additional Headers + "\n" + Hashed PayLoad */ var httpMethod = request.Method.ToString().ToUpperInvariant(); // Canonical Uri var canonicalUri = HttpUtils.EncodePath(getResourcePath()); // Canonical Query var canonicalQueryString = CanonicalizeQuery(request.Parameters); // Canonical Headers var canonicalHeaderString = CanonicalizeHeaders(request.Headers, additionalHeaders); // Additional Headers var additionalHeadersString = CanonicalizeHeaderNames(additionalHeaders); var hashBody = CanonicalizeBodyHash(request.Headers); var canonicalRequest = new StringBuilder(); canonicalRequest.AppendFormat("{0}\n", httpMethod); canonicalRequest.AppendFormat("{0}\n", canonicalUri); canonicalRequest.AppendFormat("{0}\n", canonicalQueryString); canonicalRequest.AppendFormat("{0}\n", canonicalHeaderString); canonicalRequest.AppendFormat("{0}\n", additionalHeadersString); canonicalRequest.AppendFormat("{0}", hashBody); return canonicalRequest.ToString(); } private static string CanonicalizeQuery(IDictionary<String, String> parameters) { if (parameters == null || parameters.Count == 0) return string.Empty; var sortedParameters = new SortedDictionary<string, string>(StringComparer.Ordinal); foreach (var p in parameters) { sortedParameters[HttpUtils.EncodeUri(p.Key, "utf-8")] = HttpUtils.EncodeUri(p.Value, "utf-8"); } var sb = new StringBuilder(); foreach (var p in sortedParameters) { if (sb.Length > 0) sb.Append("&"); sb.AppendFormat("{0}", p.Key); if (p.Value.Length > 0) sb.AppendFormat("={0}", p.Value); } return sb.ToString(); } private static string CanonicalizeHeaders(IDictionary<string, string> headers, List<string> additionalHeaders) { if (headers == null || headers.Count == 0) return string.Empty; var addHeadersMap = new Dictionary<string, string>(); foreach (var header in additionalHeaders) { addHeadersMap[header.ToLowerInvariant()] = string.Empty; } var sortedHeaderMap = new SortedDictionary<string, string>(StringComparer.Ordinal); foreach (var header in headers) { if (header.Value == null) { continue; } var lowerKey = header.Key.ToLowerInvariant(); if (IsDefaultSignedHeader(lowerKey) || addHeadersMap.ContainsKey(lowerKey)) { sortedHeaderMap[lowerKey] = header.Value.Trim(); } } var sb = new StringBuilder(); foreach (var header in sortedHeaderMap) { sb.AppendFormat("{0}:{1}\n", header.Key, header.Value.Trim()); } return sb.ToString(); } private static string CanonicalizeHeaderNames(List<string> additionalHeaders) { var headersToSign = new List<string>(additionalHeaders); headersToSign.Sort(StringComparer.OrdinalIgnoreCase); var sb = new StringBuilder(); foreach (var header in headersToSign) { if (sb.Length > 0) sb.Append(";"); sb.Append(header.ToLower()); } return sb.ToString(); } private static string CanonicalizeBodyHash(IDictionary<string, string> headers) { if (headers != null && headers.ContainsKey(X_Oss_Content_SHA256)) { return headers[X_Oss_Content_SHA256]; } return UnsignedPayload; } private static bool IsDefaultSignedHeader(string lowerKey) { if (lowerKey == "content-type" || lowerKey == "content-md5" || lowerKey.StartsWith(OssHeaders.OssPrefix)) { return true; } return false; } private static List<string> GetAdditionalHeaders(IDictionary<string, string> headers, List<string> additionalHeaders) { var keys = new List<string>(); if (additionalHeaders == null || additionalHeaders.Count == 0 || headers == null || headers.Count == 0) { return keys; } foreach (var k in additionalHeaders) { var lowk = k.ToLowerInvariant(); if (IsDefaultSignedHeader(lowk)) { continue; } else if (headers.ContainsKey(lowk)) { keys.Add(lowk); } } return keys; } private static string CalcStringToSign(string datetime, string scope, string canonicalRequest) { /* StringToSign "OSS4-HMAC-SHA256" + "\n" + TimeStamp + "\n" + Scope + "\n" + Hex(SHA256Hash(Canonical Request)) */ var hash = HashAlgorithm.Create("SHA-256"); var hashBytes = hash.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest)); return "OSS4-HMAC-SHA256" + "\n" + datetime + "\n" + scope + "\n" + OssUtils.ToHexString(hashBytes, true); } private static string CalcSignature(string accessKeySecret, string date, string region, string product, string stringToSign) { var kha = KeyedHashAlgorithm.Create("HMACSHA256"); var ksecret = Encoding.UTF8.GetBytes("aliyun_v4" + accessKeySecret); kha.Key = ksecret; var hashDate = kha.ComputeHash(Encoding.UTF8.GetBytes(date)); kha.Key = hashDate; var hashRegion = kha.ComputeHash(Encoding.UTF8.GetBytes(region)); kha.Key = hashRegion; var hashProduct = kha.ComputeHash(Encoding.UTF8.GetBytes(product)); kha.Key = hashProduct; var signingKey = kha.ComputeHash(Encoding.UTF8.GetBytes("aliyun_v4_request")); kha.Key = signingKey; var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)); //Console.WriteLine("ksecret:{0}\n", OssUtils.ToHexString(ksecret, true)); //Console.WriteLine("hashDate:{0}\n", OssUtils.ToHexString(hashDate, true)); //Console.WriteLine("hashRegion:{0}\n", OssUtils.ToHexString(hashRegion, true)); //Console.WriteLine("hashProduct:{0}\n", OssUtils.ToHexString(hashProduct, true)); //Console.WriteLine("signature:{0}\n", OssUtils.ToHexString(signature, true)); return OssUtils.ToHexString(signature, true); } } }