Comment réaliser un encodage sécurisé d'URL Base64 en C #?

105

Je souhaite obtenir un encodage sécurisé d'URL Base64 en C #. En Java, nous avons la Codecbibliothèque commune qui me donne une chaîne codée URL sécurisée. Comment puis-je réaliser la même chose en utilisant C #?

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

Le code ci-dessus le convertit en Base64, mais il complète ==. Existe-t-il un moyen d'obtenir un encodage sécurisé des URL?

Vishvesh Phadnis
la source
Ne pouvez-vous pas simplement utiliser une Url.Encodechaîne BASE64?
Dans quelle classe d'URL d'espace de noms est-elle présente dans c #?
Vishvesh Phadnis
Jetez un œil: msdn.microsoft.com/en-us/library/… Vous devez référencer l' System.Webassembly.
C'est la conversion = en% 3D. Je ne veux pas ça.
Vishvesh Phadnis
3
Alors qu'est-ce que tu veux dire url safe? %3Dest une url sûre.

Réponses:

182

Il est courant d' échanger simplement l'alphabet pour une utilisation dans les URL, de sorte qu'aucun% -encoding n'est nécessaire; seuls 3 des 65 caractères sont problématiques - +, /et =. les remplacements les plus courants sont -à la place +et _à la place de /. Quant au rembourrage: il suffit de le retirer (le =); vous pouvez déduire la quantité de rembourrage nécessaire. À l'autre extrémité: inversez simplement le processus:

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

avec:

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

et inverser:

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);

La question intéressante, cependant, est: est-ce la même approche que la "bibliothèque de codecs communs" utilise? Ce serait certainement une première chose raisonnable à tester - c'est une approche assez courante.

Marc Gravell
la source
1
Dans Common Codec, ils utilisent le caractère [0-9a-zA-Z_-] pour le mode sans échec de l'URL.
Vishvesh Phadnis
ceci est également mentionné dans la page wiki pour Base64 sous les applications URL. en.wikipedia.org/wiki/Base64
wonster
2
Vous disposez également de cette fonction « stackoverflow.com/questions/1886686/… » qui fait tout le travail pour vous.
toy4fun
1
Pourquoi n'avons-nous pas besoin de: cas 1: entrant + = "==="; Pause; ?
alex da franca
5
@alexdafranca car il n'aura jamais une longueur mod 4 de 1. 3x8 bits deviennent 4x6 bits (et chaque 6 bits est l'un des 64 caractères de l'alphabet choisi), 0x8 bits est codé comme 0x6 bits sans remplissage, 1x8 bits est codé comme 2x6 bits avec ==padding, 2x8 est encodé en 3x6 avec =padding, 3x8 est encodé en 4x6 sans padding, puis il est aligné pour qu'il se répète. Rien n'est jamais encodé en 1x6 bits, vous n'avez donc jamais besoin de ===remplissage.
George Helyar
83

Vous pouvez utiliser la classe Base64UrlEncoderde l'espace de noms 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!");
Jérôme
la source
1
C'est beaucoup plus propre que la réponse acceptée. Un inconvénient?
taktak004
18
Juste une note: Microsoft.IdentityModel.Tokens est un package NuGet qui doit être téléchargé.
Uber Schnoz
Je suppose par son nom que ce n'est pas multiplateforme. Est-ce le cas?
Brandon S.
8

Sur la base des réponses ici avec quelques améliorations de performances, nous avons publié une implémentation base64 sécurisée pour les URL très facile à utiliser dans NuGet avec le code source disponible sur GitHub (licence MIT).

L'utilisation est aussi simple que

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);
Mahmoud Al-Qudsi
la source
Merci incroyable. Par intérêt, pourquoi avez-vous opté pour "string".Replacela encodeméthode, mais une boucle avec manuel remplace le decode?
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
@ ᴍᴀᴛᴛʙᴀᴋᴇʀ J'ai besoin de le revoir et d'exécuter quelques benchmarks, mais c'est parce que nous ajoutons à ce dernier afin qu'il soit représenté par une liste de caractères extensible au lieu d'une chaîne immuable.
Mahmoud Al-Qudsi
Une autre classe avec return type = string à la place: github.com/vndevpro/architecture-common/blob/master/…
hazjack
8

Pour obtenir un encodage de type base64 sécurisé pour les URL, mais pas "base64url" selon RFC4648, utilisez System.Web.HttpServerUtility.UrlTokenEncode(bytes)pour encoder et System.Web.HttpServerUtility.UrlTokenDecode(bytes)pour décoder.

Karanvir Kang
la source
6
Cela ne fournit pas un codage Base64 sécurisé pour les URL conforme aux normes selon RFC4648. Voir également ce Q&R . Utiliser avec précaution.
Artjom B.
1

Solution la plus simple: (sans rembourrage)

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
  }

Le mérite revient à: Tholle

Ashi
la source
0

Voici une autre méthode pour décoder une url-safe base64 qui a été encodée de la même manière avec Marc. Je ne comprends juste pas pourquoi4-length%4 travaillé (c'est le cas).

Comme suit, seules les longueurs en bits de l'origine sont des multiples communs de 6 et 8, base64 n'ajoute pas "=" au résultat.

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
                "=="            "="

Nous pouvons donc le faire à l'inverse, si la longueur en bits du résultat ne peut pas être divisible par 8, elle a été ajoutée:

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);
joyjy
la source
0

Utilisation du moteur cryptographique Microsoft dans UWP.

uint length = 32;

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

return base64Str;
visc
la source
0

La réponse de Karanvir Kang est bonne et j'ai voté pour. Cependant, il laisse un caractère impair à la fin de la chaîne (indiquant le nombre de caractères de remplissage supprimés). Voici ma solution.

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
Joshcodes
la source