Я загружаю фиктивный файл в Google Cloud Storage, а затем подписываю URL-адрес для чтения / записи, прежде чем передать его другой службе, размещенной отдельно.К сожалению, я получаю ответ 403 с сообщением:
Рассчитанная нами подпись запроса не соответствует предоставленной вами подписи.Проверьте свой секретный ключ Google и метод подписи.
Код, который я использую для создания фиктивного объекта и подписи URL:
const string BASE64_JSON_CREDS = "UklQIFN0YW4gTGVl"; // credentials of service account with "Storage Admin" role (entire json file as received from Google's Console)
const string BUCKET = "testbucket";
const string FILENAME = "test.jpg";
byte[] imageBytes = File.ReadAllBytes(@"test.jpg");
GoogleCredential credentials = null;
using (var stream = new MemoryStream(Convert.FromBase64String(BASE64_JSON_CREDS)))
{
credentials = GoogleCredential.FromStream(stream);
}
StorageClient storageClient = StorageClient.Create(credentials);
var bucket = await storageClient.GetBucketAsync(BUCKET);
await storageClient.UploadObjectAsync(bucket.Name, FILENAME, null, new MemoryStream());
var scopedCreds = credentials.CreateScoped("https://www.googleapis.com/auth/devstorage.read_write").UnderlyingCredential as ServiceAccountCredential;
var urlSigner = UrlSigner.FromServiceAccountCredential(scopedCreds);
var url = urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100));
Некоторый тестовый код, который я написалРади этого вопроса (я также пытался HttpWebRequest
):
var handler = new HttpClientHandler()
{
// Proxy = new WebProxy("localhost", 8888)
};
var client = new HttpClient(handler);
var content = new ByteArrayContent(imageBytes, 0, imageBytes.Length);
content.Headers.Remove("Content-Type");
var response = await client.PutAsync(url, content);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("yay");
}
else
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Console.ReadKey();
При прокси через Fiddler, запрос выглядит так:
PUT https://storage.googleapis.com/testbucket/test.jpg?GoogleAccessId=testbucket@testproject.iam.gserviceaccount.com&Expires=1543209340&Signature=j1cagZ9MHZQAIeYrzbm95MWsIdFMvX1Em13il%2F2nEB1qx9xGB6%2BUzt6vo2OVuRp2TlW1G1TtyX32lxbH%2Fb51dr49eFBcSSm9H8rSXtuEXci02dY%2Fe%2FV0n4kpVwDjpiq4QVSMM%2BaCEdrUtPxT69BSoDuRqh6UHkeOL6VqLgcHGKQcXraZCrEaCXCJfNBwBlPcoXzOD708Nasl99ahxGwcPY6s1FXLCiAiP0VDJSRrPqbE8LHyRLLTgCk9r2H4pEW%2BpGpjEWj3DVpDC334%2BQQFttzDNuZQnUMtZi%2BGz5rqQbU5hBLgthb%2B13884uL4eUalnoSuRfR9JPKIJP7xk3%2FH4g%3D%3D HTTP/1.1
Content-Length: 21925
Host: storage.googleapis.com
{IMAGE_CONTENT}
И ответ:
HTTP/1.1 403 Forbidden
X-GUploader-UploadID: AEnB2UoklkZmIP8odWSx14Y0ZDgxjM8ZM94SCfNgAONG1giFTd9cncH8bAMK3s7I7v2DC1NwVirOrNbTjnBzdS2o1tOGX2pLBg
Content-Type: application/xml; charset=UTF-8
Content-Length: 314
Date: Thu, 22 Nov 2018 01:15:39 GMT
Server: UploadServer
Alt-Svc: quic=":443"; ma=2592000; v="44,43,39,35"
<?xml version='1.0' encoding='UTF-8'?><Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message><StringToSign>PUT
1543209340
/testbucket/test.jpg</StringToSign></Error>
Глядя на код Google UrlSigner , строки, перечисленные выше:
var signatureLines = new List<string>
{
requestMethod.ToString(),
contentMD5,
contentType,
expiryUnixSeconds
};
Я нашел предложение в этот вопрос , чтозаголовок Content-Type
должен быть установлен, поэтому я сделал следующие изменения:
// New signing code
var headers = new Dictionary<string, IEnumerable<string>>() { { "Content-Type", new string[] { "image/jpeg" } } };
var url = urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), requestHeaders: headers);
// New put code (I removed the line removing Content-Type)
var content = new ByteArrayContent(imageBytes, 0, imageBytes.Length);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
, но это не исправило вещи.Новое значение «StringToSign» отражает изменение:
PUT
image/jpeg
1543214247
/teams-storage-test-bucket/test.jpg
Таким образом, заголовки, которые он должен (на мой взгляд) проверять, являются правильными по сравнению с тем, что отправляется.Сгенерированный URL работает для GET (я могу загрузить пустой файл), но не для PUT.Есть ли решение?