C#字节数组转换成URL友好的字符串

28
我正在为一个简单的网站制作快速验证码生成器,希望能够在页面的URL中传递加密密钥。我可以将其作为查询字符串参数轻松处理,但是我不想这样做(因为没有其他内容使用查询字符串)......
我的加密代码生成一个byte[],然后使用Convert.ToBase64String(byte[])转换成一个字符串。然而,这个字符串仍然不太适合URL,因为它可能包含'/'和'='等字符。有没有人知道在.NET框架中有更好的函数将字节数组转换为适合URL的字符串?
我知道System.Web.HttpUtility.UrlEncode()及其等效方法,但它们只在查询字符串参数中正常工作。如果我在路径中对'='进行编码,我的Web服务器会返回400 Bad Request错误。
总之,这不是致命问题,但希望有人能给我一个好的解决方案。
**编辑:为了确保自己完全明白正在处理的字符串,我觉得应该提供更多信息。
从我的加密算法产生的byte[]应该通过某种算法转换成适合URL的字符串。之后,它成为XElement的内容,然后用作href属性的一部分,作为XSLT变换的源文档。我不认为xslt变换会导致问题,因为出现在路径上的内容似乎是一个编码的查询字符串参数,但引起HTTP 400错误。
我还尝试过对base64字符串使用HttpUtility.UrlPathEncode(),但似乎也没有解决问题(我的URL中仍然有'/')。**
4个回答

89
你需要寻找 System.Web 中的 HttpServerUtility.UrlTokenEncodeHttpServerUtility.UrlTokenDecode 来进行编码和解码,使用的是基于 base64 的编码方式,将潜在危险的 '+' 和 '/' 字符替换成 '-' 和 '_'。详细信息请参考MSDN文档

1
+1:Base64编码比仅使用BitConverter.ToString中的十六进制数字更紧凑。 - LukeH
2
运行得非常好...那个鬼畜(鲜为人知)的函数真是太棒了...谢谢啊 - LorenVS
太棒了!我解决了一个问题,就是在 URL 传输时使用 Convert.ToBase64String 和 Convert.FromBase64String 函数的问题。 - Ales Potocnik Hahonina
3
微软把这个放在 System.Web 组件中真是太恼人了。唉。 - Asbjørn Ulsberg
2
请谨慎使用 UrlTokenEncode,因为它会在末尾添加一个数字,该数字表示已删除的等号的数量。在此处查看可能的解决方案 [链接](https://codeday.me/en/qa/20190316/14910.html)。 - Yair Zamir
显示剩余2条评论

4

对于 ASP.NET Core 6.0+,请使用 Microsoft.AspNetCore.WebUtilities.WebEncoders

byte[] bytes = RandomNumberGenerator.GetBytes(64);

string encoded = WebEncoders.Base64UrlEncode(bytes);
byte[] decoded = WebEncoders.Base64UrlDecode(encoded);

1
这是正确的解决方案,请勿使用其他解码器。 - Vaibhav.Inspired

0

HttpServerUtility.UrlTokenEncode和类似的功能在.NET 6中已经不再可用于.NET Core。

以下方法应根据快速阅读提供符合RFC4648§5标准的Base64字符串。

此代码在堆上至少分配了四个对象,因此对于超高性能用例应进行调整,并且可以等待.NET 7,该版本预计将引入一种方法以获取随机字节到Span中以进行堆栈分配。

private string GetUrlSafeRandomBytes(int byteCount)
{
    var salt = new byte[byteCount];
    using var rng = RandomNumberGenerator.Create();
    rng.GetBytes(salt);
    var asBase64 = Convert.ToBase64String(salt);
    var asUrlSafeString = asBase64.Replace('+', '-').Replace('/', '_');
    return asUrlSafeString;
}

0

如果有反转此操作的函数,我愿意使用十六进制编码。 - LorenVS
1
参考资料,BitConverter.GetBytes()没有重载可以接受字符串。 - LorenVS
找到了这个网址作为参考 https://dev59.com/PHM_5IYBdhLWcg3wyWWt 将使用这个解决方案。 - LorenVS
1
你需要记住,这种方式传输二进制数据非常低效。相比于base64的130%,数据会增长300%。 - Sedat Kapanoglu
@ssg - 实际上它的大小增长了一倍,而不是四倍(每个字节2个字符)。 - orip
1
@orip:没错。我想到的是带连字符的原始形式。 - Sedat Kapanoglu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接