代码之家  ›  专栏  ›  技术社区  ›  E. Moffat

将使用rsa私钥文件(pem)的bash/openssl中的签名函数移植到c#

  •  0
  • E. Moffat  · 技术社区  · 8 年前

    我正在尝试编写一些C代码,使我能够向厨师服务器发出请求。跟随 documentation for the chef server API ,我正在尝试在我的c代码中签名规范头,以便可以向服务器进行身份验证并发出请求。

    我找到了下面的剧本 in the chef docs (请参见“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}}')
    

    以上代码片段的重要部分是 $(printf "$canonical_request" | openssl rsautl -sign -inkey "$(_chef_dir)/${client_name}.pem" | openssl enc -base64 .

    我有一些C代码 dotnet-chef-api on GitHub 我想让他们做同样的事。它使用 BouncyCastle crypto libraries 签署内容。

    private string Sign(string privateKey)
    {
        var method = _method.ToString().ToUpperInvariant(); //evals to 'GET'
        var hashedPath = _requestUri.AbsolutePath.ToBase64EncodedSha1String(); //evals to hash of '/organizations/[org]/nodes'
        var contentHash = _body.ToBase64EncodedSha1String(); //evals to hash of empty string
    
        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 key = ((AsymmetricCipherKeyPair) pemReader.ReadObject()).Private;
    
            var signer = new RsaDigestSigner(new NullDigest());
            signer.Init(true, key);
            signer.BlockUpdate(input, 0, input.Length);
    
            return Convert.ToBase64String(signer.GenerateSignature());
        }
    }
    

    我已经通过调试器和许多 echo 声明bash脚本中的规范头与c代码中的规范头完全相同。不过,我完全被签下了。我从github项目中提取的代码没有改变,但是生成的base64字符串与从bash脚本中获取的字符串不同(去掉了时间戳)。

    我如何在c中以与使用openssl工具签名相同的方式对某些内容进行签名?我尝试过将“nulldigest”切换为其他类型,但没有找到与openssl二进制文件产生相同base64结果的类型。如果已经存在于.NET框架中,我也愿意提供更好的方法来实现这一点。

    1 回复  |  直到 8 年前
        1
  •  0
  •   E. Moffat    8 年前

    我需要使用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);
        }
    }