Аутентификация REST API (OAuth 1.0) с использованием проблемы C# с добавлением параметров строки запроса
Я пытаюсь добавить параметры строки запроса в запрос API GET, который использует oauth 1.0. Мне удалось получить ответ в этом посте для работы без параметров строки запроса: аутентификация REST API (OAuth 1.0) с использованием C#
Проблема, с которой я столкнулся, заключается в том, что я пытаюсь добавить параметры строки запроса в запрос GET, не указав их в URL-адресе, поскольку это не соответствует требованиям и приведет к ошибке:
SIGNATURE_INVALID: неверная подпись - сообщение - код BAD_OAUTH_REQUEST:401
Пожалуйста, кто-нибудь может предложить несколько советов о том, как это сделать, поскольку я полностью застрял в том, где и как их следует добавить.
const string consumerKey = "REMOVED";
const string consumerSecret = "REMOVED";
const string tokenSecret = "REMOVED";
const string tokenValue = "REMOVED";
const string url = "https://api.bricklink.com/api/store/v1/orders";
//FULL URL WITH QS PARAMS IS: https://api.bricklink.com/api/store/v1/orders?direction=in
string Escape(string s)
{
// https://stackru.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
var charsToEscape = new[] { "!", "*", "'", "(", ")" };
var escaped = new StringBuilder(Uri.EscapeDataString(s));
foreach (var t in charsToEscape)
{
escaped.Replace(t, Uri.HexEscape(t[0]));
}
return escaped.ToString();
}
protected void Button1_Click(object sender, EventArgs e)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.Method = "GET";
var timeStamp = ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(timeStamp));
var signatureBaseString = Escape(httpWebRequest.Method.ToUpper()) + "&";
signatureBaseString += EscapeUriDataStringRfc3986(url.ToLower()) + "&";
signatureBaseString += EscapeUriDataStringRfc3986(
"oauth_consumer_key=" + EscapeUriDataStringRfc3986(consumerKey) + "&" +
"oauth_nonce=" + EscapeUriDataStringRfc3986(nonce) + "&" +
"oauth_signature_method=" + EscapeUriDataStringRfc3986("HMAC-SHA1") + "&" +
"oauth_timestamp=" + EscapeUriDataStringRfc3986(timeStamp) + "&" +
"oauth_token=" + EscapeUriDataStringRfc3986(tokenValue) + "&" +
"oauth_version=" + EscapeUriDataStringRfc3986("1.0")
//ATTEMPT AT ADDING QS PARAM
//"direction=" + SimpleQuote("out")
);
lblResult.Text=(@"signatureBaseString: " + signatureBaseString+ "<br/><br/>");
var key = EscapeUriDataStringRfc3986(consumerSecret) + "&" + EscapeUriDataStringRfc3986(tokenSecret);
lblResult.Text+=(@"key: " + key);
var signatureEncoding = new ASCIIEncoding();
var keyBytes = signatureEncoding.GetBytes(key);
var signatureBaseBytes = signatureEncoding.GetBytes(signatureBaseString);
string signatureString;
using (var hmacsha1 = new HMACSHA1(keyBytes))
{
var hashBytes = hmacsha1.ComputeHash(signatureBaseBytes);
signatureString = Convert.ToBase64String(hashBytes);
}
signatureString = EscapeUriDataStringRfc3986(signatureString);
lblResult.Text+=(@"signatureString: " + signatureString + "<br/><br/>");
var header =
"OAuth realm=" + SimpleQuote("") + "," +
"oauth_consumer_key=" + SimpleQuote(consumerKey) + "," +
"oauth_nonce=" + SimpleQuote(nonce) + "," +
"oauth_signature_method=" + SimpleQuote("HMAC-SHA1") + "," +
"oauth_timestamp=" + SimpleQuote(timeStamp) + "," +
"oauth_token=" + SimpleQuote(tokenValue) + "," +
"oauth_version=" + SimpleQuote("1.0") + "," +
"oauth_signature= " + SimpleQuote(signatureString);
//ATTEMPT MADE HERE TOO
lblResult.Text+=(@"header: " + header + "<br/><br/>");
httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, header);
var response = httpWebRequest.GetResponse();
var characterSet = ((HttpWebResponse)response).CharacterSet;
var responseEncoding = characterSet == ""
? Encoding.UTF8
: Encoding.GetEncoding(characterSet ?? "utf-8");
var responsestream = response.GetResponseStream();
if (responsestream == null)
{
throw new ArgumentNullException(nameof(characterSet));
}
using (responsestream)
{
var reader = new StreamReader(responsestream, responseEncoding);
var result = reader.ReadToEnd();
lblResult.Text+=(@"result: " + result);
}
}
string SimpleQuote(string s) => '"' + s + '"';
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
/// <summary>
/// Escapes a string according to the URI data string rules given in RFC 3986.
/// </summary>
/// <param name="value">The value to escape.</param>
/// <returns>The escaped value.</returns>
/// <remarks>
/// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
/// RFC 3986 behavior if certain elements are present in a .config file. Even if this
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
/// host actually having this configuration element present.
/// </remarks>
internal static string EscapeUriDataStringRfc3986(string value)
{
// Start with RFC 2396 escaping by calling the .NET method to do the work.
// This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
// If it does, the escaping we do that follows it will be a no-op since the
// characters we search for to replace can't possibly exist in the string.
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
// Upgrade the escaping to RFC 3986, if necessary.
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
// Return the fully-RFC3986-escaped string.
return escaped.ToString();
}