Как создать цифровую подпись, используя SHA256 с алгоритмом ECDSA в C#

У меня есть требование создать подпись, которая представляет собой подпись сообщения SHA256+ECDSA в кодировке URL-Safe-Base-64. Это будет использоваться для использования удаленной службы REST.

Мне дали реализацию браузера HAL, которая подключается к ним, как и ожидалось, и тестовую реализацию, выполненную в SCALA.

    val token = generateToken() // Generates a random numeric token, different for each request
    val lines = line1 + "\n" + line2 + "\n" + line3 + "\n"
    val lineBytes = lines.getBytes()
    try {
        var sig = Signature.getInstance("SHA256withECDSA")
        sig.initSign(privateKey)
        sig.update(lineBytes)
        body.foreach { input => // If there is a body, sign it too
            input.reset()
            var bytes = new Array[Byte](1024)
            while (input.available() > 0) {
                val alloc = input.read(bytes)
                sig.update(bytes, 0, alloc)
            }
        }
        val encoder = new Base64(true)
        val sigString = encoder.encodeAsString(sig.sign()).replace("\r\n", "")
        val headerVal = "authentication.scheme.signed" + " username=" + username + "&token=" + token + "&signature=" + sigString

        request.addHeader("Authorization", headerVal)
    } catch {
        case e : NoSuchAlgorithmException =>
            throw new Error("No support for SHA256withECDSA! Check your Java installation.")
    }

Я пытаюсь создать ту же подпись, используя С#.

Пока так выглядит мой метод подписи

private byte[] SignData(byte[] hashedMessageToSign)
{
    CngKey pkey2 = CngKey.Open(@"C:\OpenSSL-Win64\bin\MyPrivateiKeyInPkcs8Format.pem");

    using (ECDsaCng dsa = new ECDsaCng(pkey2))
    {
        //dsa.HashAlgorithm = CngAlgorithm.ECDsaP256;
        //bob.key = dsa.Key.Export(CngKeyBlobFormat.EccPublicBlob);

        byte[] data = hashedMessageToSign;

        return dsa.SignData(data);
    }
}

Я получаю сборку кода, но создаю недопустимую подпись. Вот метод вызова

        protected void btnLDiscover_Click(object sender, EventArgs e)
{
    HttpWebRequest request = WebRequest.Create("https://service.provider/path/") as HttpWebRequest;
    request.Method = "GET";
    request.ContentType = "application/bespoke.format+json; version=1";
    //request.Date = new DateTime(2015, 9, 3, 10, 40, 48);
    request.Date = new DateTime(2015, 9, 21, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
    request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
    request.Accept = "application/bespoke.format+json; version=1";
    request.KeepAlive = true;
    request.MaximumAutomaticRedirections = 99;
    //request.PreAuthenticate = true;

    string token = DateTime.Now.Ticks.ToString();
    string messageToSign = "GET /path/\n1\n" + token + "\n";

    string signatureString = Convert.ToBase64String(SignData(Encoding.ASCII.GetBytes(messageToSign)));
    //signatureString = RemoveControlCharacters(signatureString);
    //signatureString = HttpUtility.UrlEncode(signatureString);
    signatureString = signatureString
                        .Replace('+', '-')
                        .Replace('/', '_')
                        .Replace("=", string.Empty);

    request.Headers.Add("Authorization", "authentication.shceme.signed username=someuser&token=" + token + "&signature=" + signatureString);

    HttpWebResponse response = request.GetResponse() as HttpWebResponse;

    Encoding enc = System.Text.Encoding.GetEncoding(65001);
    StreamReader loResponseStream =
    new StreamReader(response.GetResponseStream(), enc);

    string responseString = loResponseStream.ReadToEnd();

    loResponseStream.Close();
    response.Close();

    resultTextBox.Text = responseString;
}

person Roland    schedule 21.09.2015    source источник


Ответы (2)


Если вы не возражаете, я пропущу часть, где вы выполняете кодировку base 64 в одном фрагменте кода, но не в другом.

В отличие от подписей RSA PKCS#1 v1.5, подписи ECDSA не являются детерминированными. Другими словами, они зависят от генератора случайных чисел для генерации подписей. Подписи будут иметь другое значение после каждой операции подписи. Правильность значения этих подписей можно проверить только путем проверки с помощью открытого ключа.

person Maarten Bodewes    schedule 21.09.2015
comment
Спасибо за ваше время и вклад, Маартен. Я добавил в вызывающий метод, который я использовал, где выполняется кодирование. Я уже сделал сертификат x509, который я отправил поставщику услуг - это то, что они используют для проверки моей подписи. - person Roland; 23.09.2015

На всякий случай, если у кого-то возникнет аналогичная проблема... Оказалось, что Microsoft кодирует приведенную выше подпись немного иначе, чем это делает java. Извините, если я использую неправильную терминологию, так как я некоторое время отсутствовал в этом пространстве. По сути, нам пришлось попробовать разные типы подписи, и мы нашли тот, который декодируется до одного и того же значения как в приложении C#, так и в приложении Java.

person Roland    schedule 30.11.2016
comment
Привет, Зак, я сейчас не могу вспомнить, но проверю свои записи того времени. - person Roland; 29.05.2017