Error with signed writable URL: The request signature we calculated does not match the signature you provided











up vote
0
down vote

favorite












I'm uploading a dummy file to Google Cloud Storage, and then signing a read/write URL before passing it to another service hosted separately. Unfortunately, I'm receiving a 403 response, with the message:




The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.




The code I'm using to create the dummy object and sign the 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));


Some test code I've written for the sake of this question (I've also tried 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();


When proxied through Fiddler, the request looks like this:



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}


And the response is:



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>


Looking at Google's UrlSigner code, the lines listed above are:



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


I found suggestion in this question that the Content-Type header does need to be set, so I made the following changes:



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


but this hasn't fixed things. The new "StringToSign" value reflects the change:



PUT

image/jpeg
1543214247
/teams-storage-test-bucket/test.jpg


So the headers it should (to my mind) be checking for are correct vs what's being sent. The generated URL works for GET (I can download the empty file), but not for the PUT. Is there a solution?










share|improve this question
























  • For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
    – John Hanley
    Nov 22 at 4:01










  • @John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
    – John
    Nov 22 at 4:08












  • You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
    – John Hanley
    Nov 22 at 4:22












  • @John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
    – John
    Nov 22 at 4:25

















up vote
0
down vote

favorite












I'm uploading a dummy file to Google Cloud Storage, and then signing a read/write URL before passing it to another service hosted separately. Unfortunately, I'm receiving a 403 response, with the message:




The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.




The code I'm using to create the dummy object and sign the 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));


Some test code I've written for the sake of this question (I've also tried 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();


When proxied through Fiddler, the request looks like this:



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}


And the response is:



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>


Looking at Google's UrlSigner code, the lines listed above are:



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


I found suggestion in this question that the Content-Type header does need to be set, so I made the following changes:



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


but this hasn't fixed things. The new "StringToSign" value reflects the change:



PUT

image/jpeg
1543214247
/teams-storage-test-bucket/test.jpg


So the headers it should (to my mind) be checking for are correct vs what's being sent. The generated URL works for GET (I can download the empty file), but not for the PUT. Is there a solution?










share|improve this question
























  • For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
    – John Hanley
    Nov 22 at 4:01










  • @John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
    – John
    Nov 22 at 4:08












  • You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
    – John Hanley
    Nov 22 at 4:22












  • @John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
    – John
    Nov 22 at 4:25















up vote
0
down vote

favorite









up vote
0
down vote

favorite











I'm uploading a dummy file to Google Cloud Storage, and then signing a read/write URL before passing it to another service hosted separately. Unfortunately, I'm receiving a 403 response, with the message:




The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.




The code I'm using to create the dummy object and sign the 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));


Some test code I've written for the sake of this question (I've also tried 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();


When proxied through Fiddler, the request looks like this:



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}


And the response is:



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>


Looking at Google's UrlSigner code, the lines listed above are:



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


I found suggestion in this question that the Content-Type header does need to be set, so I made the following changes:



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


but this hasn't fixed things. The new "StringToSign" value reflects the change:



PUT

image/jpeg
1543214247
/teams-storage-test-bucket/test.jpg


So the headers it should (to my mind) be checking for are correct vs what's being sent. The generated URL works for GET (I can download the empty file), but not for the PUT. Is there a solution?










share|improve this question















I'm uploading a dummy file to Google Cloud Storage, and then signing a read/write URL before passing it to another service hosted separately. Unfortunately, I'm receiving a 403 response, with the message:




The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.




The code I'm using to create the dummy object and sign the 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));


Some test code I've written for the sake of this question (I've also tried 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();


When proxied through Fiddler, the request looks like this:



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}


And the response is:



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>


Looking at Google's UrlSigner code, the lines listed above are:



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


I found suggestion in this question that the Content-Type header does need to be set, so I made the following changes:



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


but this hasn't fixed things. The new "StringToSign" value reflects the change:



PUT

image/jpeg
1543214247
/teams-storage-test-bucket/test.jpg


So the headers it should (to my mind) be checking for are correct vs what's being sent. The generated URL works for GET (I can download the empty file), but not for the PUT. Is there a solution?







c# google-cloud-platform google-cloud-storage pre-signed-url






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 at 4:09

























asked Nov 22 at 1:24









John

10.4k31734




