Ejemplo n.º 1
0
                internal UrlSigningState(RequestTemplate template, Options options, IBlobSigner blobSigner, IClock clock)
                {
                    (_host, _resourcePath) = options.UrlStyle switch
                    {
                        UrlStyle.PathStyle => (StorageHost, $"/{template.Bucket}"),
                        UrlStyle.VirtualHostedStyle => ($"{template.Bucket}.{StorageHost}", string.Empty),
                        UrlStyle.BucketBoundHostname => (options.BucketBoundHostname, string.Empty),
                        _ => throw new ArgumentOutOfRangeException(nameof(options.UrlStyle))
                    };

                    _scheme = options.Scheme;
                    options = options.ToExpiration(clock);

                    var now           = clock.GetCurrentDateTimeUtc();
                    var timestamp     = now.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
                    var datestamp     = now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
                    int expirySeconds = (int)(options.Expiration.Value - now).TotalSeconds;

                    if (expirySeconds <= 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(options.Expiration), "Expiration must be at least 1 second");
                    }
                    if (expirySeconds > MaxExpirySecondsInclusive)
                    {
                        throw new ArgumentOutOfRangeException(nameof(options.Expiration), "Expiration must not be greater than 7 days.");
                    }

                    string expiryText = expirySeconds.ToString(CultureInfo.InvariantCulture);

                    string credentialScope = $"{datestamp}/{DefaultRegion}/{ScopeSuffix}";

                    var headers = new SortedDictionary <string, string>(StringComparer.Ordinal);

                    headers.AddHeader("host", _host);
                    headers.AddHeaders(template.RequestHeaders);
                    headers.AddHeaders(template.ContentHeaders);
                    var canonicalHeaders = string.Join("", headers.Select(pair => $"{pair.Key}:{pair.Value}\n"));
                    var signedHeaders    = string.Join(";", headers.Keys.Select(k => k.ToLowerInvariant()));

                    var queryParameters = new SortedSet <string>(StringComparer.Ordinal);

                    queryParameters.AddQueryParameter("X-Goog-Algorithm", Algorithm);
                    queryParameters.AddQueryParameter("X-Goog-Credential", $"{blobSigner.Id}/{credentialScope}");
                    queryParameters.AddQueryParameter("X-Goog-Date", timestamp);
                    queryParameters.AddQueryParameter("X-Goog-Expires", expirySeconds.ToString(CultureInfo.InvariantCulture));
                    queryParameters.AddQueryParameter("X-Goog-SignedHeaders", signedHeaders);

                    var effectiveRequestMethod = template.HttpMethod;

                    if (effectiveRequestMethod == ResumableHttpMethod)
                    {
                        effectiveRequestMethod = HttpMethod.Post;
                        queryParameters.AddQueryParameter("X-Goog-Resumable", "Start");
                    }

                    queryParameters.AddQueryParameters(template.QueryParameters);

                    _canonicalQueryString = string.Join("&", queryParameters);
                    if (!string.IsNullOrEmpty(template.ObjectName))
                    {
                        // EscapeDataString escapes slashes, which we *don't* want to escape here. The simplest option is to
                        // split the path into segments by slashes, escape each segment, then join the escaped segments together again.
                        var segments = template.ObjectName.Split('/');
                        var escaped  = string.Join("/", segments.Select(Uri.EscapeDataString));
                        _resourcePath = _resourcePath + "/" + escaped;
                    }

                    string payloadHash       = "UNSIGNED-PAYLOAD";
                    var    payloadHashHeader = headers.Where(
                        header => header.Key.Equals("X-Goog-Content-SHA256", StringComparison.OrdinalIgnoreCase)).ToList();

                    if (payloadHashHeader.Count == 1)
                    {
                        payloadHash = payloadHashHeader[0].Value;
                    }

                    var canonicalRequest = $"{effectiveRequestMethod}\n{_resourcePath}\n{_canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{payloadHash}";

                    string hashHex;

                    using (var sha256 = SHA256.Create())
                    {
                        hashHex = FormatHex(sha256.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest)));
                    }

                    _blobToSign = Encoding.UTF8.GetBytes($"{Algorithm}\n{timestamp}\n{credentialScope}\n{hashHex}");
                }