出于各种原因,我需要能够在 C# 中发出 Chef API 请求。我遵循了指南 here ( header 规范)和 here (Bash 例子)但是已经走到了死胡同。我发送的每个请求都返回 401 Unauthorized with the content
{"error":["Invalid signature for user or client 'myuser'"]}
我还在本地配置 Knife 以使用 Fiddler 作为 HTTP 代理,这样我就可以检查 Knife HTTP 请求并尽可能准确地复制可见 header ,但自然我看不到它生成的规范 header ,也看不到服务器期望的 header .
但是,我已经通过这种方式确认我为内容(空)和路径生成的哈希值与 Knife 生成的哈希值相同。
这是我的代码。从 PEM 格式加载 RSA 私钥是使用从 Christian Etter 的博客中获取的扩展方法 - 抱歉,链接用完了。
const string path = "/cookbooks"
const string basePath = "https://chefserver.internal:443";
var timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
var method = "GET";
var clientName = "myuser";
var hashedPath = ToBase64EncodedSha1String(path);
var hashedBody = ToBase64EncodedSha1String(String.Empty);
var canonicalHeader = String.Format("Method:{0}\nHashed Path:{1}\nX-Ops-Content-Hash:{2}\nX-Ops-Timestamp:{3}\nX-Ops-UserId:{4}",
method, hashedPath, hashedBody, timestamp, clientName);
var privateKey = File.ReadAllText("C:\\chef\\myuser.private.pem");
string signature;
byte[] rawData;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.LoadPrivateKeyPEM(privateKey);
using (var sha1 = new SHA1CryptoServiceProvider())
{
rawData = rsa.SignData(Encoding.UTF8.GetBytes(canonicalHeader), sha1);
signature = Convert.ToBase64String(rawData);
}
}
var client = new HttpClient();
var message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(basePath + path);
message.Headers.Add("Accept", "application/json");
message.Headers.Add("Host", "chefserver.internal:443");
message.Headers.Add("X-Chef-Version", "11.12.4");
message.Headers.Add("X-Ops-Timestamp", timestamp);
message.Headers.Add("X-Ops-Sign", "version=1.0;");
message.Headers.Add("X-Ops-Userid", clientName);
message.Headers.Add("X-Ops-Content-Hash", hashedBody);
message.Headers.Add("User-Agent", "Chef Knife/11.4.0 (ruby-1.9.2-p320; ohai-6.16.0; x86_64-darwin11.3.0; +http://opscode.com)");
var currentItem = new StringBuilder();
var i = 1;
foreach (var l in signature)
{
currentItem.Append(l);
if (currentItem.Length == 60)
{
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
i++;
currentItem = new StringBuilder();
}
}
message.Headers.Add("X-Ops-Authorization-" + i, currentItem.ToString());
var response = await client.SendAsync(message);
var content = await response.Content.ReadAsStringAsync();
还有 helper
private string ToBase64EncodedSha1String(string input)
{
return
Convert.ToBase64String(new SHA1CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(input)));
}
最佳答案
我遇到了类似的问题,发现 .NET 加密库不支持完成这项工作所需的内容。我最终使用 BouncyCaSTLe 加密库来完成此任务。
我制作了一个简单的 C# 类库来包装它。在这里看看:https://github.com/mattberther/dotnet-chef-api
关于c# - 手工制作的 Chef API 请求 - 获取 "invalid signature for user",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24241696/