字节读取为 UTF8 字符串并转换为 Base64

本文关键字:串并 转换 字符串 Base64 字符 读取 UTF8 字节 | 更新日期: 2023-09-27 17:57:08

请原谅这里的冗长设置,但我认为了解上下文可能会有所帮助......

我正在实现自定义数字签名验证方法作为 WCF 服务的一部分。我们使用自定义方法,因为对某些行业标准有不同的解释,但细节并不那么相关。

在此特定场景中,我收到一个 MTOM/XOP 编码请求,其中根 MIME 部分包含数字签名,签名 DigestValue 和 SignatureValue 片段被拆分为单独的 MIME 部分。

包含签名 DigestValue 和 SignatureValue 数据的 MIME 部分是二进制编码的,因此它实际上是 Web 请求中的一堆原始字节,如下所示:

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 消息中,其中签名摘要值和签名值元素将还原到原始 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编码,或者它的行为与我预期的不同。

字节读取为 UTF8 字符串并转换为 Base64

任意二进制数据不能解码为 UTF8 编码字符串,然后编码回相同的二进制数据。http://en.wikipedia.org/wiki/UTF-8 中的"无效字节序列"段落指出了这一点。

我有点困惑为什么您希望将数据编码/解码为 UTF8。

好的,我采用了不同的方法来阅读 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();
}