在bash/openssl中移植签名函数,它使用RSA私钥文件(PEM)将签名函数移植到C#

在bash/openssl中移植签名函数,它使用RSA私钥文件(PEM)将签名函数移植到C#,c#,bash,openssl,chef-infra,rsa,C#,Bash,Openssl,Chef Infra,Rsa,我正在尝试编写一些C代码,允许我向Chef服务器发出请求。接下来,我尝试在我的C#代码中获得签名的规范头,以便我可以向服务器进行身份验证并发出请求 我发现下面的脚本(请参见“cURL”下)与我的客户机证书一起使用。调用此脚本将从服务器返回预期的JSON响应。用于对规范标头进行签名的脚本部分为: method="GET" hashed_path=$(echo -n "/organizations/[org]/nodes" | openssl dgst -sha1 -binary | openssl

我正在尝试编写一些C代码,允许我向Chef服务器发出请求。接下来,我尝试在我的C#代码中获得签名的规范头,以便我可以向服务器进行身份验证并发出请求

我发现下面的脚本(请参见“cURL”下)与我的客户机证书一起使用。调用此脚本将从服务器返回预期的JSON响应。用于对规范标头进行签名的脚本部分为:

method="GET"
hashed_path=$(echo -n "/organizations/[org]/nodes" | openssl dgst -sha1 -binary | openssl enc -base64) #where [org] is my organization
hashed_body=$(echo -n "" | openssl dgst -sha1 -binary | openssl enc -base64)
timestamp=$(date -u "+%Y-%m-%dT%H:%M:%SZ")

canonical_request="Method:$method\nHashed Path:$hashed_path\nX-Ops-Content-Hash:$hashed_body\nX-Ops-Timestamp:$timestamp\nX-Ops-UserId:$client_name"

auth_headers=$(printf "$canonical_request" | openssl rsautl -sign -inkey \
"$(_chef_dir)/${client_name}.pem" | openssl enc -base64 | _chomp |  awk '{ll=int(length/60);i=0; \
while (i<=ll) {printf " -H X-Ops-Authorization-%s:%s", i+1, substr($0,i*60+1,60);i=i+1}}')
我已经通过调试器和大量的echo语句验证了bash脚本中的规范头与C#code中的规范头完全相同。然而,我完全停留在签约部分。我从GitHub项目中提取的代码没有变化,但生成的Base64字符串与我从bash脚本中获得的不同(时间戳已移除)

我如何在C#中以与openssl工具相同的方式对某些内容进行签名?我曾尝试将“NullDigest”转换为其他类型,但没有找到一个能产生与openssl二进制文件相同的Base64结果的类型。如果.Net框架中已经有了更好的方法,我也愿意使用更好的方法来实现这一点。

我需要使用PKCS#1v1.5,而不是像@oliv建议的那样直接使用RSA操作。我的C#方法中的代码变成:

private string Sign(string privateKey)
{
    var method = _method.ToString().ToUpperInvariant();
    var hashedPath = _requestUri.AbsolutePath.ToBase64EncodedSha1String();
    var contentHash = _body.ToBase64EncodedSha1String();
    var canonicalHeader = $"Method:{method}\nHashed Path:{hashedPath}\nX-Ops-Content-Hash:{contentHash}\nX-Ops-Timestamp:{_timestamp}\nX-Ops-UserId:{_client}";
    var input = Encoding.UTF8.GetBytes(canonicalHeader);

    using (var stringReader = new StringReader(privateKey))
    {
        var pemReader = new PemReader(stringReader);
        var keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject();

        var pkcs1Encoding = new Pkcs1Encoding(new RsaBlindedEngine());
        pkcs1Encoding.Init(true, keyPair.Private);
        var signature = pkcs1Encoding.ProcessBlock(input, 0, input.Length);

        return Convert.ToBase64String(signature);
    }
}

我不希望在使用私钥进行签名之前对哈希进行base64编码。我猜ToBase64EncodedSha1String是错误的,需要进行更改,以便将sha1哈希二进制数据直接提供给RSA操作。我还将查看填充。请注意,
openssl
默认情况下使用PKCS#1v1.5进行RSA签名。由于您直接使用RSA操作,可能需要在RSA操作之前生成填充。@oliv我已确认
ToBase64EncodedSha1String
生成的结果与命令
echo-n”“| openssl dgst-sha1-binary | openssl enc-base64
相同。我知道在签名部分之前,一切都符合chef API的期望。我会看更多的填充物,这是我没看过的yetYes!那是填充物。我将用我的发现添加一个答案。非常感谢。
private string Sign(string privateKey)
{
    var method = _method.ToString().ToUpperInvariant();
    var hashedPath = _requestUri.AbsolutePath.ToBase64EncodedSha1String();
    var contentHash = _body.ToBase64EncodedSha1String();
    var canonicalHeader = $"Method:{method}\nHashed Path:{hashedPath}\nX-Ops-Content-Hash:{contentHash}\nX-Ops-Timestamp:{_timestamp}\nX-Ops-UserId:{_client}";
    var input = Encoding.UTF8.GetBytes(canonicalHeader);

    using (var stringReader = new StringReader(privateKey))
    {
        var pemReader = new PemReader(stringReader);
        var keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject();

        var pkcs1Encoding = new Pkcs1Encoding(new RsaBlindedEngine());
        pkcs1Encoding.Init(true, keyPair.Private);
        var signature = pkcs1Encoding.ProcessBlock(input, 0, input.Length);

        return Convert.ToBase64String(signature);
    }
}