Перенос функции подписи в bash / openssl, которая использует файл закрытого ключа RSA (PEM), на C # - PullRequest
0 голосов
/ 08 июня 2018

Я пытаюсь написать код на C #, который позволит мне отправлять запросы на сервер Chef.Следуя документации для API сервера 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}}')

Важной частью приведенного выше фрагмента является $(printf "$canonical_request" | openssl rsautl -sign -inkey "$(_chef_dir)/${client_name}.pem" | openssl enc -base64.

У меня есть некоторый код на C #адаптированный из dotnet-chef-api на GitHub , который я пытаюсь заставить делать то же самое.Он использует криптографические библиотеки BouncyCastle для подписи содержимого.

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 для других типов, но не нашел тот, который дает тот же результат Base64, что и двоичные файлы openssl.Я также открыт для лучших способов сделать это, если они уже существуют в рамках .Net Framework.

1 Ответ

0 голосов
/ 08 июня 2018

Мне нужно было использовать PKCS # 1v1.5 вместо прямой операции RSA, как это было предложено @oliv.Код в моем методе 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);
    }
}
...