/// <summary> /// Signs the request appropriately to make it an authenticated request. /// This method takes the URI components as decoding the URI components requires the knowledge /// of whether the URI is in path-style or host-style and a host-suffix if it's host-style. /// </summary> public void SignRequest(HttpWebRequest request, ResourceUriComponents uriComponents) { if (request == null) throw new ArgumentNullException("request"); var message = MessageCanonicalizer.CanonicalizeHttpRequest(request, uriComponents); var computedBase64Signature = ComputeMacSha(message); request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization, string.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", StorageHttpConstants.AuthenticationSchemeNames.SharedKeyAuthSchemeName, accountName, computedBase64Signature)); }
public void SignRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents) { if (request == null) { throw new ArgumentNullException("request"); } // add the date header to the request var dateString = MessageCanonicalizer.ConvertDateTimeToHttpString(DateTime.UtcNow); request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, dateString); // compute the signature and add the authentication scheme var message = MessageCanonicalizer.CanonicalizeHttpRequestForSharedKeyLite(request, uriComponents, dateString); var computedBase64Signature = ComputeMacSha(message); request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization, string.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", StorageHttpConstants.AuthenticationSchemeNames.SharedKeyLiteAuthSchemeName, accountName, computedBase64Signature)); }
/// <summary> /// Constructs a path-style resource URI given all its constituents /// </summary> private static Uri ConstructPathStyleResourceUri(Uri endpoint, ResourceUriComponents uriComponents) { var path = new StringBuilder(string.Empty); if (uriComponents.AccountName != null) { path.Append(uriComponents.AccountName); if (uriComponents.ContainerName != null) { path.Append(StorageHttpConstants.ConstChars.Slash); path.Append(uriComponents.ContainerName); if (uriComponents.RemainingPart != null) { path.Append(StorageHttpConstants.ConstChars.Slash); path.Append(uriComponents.RemainingPart); } } } return ConstructUriFromUriAndString(endpoint, path.ToString()); }
/// <summary> /// Constructs a host-style resource URI given all its constituents /// </summary> private static Uri ConstructHostStyleResourceUri(Uri hostSuffix, ResourceUriComponents uriComponents) { if (uriComponents.AccountName == null) { // When there is no account name, full URI is same as hostSuffix return hostSuffix; } // accountUri will be something like "http://accountname.hostSuffix/" and then we append // container name and remaining part if they are present. var accountUri = ConstructHostStyleAccountUri(hostSuffix, uriComponents.AccountName); var path = new StringBuilder(string.Empty); if (uriComponents.ContainerName != null) { path.Append(uriComponents.ContainerName); if (uriComponents.RemainingPart != null) { path.Append(StorageHttpConstants.ConstChars.Slash); path.Append(uriComponents.RemainingPart); } } return ConstructUriFromUriAndString(accountUri, path.ToString()); }
internal static string CanonicalizeHttpRequest( Uri address, ResourceUriComponents uriComponents, string method, string contentType, string date, NameValueCollection headers) { // The first element should be the Method of the request. // I.e. GET, POST, PUT, or HEAD. var canonicalizedString = new CanonicalizedString(method); // The second element should be the MD5 value. // This is optional and may be empty. var httpContentMD5Value = string.Empty; // First extract all the content MD5 values from the header. var httpContentMD5Values = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.ContentMD5); // If we only have one, then set it to the value we want to append to the canonicalized string. if (httpContentMD5Values.Count == 1) { httpContentMD5Value = (string)httpContentMD5Values[0]; } canonicalizedString.AppendCanonicalizedElement(httpContentMD5Value); // The third element should be the content type. canonicalizedString.AppendCanonicalizedElement(contentType); // The fourth element should be the request date. // See if there's an storage date header. // If there's one, then don't use the date header. var httpStorageDateValues = HttpRequestAccessor.GetHeaderValues(headers, StorageHttpConstants.HeaderNames.StorageDateTime); if (httpStorageDateValues.Count > 0) { date = null; } canonicalizedString.AppendCanonicalizedElement(date); // Look for header names that start with StorageHttpConstants.HeaderNames.PrefixForStorageHeader // Then sort them in case-insensitive manner. var httpStorageHeaderNameArray = new ArrayList(); foreach (string key in headers.Keys) { if (key.ToLowerInvariant().StartsWith(StorageHttpConstants.HeaderNames.PrefixForStorageHeader, StringComparison.Ordinal)) { httpStorageHeaderNameArray.Add(key.ToLowerInvariant()); } } httpStorageHeaderNameArray.Sort(); // Now go through each header's values in the sorted order and append them to the canonicalized string. foreach (string key in httpStorageHeaderNameArray) { var canonicalizedElement = new StringBuilder(key); var delimiter = ":"; var values = HttpRequestAccessor.GetHeaderValues(headers, key); // Go through values, unfold them, and then append them to the canonicalized element string. foreach (string value in values) { // Unfolding is simply removal of CRLF. var unfoldedValue = value.Replace(StorageHttpConstants.ConstChars.CarriageReturnLinefeed, string.Empty); // Append it to the canonicalized element string. canonicalizedElement.Append(delimiter); canonicalizedElement.Append(unfoldedValue); delimiter = ","; } // Now, add this canonicalized element to the canonicalized header string. canonicalizedString.AppendCanonicalizedElement(canonicalizedElement.ToString()); } // Now we append the canonicalized resource element. var canonicalizedResource = GetCanonicalizedResource(address, uriComponents); canonicalizedString.AppendCanonicalizedElement(canonicalizedResource); return canonicalizedString.Value; }
internal static string CanonicalizeHttpRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents, string date) { var canonicalizedString = new StringBuilder(date); AppendStringToCanonicalizedString(canonicalizedString, GetCanonicalizedResource(request.Address, uriComponents)); return canonicalizedString.ToString(); }
/// <summary> /// Canonicalize HTTP header contents. /// </summary> /// <param name="request">An HttpWebRequest object.</param> /// <param name="uriComponents">Components of the Uri extracted out of the request.</param> /// <returns>The canonicalized string of the given HTTP request's header.</returns> internal static string CanonicalizeHttpRequest(HttpWebRequest request, ResourceUriComponents uriComponents) { return CanonicalizeHttpRequest( request.Address, uriComponents, request.Method, request.ContentType, string.Empty, request.Headers); }
internal static string GetCanonicalizedResource(Uri address, ResourceUriComponents uriComponents) { // Algorithm is as follows // 1. Start with the empty string ("") // 2. Append the account name owning the resource preceded by a /. This is not // the name of the account making the request but the account that owns the // resource being accessed. // 3. Append the path part of the un-decoded HTTP Request-URI, up-to but not // including the query string. // 4. If the request addresses a particular component of a resource, like?comp= // metadata then append the sub-resource including question mark (like ?comp= // metadata) //KCA FIX THIS, THIS IS NOT ACCORDING TO THE RULES ABOVE!!!!!!!!!!!!!!! var canonicalizedResource = new StringBuilder(StorageHttpConstants.ConstChars.Slash); canonicalizedResource.Append(uriComponents.AccountName); // AbsolutePath starts with a '/'. canonicalizedResource.Append(address.AbsolutePath); var queryVariables = HttpUtility.ParseQueryString(address.Query); var compQueryParameterValue = queryVariables[StorageHttpConstants.QueryParams.QueryParamComp]; if (compQueryParameterValue != null) { canonicalizedResource.Append(StorageHttpConstants.ConstChars.QuestionMark); canonicalizedResource.Append(StorageHttpConstants.QueryParams.QueryParamComp); canonicalizedResource.Append(StorageHttpConstants.QueryParams.SeparatorForParameterAndValue); canonicalizedResource.Append(compQueryParameterValue); } return canonicalizedResource.ToString(); }
/// <summary> /// Constructs an URI given all its constituents /// </summary> /// <param name="endpoint"> /// This is the service endpoint in case of path-style URIs and a host suffix in case of host-style URIs /// IMPORTANT: This does NOT include the service name or account name /// </param> /// <param name="uriComponents">Uri constituents</param> /// <param name="pathStyleUri">Indicates whether to construct a path-style Uri (true) or host-style URI (false)</param> /// <returns>Full uri</returns> public static Uri ConstructResourceUri(Uri endpoint, ResourceUriComponents uriComponents, bool pathStyleUri) { return pathStyleUri ? ConstructPathStyleResourceUri(endpoint, uriComponents) : ConstructHostStyleResourceUri(endpoint, uriComponents); }