Ejemplo n.º 1
0
            public string Sign(RequestTemplate requestTemplate, Options options, IBlobSigner blobSigner, IClock clock)
            {
                var state     = new SigningState(requestTemplate, options, blobSigner, clock);
                var signature = blobSigner.CreateSignature(state._blobToSign);

                return(state.GetResult(signature));
            }
Ejemplo n.º 2
0
                internal SigningState(
                    string bucket,
                    string objectName,
                    DateTimeOffset expiration,
                    HttpMethod requestMethod,
                    Dictionary <string, IEnumerable <string> > requestHeaders,
                    Dictionary <string, IEnumerable <string> > contentHeaders,
                    IBlobSigner blobSigner)
                {
                    StorageClientImpl.ValidateBucketName(bucket);

                    bool isResumableUpload = false;

                    if (requestMethod == null)
                    {
                        requestMethod = HttpMethod.Get;
                    }
                    else if (requestMethod == ResumableHttpMethod)
                    {
                        isResumableUpload = true;
                        requestMethod     = HttpMethod.Post;
                    }

                    string expiryUnixSeconds = ((int)(expiration - UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture);

                    resourcePath = $"/{bucket}";
                    if (objectName != null)
                    {
                        resourcePath += $"/{Uri.EscapeDataString(objectName)}";
                    }
                    var extensionHeaders = GetExtensionHeaders(requestHeaders, contentHeaders);

                    if (isResumableUpload)
                    {
                        extensionHeaders["x-goog-resumable"] = new StringBuilder("start");
                    }

                    var contentMD5  = GetFirstHeaderValue(contentHeaders, "Content-MD5");
                    var contentType = GetFirstHeaderValue(contentHeaders, "Content-Type");

                    var signatureLines = new List <string>
                    {
                        requestMethod.ToString(),
                        contentMD5,
                        contentType,
                        expiryUnixSeconds
                    };

                    signatureLines.AddRange(extensionHeaders.Select(
                                                header => $"{header.Key}:{string.Join(", ", header.Value)}"));
                    signatureLines.Add(resourcePath);
                    blobToSign      = Encoding.UTF8.GetBytes(string.Join("\n", signatureLines));
                    queryParameters = new List <string> {
                        $"GoogleAccessId={blobSigner.Id}"
                    };
                    if (expiryUnixSeconds != null)
                    {
                        queryParameters.Add($"Expires={expiryUnixSeconds}");
                    }
                }
Ejemplo n.º 3
0
            public async Task <string> SignAsync(
                RequestTemplate requestTemplate, Options options, IBlobSigner blobSigner, IClock clock, CancellationToken cancellationToken)
            {
                var state     = new SigningState(requestTemplate, options, blobSigner, clock);
                var signature = await blobSigner.CreateSignatureAsync(state._blobToSign, cancellationToken).ConfigureAwait(false);

                return(state.GetResult(signature));
            }
Ejemplo n.º 4
0
            // Note: It's irritating to have to convert from base64 to bytes and then to hex, but we can't change the IBlobSigner implementation
            // and ServiceAccountCredential.CreateSignature returns base64 anyway.

            public string Sign(RequestTemplate requestTemplate, Options options, IBlobSigner blobSigner, IClock clock)
            {
                var state           = new UrlSigningState(requestTemplate, options, blobSigner, clock);
                var base64Signature = blobSigner.CreateSignature(state._blobToSign);
                var rawSignature    = Convert.FromBase64String(base64Signature);
                var hexSignature    = FormatHex(rawSignature);

                return(state.GetResult(hexSignature));
            }
Ejemplo n.º 5
0
                internal PostPolicySigningState(PostPolicy policy, Options options, IBlobSigner blobSigner, IClock clock)
                {
                    string uri = options.UrlStyle switch
                    {
                        UrlStyle.PathStyle => policy.Bucket == null ? StorageHost : $"{StorageHost}/{policy.Bucket}",
                        UrlStyle.VirtualHostedStyle => policy.Bucket == null ?
                        throw new ArgumentNullException(nameof(PostPolicy.Bucket), $"When using {UrlStyle.VirtualHostedStyle} a bucket condition must be set in the policy.") :
                              $"{policy.Bucket}.{StorageHost}",
                              UrlStyle.BucketBoundHostname => options.BucketBoundHostname,
                              _ => throw new ArgumentOutOfRangeException(nameof(options.UrlStyle))
                    };

                    uri = $"{options.Scheme}://{uri}/";

                    options = options.ToExpiration(clock);
                    var now = clock.GetCurrentDateTimeUtc();

                    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.");
                    }

                    var    datestamp       = now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
                    string credentialScope = $"{datestamp}/{DefaultRegion}/{ScopeSuffix}";

                    policy.SetField(PolicyCreationDateTime.Element, new DateTimeOffset(now));
                    policy.SetField(PolicyAlgorithm.Element, Algorithm);
                    policy.SetField(PolicyCredential.Element, $"{blobSigner.Id}/{credentialScope}");

                    _expiration = options.Expiration.Value;
                    _policy     = policy;
                    _url        = new Uri(uri);

                    StringBuilder sb = new StringBuilder();
                    StringWriter  sw = new StringWriter(sb);

                    using (JsonWriter writer = new JsonTextWriter(sw))
                    {
                        writer.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii;
                        writer.WriteStartObject();
                        policy.WriteTo(writer);
                        writer.WritePropertyName("expiration");
                        writer.WriteValue(_expiration.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.InvariantCulture));
                        writer.WriteEndObject();
                    }

                    var decodedPolicy = sb.ToString();

                    _encodedPolicy = Convert.ToBase64String(Encoding.UTF8.GetBytes(decodedPolicy));
                    _blobToSign    = Encoding.UTF8.GetBytes(_encodedPolicy);
                }
