ASP.NET框架下解码/编码修改后的base64 URL的代码

124

我想要对数据进行Base64编码以便将其放入URL中,并在我的HttpHandler中解码。

我发现Base64编码允许使用'/'字符,这会破坏我的UriTemplate匹配。然后我发现维基百科有一个名为"修改版Base64用于URL"的概念:

存在一种修改版Base64用于URL的变体,不会使用填充符'=‘,而是将标准Base64的'+'和'/'字符分别替换为'-'和'_',从而不再需要使用URL编码器/解码器,对编码值的长度没有影响,仍然可以在关系型数据库、Web表单和一般对象标识符中使用相同的编码形式。

使用.NET,我想修改我的当前代码,从执行基本的Base64编码和解码转为使用"修改版Base64用于URL"方法。有人做过这个吗?

对于解码部分,我知道它的开头类似于:

string base64EncodedText = base64UrlEncodedText.Replace('-', '+').Replace('_', '/');

// Append '=' char(s) if necessary - how best to do this?

// My normal base64 decoding now uses encodedText

但是,我可能需要在末尾添加一个或两个 '=' 字符,这看起来有点复杂。

我的编码逻辑应该更简单:

// Perform normal base64 encoding
byte[] encodedBytes = Encoding.UTF8.GetBytes(unencodedText);
string base64EncodedText = Convert.ToBase64String(encodedBytes);

// Apply URL variant
string base64UrlEncodedText = base64EncodedText.Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');

我看到过Guid to Base64 for URL的StackOverflow帖子,但那个方法有一个已知的长度,因此他们可以在结尾处硬编码等号的数量。

5个回答

181

同时还需检查具有UrlTokenEncode和UrlTokenDecode方法的HttpServerUtility类,该类可处理URL安全的Base64编码和解码。

注1:结果不是一个合法的Base64字符串。一些对URL不安全的字符被替换了。

注2:结果与RFC4648中的base64url算法不同,它将'='填充替换为'0'、'1'或'2',具体取决于它替换了多少个等号来使值对查询参数安全。

///<summary>
/// Base 64 Encoding with URL and Filename Safe Alphabet using UTF-8 character set.
///</summary>
///<param name="str">The origianl string</param>
///<returns>The Base64 encoded string</returns>
public static string Base64ForUrlEncode(string str)
{
    byte[] encbuff = Encoding.UTF8.GetBytes(str);
    return HttpServerUtility.UrlTokenEncode(encbuff);
}
///<summary>
/// Decode Base64 encoded string with URL and Filename Safe Alphabet using UTF-8.
///</summary>
///<param name="str">Base64 code</param>
///<returns>The decoded string.</returns>
public static string Base64ForUrlDecode(string str)
{
    byte[] decbuff = HttpServerUtility.UrlTokenDecode(str);
    return Encoding.UTF8.GetString(decbuff);
}

这样做不会对每个 / + 和 = 使用%编码吗? 这不如其他答案高效。 - JoelFan
不,它会用数字替换在结尾处用作填充的等号,并将加号和斜杠替换为减号和下划线。 - Fredrik Haglund
16
请注意,UrlTokenEncode不是严格意义上的base64url。它会将'='填充替换为'0'、'1'或'2',具体取决于替换了多少个等号。 - ladenedge

84

这个应该可以正确填充:

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');

4
会不会加起来变成三个等于号?看起来只会有0、1或2个等于号。 - Kirk Liemohn
1
@Kirk:如果它添加了3个字符,那么base64字符串已经损坏了。我想验证字符串是个好主意,它应该只包含预期的字符和长度%4!= 3。 - AnthonyWJones
在这个例子中,base64是什么?如果它是一个System.String,那我不确定这会如何工作,因为最终结果取决于用于获取字节的编码方式。在这个特定的例子中,你可能可以通过使用UTF8来解决问题,但是对于Unicode来说,这种方法行不通。 - Ben Collins
@Ben:我的填充代码出现在原始字符串已转换为字节并且这些字节已转换为Base64形式之后。这由输入变量称为“base64”和问题的性质所示。实际上,这个问题归结为“如何使用特定字符填充字符串,使其字符长度成为指定整数的倍数”。恰好在这种情况下,上下文使用“=”来填充基于64的字符串以使其成为4的倍数。 - AnthonyWJones
3
“@AnthonyWJones说:‘它应该只包含预期的字符,且长度 % 4 != 1’,是吗?” - blueling
显示剩余5条评论

40
评论所需的积分不够,但如果有帮助的话,Sushil在提供的链接RFC 7515 JSON Web Signature中找到的代码片段可以用于将Base 64编码作为URL参数进行编码。
以下是复制的代码片段,方便那些懒得自己写的人:
    static string Base64UrlEncode(byte[] arg)
    {
        string s = Convert.ToBase64String(arg); // Regular base64 encoder
        s = s.Split('=')[0]; // Remove any trailing '='s
        s = s.Replace('+', '-'); // 62nd char of encoding
        s = s.Replace('/', '_'); // 63rd char of encoding
        return s;
    }

    static byte[] Base64UrlDecode(string arg)
    {
        string s = arg;
        s = s.Replace('-', '+'); // 62nd char of encoding
        s = s.Replace('_', '/'); // 63rd char of encoding
        switch (s.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: s += "=="; break; // Two pad chars
            case 3: s += "="; break; // One pad char
            default: throw new System.Exception(
              "Illegal base64url string!");
        }
        return Convert.FromBase64String(s); // Standard base64 decoder
    }

这是与 Xamarin 兼容的代码,不使用 System.Web。 - Reza Mortazavi
正是我所需要的!对于 Xamarin 而言,这是一个非常好的选择,而且不需要引入任何库。 - Sleeping_Giant

21

在查找用于进行base64url编码/解码的代码时,我来到了这里,这种编码与问题中解释的base64略有不同。

在这份文档中发现了c#代码片段。JSON Web Signature ietf draft


2
这是我在解析GMail API v1中的消息(Message.Raw)时找到的唯一有效解决方案。 - HeyZiko

6
与已接受的答案相比,以下是使用C#基本解码base64编码url的方法:

解码:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);

也许如果您提供更多细节并将其与已接受的答案进行比较,您可能会获得一次赞同 - 谢谢。 - Mauricio Gracia Gutierrez
这不是一个base64编码的URL,而是base64url编码的数据。 - TimTIM Wong

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