Как создать хеш-значение SHA-256 для тела сообщения XML

Я пытаюсь создать файл XML, который читается человеком, но может быть проверен как неизмененный.

Подход, который я использую, заключается в следующем:

  • Создайте тело сообщения xml (применена канонизация (C14N))
  • Создайте хеш-значение для тела сообщения (SHA-256).
  • Зашифровать хеш-значение (AES-256)
  • Сгенерируйте заголовок сообщения, используя зашифрованный хеш

Я сталкиваюсь с проблемами на каждом этапе этой задачи, но текущая проблема заключается в создании хэш-значения из тела XML.

Я получаю исключение "Метод ReadContentAsBase64 не поддерживается в этом XmlReader. Используйте свойство CanReadBinaryContent, чтобы узнать, реализует ли его средство чтения". но я не знаю, как еще реализовать чтение XElement.

Пример xml ниже

<?xml version="1.0" encoding="UTF-8"?>
<Application>
    <MessageHeader>
        <MessageID>00000001</MessageID>
        <MessageCheck>
            dHPHxMJGgDCHtFttgPROo24yi+R1RGx6Ahno2r0nV7zrcgR2+BX4f+RmNCVCsT5g
        </MessageCheck>
    </MessageHeader>
    <MessageBody>
        <Receipt>
            <Status>OK</Status>
            <FormReference>E00000000001</FormReference>
        </Receipt>
    </MessageBody>
</Application>

И вот код, который я пытался заставить работать безрезультатно:

/// <summary>
/// Convert the message body into a Hash value
/// </summary>
/// <param name="MessageBody">XElement holding all the message body XML nodes</param>
/// <returns>a base 64 string representing the hash code</returns>
private string GenerateMessageBodyHash(XElement MessageBody)
{
    string hash = string.Empty;
    try
    {
        // Convert the XElement into a stream of data
        using (XmlReader xr = MessageBody.CreateReader())
        {
            // Now that we have a reader, lets read the data into a byte array
            List<byte> dataList = new List<byte>();

            byte[] buffer = new byte[1000];
            int fileIndex = 0;
            int bytesRead = 0;

            while ((bytesRead = xr.ReadContentAsBase64(buffer, fileIndex, buffer.Length)) != 0 )
            {
                // Update the position into the file
                fileIndex += bytesRead;

                //add the data into the list
                dataList.AddRange(buffer);

                // reset the buffer
                buffer = new byte[1000];
            }

            SHA256 shaM = new SHA256Managed();
            hash = Convert.ToBase64String( shaM.ComputeHash( dataList.ToArray() ) );
        }
    }
    catch (Exception ex)
    {
        // TODO: Add some logging in here
    }

    return hash;
}

person TeamWild    schedule 16.09.2011    source источник
comment
Разве удобочитаемый XML не является оксюмороном?   -  person NullUserException    schedule 16.09.2011
comment
Хороший момент, если только он не красиво отформатирован или вы наполовину машина. ;о)   -  person TeamWild    schedule 16.09.2011


Ответы (2)


Вот как я реализовал решение.

/// <summary>
/// Convert the message body into a Hash value
/// </summary>
/// <param name="MessageBody">XElement holding all the message body XML nodes</param>
/// <returns>a base 64 string representing the hash code</returns>
private string GenerateMessageBodyHash(XElement MessageBody)
{
    string hash = string.Empty;
    try
    {
        using( MemoryStream ms = new MemoryStream() )
        {
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.OmitXmlDeclaration = true;
            xws.Indent = false;

            using( XmlWriter xw = XmlWriter.Create( ms, xws ))
            {
                // Assign the xml to be written to the writer and then memory stream
                MessageBody.WriteTo(xw);

                SHA256 shaM = new SHA256Managed();
                hash = Convert.ToBase64String(shaM.ComputeHash( ms ));
            }
        }
    }
    catch (Exception ex)
    {
        Log.WriteLine(Category.Warning, "Exception detected generating the XML hash", ex);
    }

    return hash;
}
person TeamWild    schedule 19.09.2011
comment
Вы уверены, что это работает? Я сделал тест с точно таким же кодом, и оказалось, что каждый раз я получаю один и тот же хэш. У меня сложилось впечатление, что это связано с тем, что 1) вы не закрываете XmlWriter(=flush) перед получением хэша. 2) Поскольку MemoryStream не находится в позиции 0. (См. msdn.microsoft.com/en-us/library/xa627k19(v=vs.110).aspx ). Возможно, ваша проверка хэша бесполезна, будьте осторожны в своем коде - person J4N; 22.04.2015
comment
В вашем коде нужны еще две строки после MessageBody.WriteTo(xw): xw.Flush(); ms.Seek(0, SeekOrigin.Begin); - person lsalamon; 05.06.2015