Байты читаются как строка UTF8 и конвертируются в Base64.

Простите за длинную настройку здесь, но я подумал, что это может помочь иметь контекст...

Я реализую пользовательский метод проверки цифровой подписи в рамках службы WCF. Мы используем нестандартный метод, потому что различные интерпретации некоторых отраслевых стандартов, но детали там не так уж актуальны.

В этом конкретном сценарии я получаю запрос в кодировке MTOM/XOP, где корневая часть MIME содержит цифровую подпись, а фрагменты подписи DigestValue и SignatureValue разделены на отдельные части MIME.

Части MIME, которые содержат данные подписи DigestValue и SignatureValue, имеют двоичное кодирование, поэтому в веб-запросе это буквально набор необработанных байтов, например:

Content-Id: <c18605af-18ec-4fcb-bec7-e3767ef6fe53@example.jaxws.sun.com>
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

[non-printable-binary-data-goes-here]
--uuid:eda4d7f2-4647-4632-8ecb-5ba44f1a076d

Я читаю содержимое сообщения в виде строки (используя кодировку UTF8 по умолчанию) следующим образом (см. Параметр requestAsString ниже):

MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);
try
{
    using (MemoryStream mstream = new MemoryStream())
    {
        buffer.WriteMessage(mstream);
        mstream.Position = 0;

        using (StreamReader sr = new StreamReader(mstream))
        {
            requestAsString = sr.ReadToEnd();
        }

        request = buffer.CreateMessage();
    }
}

После прочтения сообщения MTOM/XOP я пытаюсь реорганизовать несколько частей MIME в одно сообщение SOAP, где элементы подписи DigestValue и SignatureValue восстанавливаются в исходный конверт SOAP (а не в виде вложений). Так что в основном я беру декодирование запроса MTOM/XOP.

К сожалению, у меня возникают проблемы с правильным чтением фрагментов DigestValue и SignatureValue. Мне нужно прочитать байты из сообщения и получить строковое представление base64 этих данных.

Несмотря на весь контекст выше, кажется, что основная проблема заключается в чтении двоичных данных в виде строки (в кодировке UTF8), а затем преобразовании их в правильное представление base64.

Вот что я вижу в своем тестовом коде:

Это мой пример строки base64:

string base64String = "mowXMw68eLSv9J1W7f43MvNgCrc=";

Затем я могу получить байтовое представление этой строки. Это дает массив из 20 байтов:

byte[] base64Bytes = Convert.FromBase64String(base64String);

Затем я получаю версию этих байтов в кодировке UTF8:

string decodedString = UTF8Encoding.UTF8.GetString(base64Bytes);

Теперь странная часть... если я преобразую строку обратно в байты следующим образом, я получу массив байтов длиной 39 байтов:

byte[] base64BytesBack = UTF8Encoding.UTF8.GetBytes(decodedString);

Очевидно, что в этот момент, когда я преобразую обратно в строку base64, она не соответствует исходному значению:

string base64StringBack = Convert.ToBase64String(base64BytesBack);

Для base64StringBack установлено значение "77+977+9FzMO77+9eO+/ve+/ve+/vVbvv73vv703Mu+/vWAK77+9"

Что я здесь не так делаю? Если я переключаюсь на использование UTF8Encoding.Unicode.GetString() и UTF8Encoding.Unicode.GetBytes(), он работает как положено:

string base64String = "mowXMw68eLSv9J1W7f43MvNgCrc=";

// First get an array of bytes from the base64 string
byte[] base64Bytes = Convert.FromBase64String(base64String);

// Get the Unicode representation of the base64 bytes.
string decodedString = UTF8Encoding.Unicode.GetString(base64Bytes);

byte[] base64BytesBack = UTF8Encoding.Unicode.GetBytes(decodedString);

string base64StringBack = Convert.ToBase64String(base64BytesBack);

Теперь для base64StringBack установлено значение "mowXMw68eLSv9J1W7f43MvNgCrc=", так что, похоже, я каким-то образом неправильно использую кодировку UTF8 или ведет себя иначе, чем я ожидал.

2 ответа

Решение

Хорошо, я принял другой подход к чтению сообщения MTOM/XOP:

Вместо того чтобы полагаться на свой собственный код для анализа MIME-компонентов вручную, я просто использовал XmlDictionaryReader.CreateMtomReader(), чтобы получить XmlDictionaryReader и прочитать сообщение в XmlDocument (стараясь сохранить пробелы в XmlDocument, чтобы цифровые подписи не нарушались):

MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);

messageContentType = WebOperationContext.Current.IncomingRequest.ContentType;

try
{
    using (MemoryStream mstream = new MemoryStream())
    {
        buffer.WriteMessage(mstream);
        mstream.Position = 0;

        if (messageContentType.Contains("multipart/related;"))
        {
            Encoding[] encodings = new Encoding[1];
            encodings[0] = Encoding.UTF8;

            // MTOM
            using (XmlDictionaryReader reader = XmlDictionaryReader.CreateMtomReader(mstream, encodings, messageContentType, XmlDictionaryReaderQuotas.Max))
            {
                XmlDocument msgDoc = new XmlDocument();
                msgDoc.PreserveWhitespace = true;
                msgDoc.Load(reader);

                requestAsString = msgDoc.OuterXml;

                reader.Close();
            }
        }
        else
        {
            // Text
            using (StreamReader sr = new StreamReader(mstream))
            {
                requestAsString = sr.ReadToEnd();
            }
        }

        request = buffer.CreateMessage();
    }
}
finally
{
    buffer.Close();
}

Произвольные двоичные данные не могут быть декодированы в строку в кодировке UTF8, а затем закодированы обратно в те же двоичные данные. На это указывает параграф "Недопустимые последовательности байтов" в http://en.wikipedia.org/wiki/UTF-8.

Я немного сбит с толку относительно того, почему вы хотите, чтобы данные были закодированы / декодированы как UTF8.

Другие вопросы по тегам