Как добиться безопасного кодирования URL-адресов Base64 в C #?

108

Я хочу добиться безопасного кодирования URL-адресов Base64 на C #. В Java у нас есть общая Codecбиблиотека, которая дает мне строку с безопасным кодированием URL. Как я могу добиться того же с помощью C #?

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

Приведенный выше код преобразует его в Base64, но он дополняет ==. Есть ли способ добиться безопасного кодирования URL?

Вишвеш Паднис
источник
1
Разве вы не можете просто использовать Url.Encodeструну BASE64?
В каком пространстве имен класс url присутствует в c #?
Вишвеш Фаднис
Взгляните: msdn.microsoft.com/en-us/library/… Вам нужно ссылаться на System.Webсборку.
Он конвертирует = в% 3D. Я не хочу этого.
Вишвеш Паднис
3
Так что вы имеете в виду url safe? %3Dбезопасен по URL.

Ответы:

185

Обычно просто заменяют алфавит местами для использования в URL-адресах, поэтому нет необходимости в% -кодировании; только 3 из 65 символов являются проблемными - +, /и =. наиболее распространенные замены - -на место +и _вместо /. Что касается прокладки: просто удалите ее (the =); вы можете сделать вывод о количестве необходимых отступов. С другой стороны: просто переверните процесс:

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
В Common Codec они используют символ [0-9a-zA-Z_-] для безопасного режима URL.
Вишвеш Фаднис
это также упоминается на вики-странице для Base64 в разделе приложений URL. en.wikipedia.org/wiki/Base64
wonster
2
У вас также есть функция stackoverflow.com/questions/1886686/… , которая выполняет всю тяжелую работу за вас.
toy4fun
1
Почему нам не нужны: case 1: incoming + = "==="; перерыв; ?
Алекс да
5
@alexdafranca, потому что у него никогда не будет длины mod 4, равной 1. 3x8 бит становятся 4x6 битами (и каждые 6 битов являются одним из 64 символов в выбранном алфавите), 0x8 бит кодируется как 0x6 бит без заполнения, 1x8 бит кодируется как 2x6 бит с ==заполнением, 2x8 кодируется как 3x6 с =заполнением, 3x8 кодируется как 4x6 без заполнения, а затем выравнивается, поэтому он повторяется. Ничего не кодируется в бит 1x6, поэтому ===заполнение не требуется .
George Helyar
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 06
19
Просто примечание: Microsoft.IdentityModel.Tokens - это пакет NuGet, который необходимо загрузить.
Uber Schnoz
По названию я предполагаю, что это не кроссплатформенный. Так ли это?
Брэндон С.
8

Основываясь на приведенных здесь ответах с некоторыми улучшениями производительности, мы опубликовали очень простую в использовании URL-безопасную реализацию base64 для NuGet с исходным кодом, доступным на GitHub (с лицензией MIT).

Использование так же просто, как

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);
Махмуд Аль-Кудси
источник
Прекрасное спасибо. Из интереса почему вы выбрали для "string".Replaceдля encodeметода, но петли с ручным Заменяет для decode?
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ 03
@ ᴍᴀᴛᴛʙᴀᴋᴇʀ Мне нужно вернуться к нему и запустить несколько тестов, но это потому, что мы добавляем к последнему, чтобы он был представлен растущим списком символов вместо неизменяемой строки.
Махмуд Аль-Кудси
Другой класс с возвращаемым типом вместо строки: github.com/vndevpro/architecture-common/blob/master/…
hazjack
8

Чтобы получить безопасную для URL кодировку, подобную base64, но не «base64url» согласно RFC4648, используйте System.Web.HttpServerUtility.UrlTokenEncode(bytes)для кодирования и System.Web.HttpServerUtility.UrlTokenDecode(bytes)декодирования.

Каранвир Канг
источник
6
Это не обеспечивает совместимую со стандартами безопасную для URL-адресов кодировку Base64 согласно RFC4648. См. Также эти вопросы и ответы . Используйте с осторожностью.
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
  }

Кредит принадлежит: Толле

Аши
источник
0

Вот еще один метод декодирования URL-адреса base64, который был закодирован таким же образом с Marc. Я просто не понимаю, почему 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

Использование криптографического ядра Microsoft в UWP.

uint length = 32;

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

return base64Str;
вязкий
источник
0

Ответ Каранвира Канга хороший, и я проголосовал за него. Однако в конце строки остается нечетный символ (указывающий на количество удаленных символов заполнения). Вот мое решение.

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
Joshcodes
источник