Байты читаются как строка 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.