Wie erreicht man eine sichere Base64-URL-Codierung in C #?

103

Ich möchte eine sichere Base64-URL-Codierung in C # erreichen. In Java haben wir die gemeinsame CodecBibliothek, die mir eine URL-sicher codierte Zeichenfolge gibt. Wie kann ich mit C # dasselbe erreichen?

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

Der obige Code konvertiert es in Base64, aber es wird aufgefüllt ==. Gibt es eine Möglichkeit, eine URL-sichere Codierung zu erreichen?

Vishvesh Phadnis
quelle
Kannst du nicht einfach Url.Encodeauf String in verwenden BASE64?
In welchem ​​Namespace ist die Url-Klasse in c # vorhanden?
Vishvesh Phadnis
Schauen Sie sich das an: msdn.microsoft.com/en-us/library/… Sie müssen auf die System.WebAssembly verweisen .
Es konvertiert = in% 3D. Das will ich nicht.
Vishvesh Phadnis
3
Also, was meinst du damit url safe? %3Dist url sicher.

Antworten:

180

Es ist üblich, das Alphabet einfach gegen URLs auszutauschen, sodass keine% -Codierung erforderlich ist. nur 3 der 65 Zeichen sind problematisch - +, /und =. Die häufigsten Ersetzungen sind -anstelle von +und _anstelle von /. Was die Polsterung betrifft: entfernen Sie sie einfach (die =); Sie können den erforderlichen Polsterungsgrad ableiten . Am anderen Ende: einfach den Vorgang umkehren:

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

mit:

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

und umzukehren:

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

Die interessante Frage ist jedoch: Ist dies der gleiche Ansatz, den die "Common Codec Library" verwendet? Es wäre sicherlich eine vernünftige erste Sache zu testen - dies ist ein ziemlich häufiger Ansatz.

Marc Gravell
quelle
1
Im Common Codec verwenden sie das Zeichen [0-9a-zA-Z_-] für den abgesicherten URL-Modus.
Vishvesh Phadnis
Dies wird auch auf der Wiki-Seite für Base64 unter URL-Anwendungen erwähnt. en.wikipedia.org/wiki/Base64
Wonster
2
Sie haben auch diese Funktion ' stackoverflow.com/questions/1886686/… ', die die ganze harte Arbeit für Sie erledigt.
toy4fun
1
Warum brauchen wir nicht: Fall 1: eingehend + = "==="; brechen; ?
Alex da Franca
5
@alexdafranca, da es niemals eine Länge mod 4 von 1 haben wird. 3x8 Bits werden zu 4x6 Bits (und jedes 6 Bit ist eines von 64 Zeichen im gewählten Alphabet), 0x8 Bits werden als 0x6 Bits ohne Auffüllen codiert, 1x8 Bits werden als codiert 2x6 Bits mit ==Auffüllung, 2x8 wird als 3x6 mit =Auffüllung codiert, 3x8 wird als 4x6 ohne Auffüllung codiert und dann so ausgerichtet, dass es sich wiederholt. Nichts wird jemals auf 1x6 Bit codiert, sodass Sie niemals eine ===Auffüllung benötigen .
George Helyar
83

Sie können eine Klasse Base64UrlEncoderaus dem Namespace verwenden 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!");
Hieronymus
quelle
1
Dies ist viel sauberer als die akzeptierte Antwort. Irgendwelche Nachteile?
taktak004
18
Nur ein Hinweis: Microsoft.IdentityModel.Tokens ist ein NuGet-Paket, das heruntergeladen werden muss.
Uber Schnoz
Ich gehe beim Namen davon aus, dass dies nicht plattformübergreifend ist. Ist das der Fall?
Brandon S.
8

Basierend auf den Antworten hier mit einigen Leistungsverbesserungen haben wir eine sehr einfach zu verwendende url-sichere base64-Implementierung für NuGet mit dem auf GitHub verfügbaren Quellcode (MIT-lizenziert) veröffentlicht.

Die Bedienung ist so einfach wie

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);
Mahmoud Al-Qudsi
quelle
Erstaunlich danke. Aus Interesse, warum haben Sie sich für "string".Replacedie encodeMethode entschieden, aber eine Schleife mit manuellen ersetzt die decode?
3 ʙᴀᴋᴇʀ
@ ᴍᴀᴛᴛʙᴀᴋᴇʀ Ich muss es erneut besuchen und einige Benchmarks ausführen, aber das liegt daran, dass wir letzteres hinzufügen, damit es durch eine erweiterbare Liste von Zeichen anstelle einer unveränderlichen Zeichenfolge dargestellt wird.
Mahmoud Al-Qudsi
Eine andere Klasse mit Rückgabetyp = Zeichenfolge stattdessen: github.com/vndevpro/architecture-common/blob/master/…
Hazjack
8

Verwenden Sie System.Web.HttpServerUtility.UrlTokenEncode(bytes)zum Codieren und System.Web.HttpServerUtility.UrlTokenDecode(bytes)Decodieren eine URL-sichere base64-ähnliche Codierung, jedoch nicht "base64url" gemäß RFC4648 .

Karanvir Kang
quelle
6
Dies bietet keine standardkonforme URL-sichere Base64-Codierung gemäß RFC4648. Siehe auch diese Fragen und Antworten . Mit Vorsicht verwenden.
Artjom B.
1

Einfachste Lösung: (ohne Polsterung)

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
  }

Gutschrift geht an: Tholle

Ashi
quelle
0

Hier ist eine andere Methode zum Dekodieren einer URL-sicheren Base64, die auf die gleiche Weise mit Marc codiert wurde. Ich verstehe einfach nicht warum4-length%4 funktioniert hat (es funktioniert).

Wie folgt ist nur die Bitlänge des Ursprungs ein gemeinsames Vielfaches von 6 und 8. Base64 hängt nicht "=" an das Ergebnis an.

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

Umgekehrt können wir es also tun: Wenn die Bitlänge des Ergebnisses nicht durch 8 teilbar ist, wurde Folgendes angehängt:

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
quelle
0

Verwenden der kryptografischen Microsoft-Engine in UWP.

uint length = 32;

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

return base64Str;
visc
quelle
0

Karanvir Kangs Antwort ist gut und ich habe dafür gestimmt. Am Ende der Zeichenfolge bleibt jedoch ein ungerades Zeichen (das die Anzahl der entfernten Füllzeichen angibt). Hier ist meine Lösung.

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