10.4k31734












  • For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
    – John Hanley
    Nov 22 at 4:01










  • @John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
    – John
    Nov 22 at 4:08












  • You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
    – John Hanley
    Nov 22 at 4:22












  • @John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
    – John
    Nov 22 at 4:25




















  • For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
    – John Hanley
    Nov 22 at 4:01










  • @John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
    – John
    Nov 22 at 4:08












  • You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
    – John Hanley
    Nov 22 at 4:22












  • @John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
    – John
    Nov 22 at 4:25


















For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
– John Hanley
Nov 22 at 4:01




For your line that is loading the credentials are you loading the entire json file base64 encoded or just part of it: const string BASE64_JSON_CREDS=. To create credentials correctly you need both the client_email and private_key which are included in the credentials json file.
– John Hanley
Nov 22 at 4:01












@John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
– John
Nov 22 at 4:08






@John The entire file as received from Google. The same credentials are allowing me to upload files, download files, and delete files via the ServiceClient and, also allowing me create resumable uploads (via REST API because I need an Angular web front-end to perform the upload).
– John
Nov 22 at 4:08














You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
– John Hanley
Nov 22 at 4:22






You need to specify the HTTP method and headers in your calll tourlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });
– John Hanley
Nov 22 at 4:22














@John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
– John
Nov 22 at 4:25






@John Can you add that as an answer so that I can accept my humiliation and give you the upvotes you deserve? :-) I naively assumed (not looking at the overloads carefully enough) that specifying the scope was the equivalent of specifying the verb for S3, etc.
– John
Nov 22 at 4:25














1 Answer
1






active

oldest

votes

















up vote
3
down vote



accepted










In your call to Sign() include the HTTP Method and Content-Type headers:



urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });





share|improve this answer





















  • I feel like an idiot now. Thank you for your assistance.
    – John
    Nov 22 at 4:28










  • @John - There are so many APIs, signing methods, credentials, ...
    – John Hanley
    Nov 22 at 4:29











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53422663%2ferror-with-signed-writable-url-the-request-signature-we-calculated-does-not-mat%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
3
down vote



accepted










In your call to Sign() include the HTTP Method and Content-Type headers:



urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });





share|improve this answer





















  • I feel like an idiot now. Thank you for your assistance.
    – John
    Nov 22 at 4:28










  • @John - There are so many APIs, signing methods, credentials, ...
    – John Hanley
    Nov 22 at 4:29















up vote
3
down vote



accepted










In your call to Sign() include the HTTP Method and Content-Type headers:



urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });





share|improve this answer





















  • I feel like an idiot now. Thank you for your assistance.
    – John
    Nov 22 at 4:28










  • @John - There are so many APIs, signing methods, credentials, ...
    – John Hanley
    Nov 22 at 4:29













up vote
3
down vote



accepted







up vote
3
down vote



accepted






In your call to Sign() include the HTTP Method and Content-Type headers:



urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });





share|improve this answer












In your call to Sign() include the HTTP Method and Content-Type headers:



urlSigner.Sign(bucket.Name, FILENAME, TimeSpan.FromHours(100), HttpMethod.Put, contentHeaders: new Dictionary<string, IEnumerable<string>> { { "Content-Type", new { "image/jpeg" } } });






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 22 at 4:27









John Hanley

11.3k2527




11.3k2527












  • I feel like an idiot now. Thank you for your assistance.
    – John
    Nov 22 at 4:28










  • @John - There are so many APIs, signing methods, credentials, ...
    – John Hanley
    Nov 22 at 4:29


















  • I feel like an idiot now. Thank you for your assistance.
    – John
    Nov 22 at 4:28










  • @John - There are so many APIs, signing methods, credentials, ...
    – John Hanley
    Nov 22 at 4:29
















I feel like an idiot now. Thank you for your assistance.
– John
Nov 22 at 4:28




I feel like an idiot now. Thank you for your assistance.
– John
Nov 22 at 4:28












@John - There are so many APIs, signing methods, credentials, ...
– John Hanley
Nov 22 at 4:29




@John - There are so many APIs, signing methods, credentials, ...
– John Hanley
Nov 22 at 4:29


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53422663%2ferror-with-signed-writable-url-the-request-signature-we-calculated-does-not-mat%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Catalogne

Violoncelliste

Héron pourpré