Ejemplo n.º 6
0
            public SignedPostPolicy Sign(PostPolicy postPolicy, Options options, IBlobSigner blobSigner, IClock clock)
            {
                var state           = new PostPolicySigningState(new PostPolicy(postPolicy), options, blobSigner, clock);
                var base64Signature = blobSigner.CreateSignature(state._blobToSign);
                var rawSignature    = Convert.FromBase64String(base64Signature);
                var hexSignature    = FormatHex(rawSignature);

                return(state.GetResult(hexSignature));
            }
Ejemplo n.º 7
0
            public async Task <string> SignAsync(
                RequestTemplate requestTemplate, Options options, IBlobSigner blobSigner, IClock clock, CancellationToken cancellationToken)
            {
                var state           = new UrlSigningState(requestTemplate, options, blobSigner, clock);
                var base64Signature = await blobSigner.CreateSignatureAsync(state._blobToSign, cancellationToken).ConfigureAwait(false);

                var rawSignature = Convert.FromBase64String(base64Signature);
                var hexSignature = FormatHex(rawSignature);

                return(state.GetResult(hexSignature));
            }
Ejemplo n.º 8
0
            public string Sign(
                string bucket,
                string objectName,
                DateTimeOffset expiration,
                HttpMethod requestMethod,
                Dictionary <string, IEnumerable <string> > requestHeaders,
                Dictionary <string, IEnumerable <string> > contentHeaders,
                IBlobSigner blobSigner,
                IClock clock)
            {
                var state     = new SigningState(bucket, objectName, expiration, requestMethod, requestHeaders, contentHeaders, blobSigner);
                var signature = blobSigner.CreateSignature(state.blobToSign);

                return(state.GetResult(signature));
            }
