手工制作的 Chef API 请求 - 获取"invalid signature for user"



出于各种原因,我需要能够在c#中制作Chef API请求。我遵循了这里的指南(Header规范)和这里的指南(Bash示例),但已经进入了死胡同。我发送的每个请求返回401 Unauthorized,内容为

{"error":["Invalid signature for user or client 'myuser'"]}

我还在本地配置了Knife,使用Fiddler作为HTTP代理,这样我就可以检查Knife的HTTP请求,并尽可能准确地复制了可见的标头,但自然我看不到它生成的规范标头,也看不到服务器期望的标头。

但是,我已经以这种方式确认了我为内容(空)和路径生成的哈希值与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();

和助手

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#加密不是很熟悉,但是SHA1CryptoServiceProvider的文档似乎表明它正在签署SHA1哈希。Chef不使用签名哈希,实际上你需要对canonicalHeader本身进行RSA签名。

SignData(Byte[], Object): Computes the hash value of the specified byte array using the specified hash algorithm, and signs the resulting hash value.

是否存在可以使用的空散列或传递散列?

最新更新