如何在C#中实现Base64 URL安全编码?


105

我想在C#中实现Base64 URL安全编码。在Java中,我们有一个公共Codec库,该库为我提供了一个URL安全编码的字符串。如何使用C#实现相同目的?

byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes("StringToEncode");
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);

上面的代码将其转换为Base64,但填充了==。有没有办法实现URL安全编码?


您不能仅Url.Encode在string中使用BASE64吗?

c#中存在哪个名称空间Url类?
Vishvesh Phadnis 2014年

看一下:msdn.microsoft.com/en-us/library/…您需要引用System.Web程序集。

它将=转换为%3D。我不想要那个。
Vishvesh Phadnis 2014年

3
那是什么意思url safe呢?%3D是网址安全的。

Answers:


182

通常,只需交换字母以用于url中,这样就不必进行%编码;只有65个字符的3是有问题- +/=。最常见的替换是-到位的+,并_代替/。至于填充:只需将其删除=)即可;您可以推断出所需的填充量。在另一端:只需逆转该过程即可:

string returnValue = System.Convert.ToBase64String(toEncodeAsBytes)
        .TrimEnd(padding).Replace('+', '-').Replace('/', '_');

与:

static readonly char[] padding = { '=' };

并反转:

string incoming = returnValue
    .Replace('_', '/').Replace('-', '+');
switch(returnValue.Length % 4) {
    case 2: incoming += "=="; break;
    case 3: incoming += "="; break;
}
byte[] bytes = Convert.FromBase64String(incoming);
string originalText = Encoding.ASCII.GetString(bytes);

一个有趣的问题,却是:这是相同的做法,即“共同编解码器库”用途?测试当然是一件很合理的事情-这是一种非常普遍的方法。


1
在通用编解码器中,他们将[0-9a-zA-Z_-]字符用于网址安全模式。
Vishvesh Phadnis 2014年

在URL应用程序下的Base64 Wiki页面中也提到了这一点。en.wikipedia.org/wiki/Base64
翁斯特(Wonster)2015年

2
您还具有此功能' stackoverflow.com/questions/1886686/… ',它将为您完成所有艰苦的工作。
toy4fun

1
为什么我们不需要:情况1:传入+ =“ ===”; 打破; ?
alex da franca,

5
@alexdafranca,因为它将永远不会具有1的长度mod4。3x8位变成4x6位(并且每个6位是所选字母中64个字符之一),0x8位被编码为0x6位而没有填充,1x8位被编码为带==填充的2x6位,带填充的2x8编码为3x6,不带=填充的3x8编码为4x6,然后对齐以便重复。没有任何东西编码为1x6位,因此您永远不需要===填充。
乔治·希拉尔

83

您可以使用Base64UrlEncoder命名空间中的类Microsoft.IdentityModel.Tokens

const string StringToEncode = "He=llo+Wo/rld";

var encodedStr = Base64UrlEncoder.Encode(StringToEncode);
var decodedStr = Base64UrlEncoder.Decode(encodedStr);

if (decodedStr == StringToEncode)
    Console.WriteLine("It works!");
else
    Console.WriteLine("Dangit!");

1
这比公认的答案要干净得多。有什么缺点吗?
taktak004

18
只需注意:Microsoft.IdentityModel.Tokens是必须下载的NuGet程序包。
Uber Schnoz

我假设这不是跨平台的。是这样吗
布兰登S.19年


8

基于此处的答案并进行了一些性能改进,我们使用GitHub上提供的源代码(由MIT许可)向NuGet发布了一个非常易于使用的url安全的base64实现

使用就像

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);

太好了,谢谢。出于兴趣,为什么选择了"string".Replaceencode方法,但是用手动循环代替了decode
ᴍᴀᴛᴛʙᴀᴋᴇʀ

@ᴍᴀᴛᴛʙᴀᴋᴇʀ我需要重新访问它并运行一些基准测试,但这是因为我们添加了后者,因此它由可扩展的字符列表而不是不可变的字符串表示。
Mahmoud Al-Qudsi '18

另一个带有返回类型= string的类:github.com/vndevpro/architecture-common/blob/master/…–
hazjack

8

要获得类似于URL安全的类似于base64的编码,而不是根据RFC4648获得“ base64url”,请使用System.Web.HttpServerUtility.UrlTokenEncode(bytes)编码和 System.Web.HttpServerUtility.UrlTokenDecode(bytes)解码。


6
根据RFC4648,这没有提供符合标准的URL安全Base64编码。另请参阅此问答请谨慎使用。
Artjom B.

1

最简单的解决方案:(无填充)

private static string Base64UrlEncode(string input) {
    var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
    // Special "url-safe" base64 encode.
    return Convert.ToBase64String(inputBytes)
      .Replace('+', '-') // replace URL unsafe characters with safe ones
      .Replace('/', '_') // replace URL unsafe characters with safe ones
      .Replace("=", ""); // no padding
  }

归功于Tholle


0

这是另一种解码Marc安全编码的URL安全base64的方法。我只是不明白为什么4-length%4起作用(确实如此)。

如下所示,只有原点的位长是6和8的公倍数,base64不会在结果后附加“ =”。

1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8 
1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6
                "=="            "="

因此,我们可以相反地进行,如果结果的位长度不能被8整除,则将其附加:

base64String = base64String.Replace("-", "+").Replace("_", "/");
var base64 = Encoding.ASCII.GetBytes(base64String);
var padding = base64.Length * 3 % 4;//(base64.Length*6 % 8)/2
if (padding != 0)
{
    base64String = base64String.PadRight(base64String.Length + padding, '=');
}
return Convert.FromBase64String(base64String);

0

在UWP中使用Microsoft加密引擎。

uint length = 32;

IBuffer buffer = CryptographicBuffer.GenerateRandom(length);
string base64Str = CryptographicBuffer.EncodeToBase64String(buffer)
                   // ensure url safe
                   .TrimEnd('=').Replace('+', '-').Replace('/', '_');

return base64Str;

0

Karanvir Kang的答案很好,我投了赞成票。但是,它的确在字符串的末尾保留了一个奇数字符(指示已删除的填充字符数)。这是我的解决方案。

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.