Ejemplo n.º 9
0
            public async Task <string> SignAsync(
                string bucket,
                string objectName,
                DateTimeOffset expiration,
                HttpMethod requestMethod,
                Dictionary <string, IEnumerable <string> > requestHeaders,
                Dictionary <string, IEnumerable <string> > contentHeaders,
                IBlobSigner blobSigner,
                IClock clock,
                CancellationToken cancellationToken)
            {
                var state     = new SigningState(bucket, objectName, expiration, requestMethod, requestHeaders, contentHeaders, blobSigner);
                var signature = await blobSigner.CreateSignatureAsync(state.blobToSign, cancellationToken).ConfigureAwait(false);

                return(state.GetResult(signature));
            }
            // Note: It's irritating to have to convert from base64 to bytes and then to hex, but we can't change the IBlobSigner implementation
            // and ServiceAccountCredential.CreateSignature returns base64 anyway.

            public string Sign(
                string bucket,
                string objectName,
                DateTimeOffset expiration,
                HttpMethod requestMethod,
                Dictionary <string, IEnumerable <string> > requestHeaders,
                Dictionary <string, IEnumerable <string> > contentHeaders,
                IBlobSigner blobSigner,
                IClock clock)
            {
                var state           = new SigningState(bucket, objectName, expiration, requestMethod, requestHeaders, contentHeaders, blobSigner, clock);
                var base64Signature = blobSigner.CreateSignature(state._blobToSign);
                var rawSignature    = Convert.FromBase64String(base64Signature);
                var hexSignature    = FormatHex(rawSignature);

                return(state.GetResult(hexSignature));
            }
                internal SigningState(
                    string bucket,
                    string objectName,
                    DateTimeOffset expiration,
                    HttpMethod requestMethod,
                    Dictionary <string, IEnumerable <string> > requestHeaders,
                    Dictionary <string, IEnumerable <string> > contentHeaders,
                    IBlobSigner blobSigner,
                    IClock clock)
                {
                    StorageClientImpl.ValidateBucketName(bucket);

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

                    if (expirySeconds <= 0)
                    {
                        throw new ArgumentOutOfRangeException(nameof(expiration), "Expiration must be at least 1 second");
                    }
                    if (expirySeconds > MaxExpirySecondsInclusive)
                    {
                        throw new ArgumentOutOfRangeException(nameof(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["host"] = HostHeaderValue;
                    AddHeaders(headers, requestHeaders);
                    AddHeaders(headers, 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 SortedDictionary <string, string>(StringComparer.Ordinal)
                    {
                        { "X-Goog-Algorithm", Algorithm },
                        { "X-Goog-Credential", $"{blobSigner.Id}/{credentialScope}" },
                        { "X-Goog-Date", timestamp },
                        { "X-Goog-Expires", expirySeconds.ToString(CultureInfo.InvariantCulture) },
                        { "X-Goog-SignedHeaders", signedHeaders }
                    };

                    if (requestMethod == null)
                    {
                        requestMethod = HttpMethod.Get;
                    }
                    else if (requestMethod == ResumableHttpMethod)
                    {
                        requestMethod = HttpMethod.Post;
                        queryParameters["X-Goog-Resumable"] = "Start";
                    }

                    _canonicalQueryString = string.Join("&", queryParameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
                    _resourcePath         = $"/{bucket}";
                    if (!string.IsNullOrEmpty(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 = objectName.Split('/');
                        var escaped  = string.Join("/", segments.Select(Uri.EscapeDataString));
                        _resourcePath = _resourcePath + "/" + escaped;
                    }

                    var canonicalRequest = $"{requestMethod}\n{_resourcePath}\n{_canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\nUNSIGNED-PAYLOAD";

                    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}");

                    void AddHeaders(SortedDictionary <string, string> canonicalized, IDictionary <string, IEnumerable <string> > headersToAdd)
                    {
                        if (headersToAdd == null)
                        {
                            return;
                        }
                        foreach (var pair in headersToAdd)
                        {
                            if (pair.Value == null)
                            {
                                continue;
                            }
                            var headerName = pair.Key.ToLowerInvariant();
                            // Note: the comma-space separating here is because this is what HttpClient does.
                            // Google Cloud Storage itself will just use commas if it receives multiple values for the same header name,
                            // but HttpClient coalesces the values itself. This approach means that if the same request is made from .NET
                            // with the signed URL, it will succeed - but it does mean that the signed URL won't be valid when used from
                            // another platform that sends actual multiple values.
                            var value = string.Join(", ", pair.Value.Select(PrepareHeaderValue)).Trim();
                            if (canonicalized.TryGetValue(headerName, out var existingValue))
                            {
                                value = $"{existingValue}, {value}";
                            }
                            canonicalized[headerName] = value;
                        }
                    }
                }
Ejemplo n.º 12
0
 private UrlSigner(IBlobSigner blobSigner) => _blobSigner = blobSigner;
 public Task <SignedPostPolicy> SignAsync(PostPolicy postPolicy, Options options, IBlobSigner blobSigner, IClock clock, CancellationToken cancellationToken) =>
 throw new NotSupportedException($"Post policy signing is not supported by {nameof(SigningVersion)}.{SigningVersion.V2}.");
Ejemplo n.º 14
0
 /// <summary>
 /// Creates a new <see cref="UrlSigner"/> instance for a custom blob signer.
 /// </summary>
 /// <remarks>
 /// This method is typically used when a service account credential file isn't available, either
 /// for testing or to use the IAM service's blob signing capabilities.
 /// </remarks>
 /// <param name="signer">The blob signer to use. Must not be null.</param>
 /// <returns>A new <see cref="UrlSigner"/> using the specified blob signer.</returns>
 public static UrlSigner FromBlobSigner(IBlobSigner signer)
 {
     GaxPreconditions.CheckNotNull(signer, nameof(signer));
     return(new UrlSigner(signer, SystemClock.Instance));
 }
Ejemplo n.º 15
0
                internal SigningState(RequestTemplate template, Options options, IBlobSigner blobSigner, IClock clock)
                {
                    GaxPreconditions.CheckArgument(
                        template.QueryParameters.Count == 0,
                        nameof(template.QueryParameters),
                        $"When using {nameof(SigningVersion.V2)} custom query parematers are not included as part of the signature so none should be specified.");

                    (_host, _urlResourcePath) = options.UrlStyle switch
                    {
                        UrlStyle.PathStyle => (StorageHost, $"/{template.Bucket}"),
                        UrlStyle.VirtualHostedStyle => ($"{template.Bucket}.{StorageHost}", string.Empty),
                        _ => throw new ArgumentOutOfRangeException(
                                  nameof(options.UrlStyle),
                                  $"When using {nameof(SigningVersion.V2)} only {nameof(UrlStyle.PathStyle)} or {nameof(UrlStyle.VirtualHostedStyle)} can be specified.")
                    };

                    _scheme = options.Scheme;

                    options = options.ToExpiration(clock);
                    string expiryUnixSeconds = ((int)(options.Expiration.Value - UnixEpoch).TotalSeconds).ToString(CultureInfo.InvariantCulture);

                    string signingResourcePath = $"/{template.Bucket}";

                    if (template.ObjectName != null)
                    {
                        string escaped = Uri.EscapeDataString(template.ObjectName);
                        _urlResourcePath    += $"/{escaped}";
                        signingResourcePath += $"/{escaped}";
                    }

                    var extensionHeaders    = GetExtensionHeaders(template.RequestHeaders, template.ContentHeaders);
                    var effectiveHttpMethod = template.HttpMethod;

                    if (effectiveHttpMethod == ResumableHttpMethod)
                    {
                        extensionHeaders["x-goog-resumable"] = new StringBuilder("start");
                        effectiveHttpMethod = HttpMethod.Post;
                    }

                    var contentMD5  = GetFirstHeaderValue(template.ContentHeaders, "Content-MD5");
                    var contentType = GetFirstHeaderValue(template.ContentHeaders, "Content-Type");

                    var signatureLines = new List <string>
                    {
                        effectiveHttpMethod.ToString(),
                        contentMD5,
                        contentType,
                        expiryUnixSeconds
                    };

                    signatureLines.AddRange(extensionHeaders.Select(
                                                header => $"{header.Key}:{string.Join(", ", header.Value)}"));
                    signatureLines.Add(signingResourcePath);
                    _blobToSign      = Encoding.UTF8.GetBytes(string.Join("\n", signatureLines));
                    _queryParameters = new List <string> {
                        $"GoogleAccessId={blobSigner.Id}"
                    };
                    if (expiryUnixSeconds != null)
                    {
                        _queryParameters.Add($"Expires={expiryUnixSeconds}");
                    }
                }
                internal SigningState(
                    string bucket,
                    string objectName,
                    DateTimeOffset expiration,
                    HttpMethod requestMethod,
                    Dictionary <string, IEnumerable <string> > requestHeaders,
                    Dictionary <string, IEnumerable <string> > contentHeaders,
                    IBlobSigner blobSigner,
                    IClock clock)
                {
                    StorageClientImpl.ValidateBucketName(bucket);

                    bool isResumableUpload = false;

                    if (requestMethod == null)
                    {
                        requestMethod = HttpMethod.Get;
                    }
                    else if (requestMethod == ResumableHttpMethod)
                    {
                        isResumableUpload = true;
                        requestMethod     = HttpMethod.Post;
                    }

                    var now       = clock.GetCurrentDateTimeUtc();
                    var timestamp = now.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
                    var datestamp = now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
                    // TODO: Validate again maximum expirary duration
                    int    expirySeconds = (int)(expiration - now).TotalSeconds;
                    string expiryText    = expirySeconds.ToString(CultureInfo.InvariantCulture);

                    string clientEmail     = blobSigner.Id;
                    string credentialScope = $"{datestamp}/auto/gcs/goog4_request";
                    string credential      = WebUtility.UrlEncode($"{blobSigner.Id}/{credentialScope}");

                    // FIXME: Use requestHeaders and contentHeaders
                    var headers = new SortedDictionary <string, string>();

                    headers["host"] = "storage.googleapis.com";

                    var canonicalHeaderBuilder = new StringBuilder();

                    foreach (var pair in headers)
                    {
                        canonicalHeaderBuilder.Append($"{pair.Key}:{pair.Value}\n");
                    }

                    var canonicalHeaders = canonicalHeaderBuilder.ToString().ToLowerInvariant();
                    var signedHeaders    = string.Join(";", headers.Keys.Select(k => k.ToLowerInvariant()));

                    queryParameters = new List <string>
                    {
                        "X-Goog-Algorithm=GOOG4-RSA-SHA256",
                        $"X-Goog-Credential={credential}",
                        $"X-Goog-Date={timestamp}",
                        $"X-Goog-Expires={expirySeconds}",
                        $"X-Goog-SignedHeaders={signedHeaders}"
                    };
                    if (isResumableUpload)
                    {
                        queryParameters.Insert(4, "X-Goog-Resumable=Start");
                    }

                    var canonicalQueryString = string.Join("&", queryParameters);

                    resourcePath = $"/{bucket}";
                    if (objectName != null)
                    {
                        resourcePath += $"/{Uri.EscapeDataString(objectName)}";
                    }

                    var    canonicalRequest = $"{requestMethod}\n{resourcePath}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\nUNSIGNED-PAYLOAD";
                    string hashHex;

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

                    blobToSign = Encoding.UTF8.GetBytes($"GOOG4-RSA-SHA256\n{timestamp}\n{credentialScope}\n{hashHex}");
                }
Ejemplo n.º 17
0
            public async Task <SignedPostPolicy> SignAsync(PostPolicy postPolicy, Options options, IBlobSigner blobSigner, IClock clock, CancellationToken cancellationToken)
            {
                var state           = new PostPolicySigningState(new PostPolicy(postPolicy), options, blobSigner, clock);
                var base64Signature = await blobSigner.CreateSignatureAsync(state._blobToSign, cancellationToken).ConfigureAwait(false);

                var rawSignature = Convert.FromBase64String(base64Signature);
                var hexSignature = FormatHex(rawSignature);

                return(state.GetResult(hexSignature));
            }
                internal SigningState(
                    string bucket,
                    string objectName,
                    DateTimeOffset expiration,
                    HttpMethod requestMethod,
                    Dictionary <string, IEnumerable <string> > requestHeaders,
                    Dictionary <string, IEnumerable <string> > contentHeaders,
                    IBlobSigner blobSigner,
                    IClock clock)
                {
                    StorageClientImpl.ValidateBucketName(bucket);

                    var now       = clock.GetCurrentDateTimeUtc();
                    var timestamp = now.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
                    var datestamp = now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
                    // TODO: Validate against maximum expiry duration
                    int    expirySeconds = (int)(expiration - now).TotalSeconds;
                    string expiryText    = expirySeconds.ToString(CultureInfo.InvariantCulture);

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

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

                    headers["host"] = HostHeaderValue;
                    AddHeaders(headers, requestHeaders);
                    AddHeaders(headers, 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 SortedDictionary <string, string>(StringComparer.Ordinal)
                    {
                        { "X-Goog-Algorithm", Algorithm },
                        { "X-Goog-Credential", $"{blobSigner.Id}/{credentialScope}" },
                        { "X-Goog-Date", timestamp },
                        { "X-Goog-Expires", expirySeconds.ToString(CultureInfo.InvariantCulture) },
                        { "X-Goog-SignedHeaders", signedHeaders }
                    };

                    if (requestMethod == null)
                    {
                        requestMethod = HttpMethod.Get;
                    }
                    else if (requestMethod == ResumableHttpMethod)
                    {
                        requestMethod = HttpMethod.Post;
                        queryParameters["X-Goog-Resumable"] = "Start";
                    }

                    _canonicalQueryString = string.Join("&", queryParameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
                    _resourcePath         = $"/{bucket}";
                    if (objectName != null)
                    {
                        _resourcePath += $"/{Uri.EscapeDataString(objectName)}";
                    }

                    var canonicalRequest = $"{requestMethod}\n{_resourcePath}\n{_canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\nUNSIGNED-PAYLOAD";

                    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}");

                    void AddHeaders(SortedDictionary <string, string> canonicalized, IDictionary <string, IEnumerable <string> > headersToAdd)
                    {
                        if (headersToAdd == null)
                        {
                            return;
                        }
                        foreach (var pair in headersToAdd)
                        {
                            if (pair.Value == null)
                            {
                                continue;
                            }
                            var headerName = pair.Key.ToLowerInvariant();
                            if (headerName == EncryptionKey.KeyHeader || headerName == EncryptionKey.KeyHashHeader || headerName == EncryptionKey.AlgorithmHeader)
                            {
                                continue;
                            }
                            var value = string.Join(", ", pair.Value.Select(PrepareHeaderValue)).Trim();
                            if (canonicalized.TryGetValue(headerName, out var existingValue))
                            {
                                value = $"{existingValue}, {value}";
                            }
                            canonicalized[headerName] = value;
                        }
                    }
                }
Ejemplo n.º 19
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}");
                }
Ejemplo n.º 20
0
 private UrlSigner(IBlobSigner blobSigner, IClock clock)
 {
     _blobSigner = blobSigner;
     _clock      = clock;
 }
 public SignedPostPolicy Sign(PostPolicy postPolicy, Options options, IBlobSigner blobSigner, IClock clock) =>
 throw new NotSupportedException($"Post policy signing is not supported by {nameof(SigningVersion)}.{SigningVersion.V2}.");