كل مشروع .NET عملت عليه احتاج في نهاية المطاف إلى فك تشفير Base64 — سحب سلاسل الاتصال من أسرار Kubernetes، وقراءة حمولات ثنائية من webhooks، أو فحص رموز JWT أثناء جلسات التصحيح. لفك تشفير Base64 في C#، الدالة الرئيسية هي Convert.FromBase64String()، التي تُعيد byte[] تمرره بعدها إلى Encoding.UTF8.GetString() للحصول على نص مقروء. للتحقق السريع دون كتابة كود، مفكك تشفير Base64 من ToolDeck يتعامل معه فوراً في المتصفح. يغطي هذا الدليل Convert.FromBase64String()، والنسخة المبنية على Span وهي TryFromBase64String() لـ .NET 5+، وواجهة برمجة التطبيقات عالية الأداء System.Buffers.Text.Base64، واستخراج حمولة JWT، وفك تشفير الملفات واستجابات API، والبث عبر CryptoStream، وأبرز أربعة أخطاء يقع فيها مطورو C# كثيراً.
- ✓Convert.FromBase64String(s) + Encoding.UTF8.GetString(bytes) هو خط الأنابيب القياسي المكون من خطوتين — يعمل عبر جميع إصدارات .NET.
- ✓تتجنب Convert.TryFromBase64String() الاستثناءات على المدخلات غير الصالحة وتكتب في Span<byte> — مثالية للمسارات الحرجة في .NET 5+.
- ✓توفر System.Buffers.Text.Base64.DecodeFromUtf8() فك تشفير بدون تخصيص ذاكرة لمخازن بايت UTF-8 في الخدمات الحساسة للأداء.
- ✓تستخدم رموز JWT صيغة Base64url (- و_ بدلاً من + و/) — يجب تطبيع المدخل قبل استدعاء Convert.FromBase64String().
- ✓يتعامل CryptoStream مع FromBase64Transform مع فك التشفير عبر البث للملفات الكبيرة دون تحميل كل شيء في الذاكرة.
ما هو فك تشفير Base64؟
يحوّل ترميز Base64 البيانات الثنائية إلى أبجدية ASCII مكونة من 64 محرفاً حتى تتحمل النقل عبر القنوات النصية فقط — حقول JSON، رؤوس HTTP، أجسام البريد الإلكتروني، سمات XML. كل 3 بايتات من المدخل تصبح 4 محارف Base64، ولهذا يكون ناتج Base64 دائماً أكبر من الأصل بنحو 33%. فك التشفير يعكس هذا التحويل. تُخبر حشوة = في النهاية المفكِّك بعدد البايتات التي يجب قطعها من المجموعة الأخيرة. علامة = واحدة تعني أن الكتلة الأخيرة كانت تحتوي على بايتين؛ بينما == تعني بايت واحد. Base64 ليس تشفيراً — يستطيع أي شخص عكسه. الغرض منه هو النقل الآمن عبر القنوات التي تُتلف البيانات الثنائية الخام، وليس السرية.
redis://cache-prod.internal:6379/session-store
cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==
Convert.FromBase64String() — الدالة القياسية لفك التشفير
دالة Convert.FromBase64String() موجودة في .NET منذ أيام Framework 1.1. لا حزم NuGet، ولا استيرادات إضافية تتجاوز System — استدعِها فقط واحصل على byte[]. خط الأنابيب المكون من خطوتين لفك تشفير Base64 إلى سلسلة C# هو دائماً نفسه: Convert.FromBase64String() للحصول على البايتات، ثم Encoding.UTF8.GetString() لتفسير تلك البايتات كنص. المشكلة هي أن الدالة تُعيد بايتات خاماً، وليس سلسلة. عليك اختيار Encoding الصحيح لتحويل تلك البايتات إلى نص، والاختيار أهم مما يتوقعه معظم الناس. الترميز الخاطئ يُنتج أحرفاً مشوهة بصمت — بدون استثناء يُحذرك.
مثال عملي بسيط
using System; using System.Text; // سلسلة الاتصال المخزنة كـ Base64 في سر Kubernetes string encoded = "cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ=="; byte[] decodedBytes = Convert.FromBase64String(encoded); string connectionString = Encoding.UTF8.GetString(decodedBytes); Console.WriteLine(connectionString); // redis://cache-prod.internal:6379/session-store
استخدم دائماً Encoding.UTF8 إلا إذا كان لديك سبب محدد لعدم ذلك. وقت التشغيل في .NET يمثّل السلاسل داخلياً بصيغة UTF-16، لكن معظم البيانات التي تعبر حدود الأنظمة (استجابات API، ملفات الإعداد، الأسرار) مُرمَّزة بصيغة UTF-8. استخدام Encoding.ASCII على بيانات تحتوي على محارف متعددة البايت يستبدلها صمتاً بـ ? — بدون استثناء، فقط ناتج تالف.
التحقق من رحلة الذهاب والإياب
using System; using System.Text; string original = "postgres://db-admin:Kx8!mQ@db-prod.us-east-1.internal:5432/orders"; // الترميز string encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(original)); Console.WriteLine(encoded); // cG9zdGdyZXM6Ly9kYi1hZG1pbjpLeDghbVFAZGItcHJvZC51cy1lYXN0LTEuaW50ZXJuYWw6NTQzMi9vcmRlcnM= // فك التشفير byte[] decoded = Convert.FromBase64String(encoded); string recovered = Encoding.UTF8.GetString(decoded); Console.WriteLine(recovered == original); // True
اختيار الترميز المناسب
يجب أن يتطابق Encoding الذي تمرره إلى GetString() مع ما استُخدم عند الترميز الأصلي للبيانات. اختر الخاطئ وستحصل على أحرف غير مفهومة بدون استثناء — المفكك يُعيد بيانات غير مفهومة دون أي تحذير. إليك التفصيل العملي:
Encoding.UTF8— الخيار الافتراضي الآمن. يتعامل مع ASCII وجميع Unicode. استخدمه إلا إذا كنت تعرف خلاف ذلك.Encoding.ASCII— فقط لبيانات ASCII النقية ذات 7 بت. المحارف متعددة البايت تصبح?.Encoding.Unicode— هذا هو UTF-16LE في .NET. بعض السلاسل الداخلية لنظام Windows وصادرات PowerShell تستخدمه.Encoding.Latin1— ترميز أوروبي غربي قديم. يظهر في خدمات SOAP القديمة وتكاملات الحاسبات المركزية.
using System;
using System.Text;
// نفس البايتات، ترميزات مختلفة — نتائج مختلفة
byte[] decoded = Convert.FromBase64String("w7bDvMOk");
Console.WriteLine(Encoding.UTF8.GetString(decoded)); // oua (صحيح)
Console.WriteLine(Encoding.ASCII.GetString(decoded)); // ?????? (مفقود)
Console.WriteLine(Encoding.Latin1.GetString(decoded)); // öüä (تشويه)دالة مساعدة قابلة لإعادة الاستخدام مع معالجة الأخطاء
ولأن Convert.FromBase64String() تُطلق استثناء على أي مدخل خاطئ، وإزالة المسافات البيضاء أمر متكرر في كل مشروع تقريباً، أحتفظ بدالة مساعدة صغيرة في معظم المشاريع:
using System;
using System.Text;
static class Base64Helper
{
public static string? DecodeToString(string encoded)
{
if (string.IsNullOrWhiteSpace(encoded))
return null;
// إزالة المسافات البيضاء التي يرفضها مفكك .NET
string cleaned = encoded
.Replace("
", "")
.Replace("
", "")
.Replace(" ", "")
.Trim();
try
{
byte[] bytes = Convert.FromBase64String(cleaned);
return Encoding.UTF8.GetString(bytes);
}
catch (FormatException)
{
return null; // أو أطلق استثناءً خاصاً بالنطاق
}
}
}
// الاستخدام
string? decoded = Base64Helper.DecodeToString(" cmVkaXM6Ly9jYWNoZQ== \n");
Console.WriteLine(decoded); // redis://cacheConvert.FromBase64String() استثناء FormatException إذا كان المدخل يحتوي على محارف خارج أبجدية Base64 — بما في ذلك المسافات وفواصل الأسطر والمحارف الآمنة لعناوين URL مثل - و_. تتعامل الدالة المساعدة أعلاه مع المسافات البيضاء تلقائياً.فك تشفير Base64 إلى أنواع غير قياسية
المفكك يُعطيك دائماً byte[]. ما تفعله بتلك البايتات يعتمد على البيانات الأصلية. في بعض الأحيان تكون GUID مخزناً كـ 16 بايتاً خاماً، وأحياناً رسالة protobuf مُسلسَلة، وأحياناً طابع زمني بتنسيق ثنائي. إليك أكثر التحويلات التي أستخدمها.
Base64 إلى GUID
using System; // بعض APIs ترسل GUIDs كـ Base64 من 22 محرفاً بدلاً من سلاسل hex من 36 محرفاً string compactGuid = "C0HqetxMckKlZw4CssPUeQ=="; byte[] guidBytes = Convert.FromBase64String(compactGuid); Guid recovered = new Guid(guidBytes); Console.WriteLine(recovered); // 7aea41c0-4cdc-4272-a567-0e02b2c3d479
Base64 إلى كائن JSON مُحلَّل مع System.Text.Json
using System;
using System.Text;
using System.Text.Json;
// حمولة JSON مُرمَّزة بـ Base64 من قائمة رسائل
string encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5IiwicmVnaW9uIjoiZXUtd2VzdC0xIiwicmVwbGljYXMiOjR9";
byte[] jsonBytes = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(jsonBytes);
Console.WriteLine(json);
// {"service":"payment-gateway","region":"eu-west-1","replicas":4}
// إلغاء التسلسل إلى سجل
var deployEvent = JsonSerializer.Deserialize<DeployEvent>(jsonBytes);
Console.WriteLine($"{deployEvent!.Service} in {deployEvent.Region}");
// payment-gateway in eu-west-1
record DeployEvent(string Service, string Region, int Replicas);Base64 إلى سلسلة hex
using System; // تجزئة SHA-256 مخزنة كـ Base64 string hashBase64 = "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="; byte[] hashBytes = Convert.FromBase64String(hashBase64); string hex = Convert.ToHexString(hashBytes); Console.WriteLine(hex); // 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08
BinaryFormatter. لديه ثغرات معروفة لتنفيذ كود عن بُعد وأصبح متقادماً في .NET 8+. إذا كان المحتوى المُرمَّز يأتي من مصدر خارجي، حلِّله بصيغة JSON أو protobuf بدلاً من استخدام التسلسل الثنائي لـ .NET.مرجع دوال فك تشفير Base64
يوفر .NET دوالاً متعددة لفك التشفير عبر فضاءَي اسمين. تتعامل فئة Convert مع فك التشفير للأغراض العامة، بينما تستهدف System.Buffers.Text.Base64 السيناريوهات عالية الإنتاجية حيث تعمل بالفعل مع مخازن بايت UTF-8 خام.
Convert.TryFromBase64String() — فك تشفير بدون استثناءات
نمط Try قياسي في .NET للعمليات التي قد تفشل على مدخلات المستخدم. Convert.TryFromBase64String()، المتاحة منذ .NET 5، تُعيد bool بدلاً من إطلاق FormatException. تكتب البايتات المفككة في Span<byte> يوفره المُستدعي، مما يعني إمكانية استخدام ذاكرة مخصصة على المكدس للحمولات الصغيرة وتجاوز الكومة كلياً.
using System;
using System.Text;
string userInput = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=";
// تخصيص على المكدس للحمولات الصغيرة (أقل من 1KB كقاعدة آمنة)
Span<byte> buffer = stackalloc byte[256];
if (Convert.TryFromBase64String(userInput, buffer, out int bytesWritten))
{
string result = Encoding.UTF8.GetString(buffer[..bytesWritten]);
Console.WriteLine(result);
// {"host":"10.0.1.50","port":8443}
}
else
{
Console.WriteLine("Invalid Base64 input — skipping");
}يتألق هذا الأسلوب في برامج وسيطة الطلبات أو خطوط أنابيب التحقق حيث يكون المدخل الخاطئ متوقعاً لا استثنائياً. إطلاق FormatException وصيدها عند كل طلب مشوَّه يُضيف عبئاً ملموساً على نطاق واسع — تتجنب TryFromBase64String() ذلك كلياً.
التحقق من صحة مدخل Base64 دون فك التشفير
نمط شائع في وحدات تحكم API: التحقق من صحة المدخل كـ Base64 قبل تمريره للأسفل. يمكنك استخدام TryFromBase64String() كمُحقِّق عبر تخصيص مخزن مؤقت يُرمى:
using System;
static bool IsValidBase64(string input)
{
// حساب الحجم الأقصى لفك التشفير
Span<byte> buffer = stackalloc byte[((input.Length + 3) / 4) * 3];
return Convert.TryFromBase64String(input, buffer, out _);
}
// الاستخدام في وحدة تحكم API
Console.WriteLine(IsValidBase64("eyJob3N0IjoiMTAuMC4xLjUwIn0=")); // True
Console.WriteLine(IsValidBase64("not!!valid!!base64")); // False
Console.WriteLine(IsValidBase64("")); // True (الفارغ صالح)TryFromBase64String() قيمة false حتى عندما يكون المدخل صالحاً. احسب الحجم المطلوب كـ (inputLength / 4) * 3 للأمان.فك تشفير Base64 من ملف واستجابة API
قراءة ملف مُرمَّز بـ Base64 من القرص
الشهادات والكتل المشفرة وملفات تصدير البيانات تُشحن أحياناً كنص Base64. النمط المعتاد: اقرأ الملف كسلسلة، احذف أي مسافات بيضاء أو فواصل أسطر قد تسبب FormatException، فك التشفير إلى بايتات، واكتب الناتج الثنائي. انتبه لمعالجة الأخطاء — أخطاء I/O للملفات وأخطاء تنسيق Base64 يجب صيدها بشكل منفصل.
using System;
using System.IO;
string inputPath = "tls-cert.pem.b64";
string outputPath = "tls-cert.pem";
try
{
string encoded = File.ReadAllText(inputPath).Trim();
// إزالة فواصل الأسطر — مفكك .NET يرفضها
encoded = encoded.Replace("
", "").Replace("
", "");
byte[] decoded = Convert.FromBase64String(encoded);
File.WriteAllBytes(outputPath, decoded);
Console.WriteLine($"Decoded {decoded.Length} bytes -> {outputPath}");
}
catch (IOException ex)
{
Console.Error.WriteLine($"File error: {ex.Message}");
}
catch (FormatException ex)
{
Console.Error.WriteLine($"Invalid Base64: {ex.Message}");
}فك تشفير حقل Base64 من استجابة HTTP API
واجهات برمجة التطبيقات السحابية (Azure Key Vault، AWS Secrets Manager، GitHub Contents API) كثيراً ما تُعيد البيانات الثنائية كسلاسل Base64 مُضمَّنة داخل JSON. سير العمل دائماً هو نفسه: قدِّم طلب HTTP، حلِّل استجابة JSON، استخرج حقل Base64، وفك تشفيره. المثال أدناه يستخدم HttpClient وSystem.Text.Json — كلاهما مدمج في .NET 6+.
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk-prod-9f8e7d6c");
try
{
var response = await client.GetAsync("https://api.example.com/secrets/db-password");
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
// API returns: {"name":"db-password","value":"cG9zdGdyZXM6eGs5bVAycVI=","version":3}
using var doc = JsonDocument.Parse(body);
string encodedValue = doc.RootElement.GetProperty("value").GetString()!;
byte[] decoded = Convert.FromBase64String(encodedValue);
string secret = Encoding.UTF8.GetString(decoded);
Console.WriteLine($"Secret: {secret}");
// Secret: postgres:xk9mP2qR
}
catch (HttpRequestException ex)
{
Console.Error.WriteLine($"HTTP error: {ex.Message}");
}
catch (FormatException ex)
{
Console.Error.WriteLine($"Base64 decode failed: {ex.Message}");
}catch لأخطاء الشبكة وFormatException. دمجها معاً يجعل من الصعب تحديد ما إذا كانت API قد أعادت بيانات خاطئة أم فشل الطلب نفسه. في الإنتاج، سجِّل قيمة Base64 الخام (أو على الأقل طولها وأول 20 محرفاً منها) عند صيد FormatException — هذا يجعل التصحيح أسهل بكثير.فك تشفير Base64 من سطر الأوامر
لا تحتاج دائماً إلى مشروع مُترجَم. أداة dotnet-script وPowerShell كلاهما يتعاملان مع فك تشفير Base64 بأوامر أحادية السطر. للفحص السريع أثناء التصحيح، هذه الطرق أسرع من إنشاء تطبيق console.
# PowerShell (مدمج في Windows، متاح على Linux/macOS)
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0="))
# {"host":"10.0.1.50"}
# أمر base64 الأصلي في Linux / macOS
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 --decode
# {"host":"10.0.1.50"}
# macOS تستخدم -D بدلاً من --decode
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 -D
# dotnet-script (التثبيت: dotnet tool install -g dotnet-script)
echo 'Console.WriteLine(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0=")));' | dotnet-script eval
# فك التشفير وعرض JSON بشكل جميل مع jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .للصق السلاسل المُرمَّزة مباشرة في المتصفح، مفكك تشفير Base64 من ToolDeck يتعامل مع كلا الصيغتين القياسية والآمنة لعناوين URL دون أي إعداد.
البديل عالي الأداء: System.Buffers.Text.Base64
فئة System.Buffers.Text.Base64، المتاحة منذ .NET Core 2.1، تعمل على Span بايت UTF-8 خام بدلاً من سلاسل .NET. هذا يتجاوز تكلفة تحويل السلسلة إلى بايت كلياً — لا تخصيص وسيط للسلسلة، ولا خطوة ترميز UTF-16. ألجأ إليها في برمجيات ASP.NET Core الوسيطة حيث البيانات الواردة هي بالفعل ReadOnlySpan<byte> من جسم الطلب. إنشاء string فقط لتمريره إلى Convert.FromBase64String() يُضاعف التخصيصات بدون سبب. في اختبارات BenchmarkDotNet على .NET 8، المسار المبني على Span أسرع بنحو 2-3 أضعاف للحمولات أقل من 1 KB، والفجوة تتسع مع المدخلات الأكبر لأن ضغط جامع القمامة يبقى ثابتاً.
using System;
using System.Buffers.Text;
using System.Text;
// محاكاة بايت UTF-8 خام من جسم طلب HTTP
byte[] utf8Input = Encoding.UTF8.GetBytes("eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=");
byte[] output = new byte[Base64.GetMaxDecodedFromUtf8Length(utf8Input.Length)];
OperationStatus status = Base64.DecodeFromUtf8(
utf8Input, output, out int bytesConsumed, out int bytesWritten);
if (status == OperationStatus.Done)
{
string result = Encoding.UTF8.GetString(output.AsSpan(0, bytesWritten));
Console.WriteLine(result);
// {"host":"10.0.1.50","port":8443}
Console.WriteLine($"Consumed: {bytesConsumed}, Written: {bytesWritten}");
// Consumed: 44, Written: 31
}
else
{
Console.WriteLine($"Decode failed: {status}");
// Possible: InvalidData, DestinationTooSmall, NeedMoreData
}نوع الإعادة هو OperationStatus — تعداد بأربع قيم: Done، InvalidData، DestinationTooSmall، وNeedMoreData. القيمة الأخيرة مفيدة لفك التشفير الجزئي لبيانات متدفقة. للحصول على تخصيص صفري مطلق، اقرن هذا مع ArrayPool<byte>.Shared.Rent() بدلاً من new byte[].
DecodeFromUtf8InPlace — الكتابة فوق المخزن المؤقت للمدخل
using System;
using System.Buffers.Text;
using System.Text;
// يُكتب فوق المخزن المؤقت للمدخل ببايتات مفككة
byte[] data = Encoding.UTF8.GetBytes("c2VydmVyLWNvbmZpZw==");
OperationStatus status = Base64.DecodeFromUtf8InPlace(data, out int bytesWritten);
if (status == OperationStatus.Done)
{
Console.WriteLine(Encoding.UTF8.GetString(data.AsSpan(0, bytesWritten)));
// server-config
}DecodeFromUtf8InPlace المخزن المؤقت للمدخل. لا تستخدمها إذا كنت بحاجة إلى سلسلة Base64 الأصلية بعد ذلك — أول bytesWritten بايت من المصفوفة تحتوي الآن على البيانات المفككة، والبقية مهملة.شيء يجب الانتباه إليه: System.Buffers.Text.Base64 لا تتعامل مع Base64 الآمن لعناوين URL مباشرةً. إذا كان المدخل يستخدم محارف - و_ (رموز JWT مثلاً)، لا يزال يتعين عليك استبدالهما بـ + و/ قبل استدعاء هذه الدوال. واجهات برمجة التطبيقات المبنية على Span تلتزم صارماً بالأبجدية القياسية وفق RFC 4648. لا يوجد متغير DecodeFromUtf8Url — ثغرة مفاجئة بالنظر إلى مدى شيوع Base64url في واجهات برمجة التطبيقات الحديثة.
ناتج الطرفية مع تظليل بنوي للكود
توفر مكتبة Spectre.Console ناتج طرفية غني يشمل تظليل JSON بالألوان — مفيد عند بناء أدوات CLI تفك تشفير Base64 وتعرض النتيجة. ثبِّتها بـ dotnet add package Spectre.Console.
using System;
using System.Text;
using Spectre.Console;
using Spectre.Console.Json;
string encoded = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0MywibWF4Q29ubiI6MTAwfQ==";
byte[] decoded = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(decoded);
// طباعة جميلة مع تظليل بنوي في الطرفية
AnsiConsole.Write(new JsonText(json));
// ناتج JSON ملوَّن:
// {
// "host": "10.0.1.50",
// "port": 8443,
// "maxConn": 100
// }هذا مفيد بشكل خاص لأدوات CLI التي تجلب وتفك تشفير الإعدادات من الخدمات البعيدة. فكِّ تشفير إعداد Base64، حوِّله إلى JSON، واطبع الناتج المُظلَّل — كل ذلك في بضعة أسطر. يمتلك Spectre.Console أيضاً عرض جداول وأشرطة تقدم وعروض شجرية إذا كنت بحاجة إلى عرض هياكل بيانات مفككة أكثر تعقيداً في الطرفية.
بث ملفات Base64 الكبيرة مع CryptoStream
تحميل ملف Base64 بحجم 500 ميغابايت باستخدام File.ReadAllText() ثم استدعاء Convert.FromBase64String() يُخصص نحو 700 ميغابايت من الكومة: السلسلة نفسها (UTF-16، أي ضعف حجم الملف) بالإضافة إلى مصفوفة البايت المفككة. يجمع CryptoStream مع FromBase64Transform بين فك التشفير في أجزاء، مما يجعل استخدام الذاكرة ثابتاً.
using System.IO;
using System.Security.Cryptography;
string inputPath = "database-export.sql.b64";
string outputPath = "database-export.sql";
using var inputStream = new FileStream(inputPath, FileMode.Open, FileAccess.Read);
using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
using var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
base64Stream.CopyTo(outputStream);
Console.WriteLine($"Decoded {new FileInfo(outputPath).Length} bytes -> {outputPath}");يتعامل علم FromBase64TransformMode.IgnoreWhiteSpaces مع Base64 المُلفَّف على أسطر (ملفات PEM، صادرات البريد الإلكتروني) دون تنظيف يدوي. بدون هذا العلم، تسبب فواصل الأسطر في المدخل FormatException. هذا هو أقرب ما يعادل getMimeDecoder() في Java لـ .NET — يتجاهل صمتاً محارف المسافات البيضاء أثناء فك التشفير.
الاسم CryptoStream مُضلِّل — لا شيء تشفيري في Base64. وضع Microsoft فئة FromBase64Transform في فضاء الاسم System.Security.Cryptography لأنها تُنفِّذ واجهة ICryptoTransform، نفس الواجهة المستخدمة لـ AES وتحويلات الشفرات الأخرى. البث نفسه فقط يمرر البيانات عبر أي تحويل في أجزاء. فكِّر فيه كخط أنابيب تحويل بث للأغراض العامة يحدث أن يعيش في فضاء الاسم الخاطئ.
البث غير المتزامن لسيناريوهات ASP.NET Core
using System.IO;
using System.Security.Cryptography;
async Task DecodeStreamAsync(Stream inputStream, string outputPath)
{
using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
await using var outputStream = new FileStream(
outputPath, FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 81920, useAsync: true);
await base64Stream.CopyToAsync(outputStream);
}
// الاستخدام في نقطة نهاية:
// await DecodeStreamAsync(Request.Body, "uploaded-file.bin");CopyTo إلى CopyToAsync في معالجات ASP.NET Core. I/O المتزامن على خيط الطلب محظور افتراضياً في Kestrel ويطلق InvalidOperationException.كيفية فك تشفير حمولة رمز JWT بصيغة Base64 في C#
يحتوي JWT على ثلاثة مقاطع مُرمَّزة بـ Base64url مفصولة بنقاط. المقطع الأوسط هو الحمولة. يمكنك فك تشفيرها دون الحاجة إلى مكتبة JWT — قسِّم على .، طبِّع محارف Base64url، أصلح الحشوة، واستدعِ Convert.FromBase64String(). هذا يُعثِر الجميع تقريباً في المرة الأولى لأن JWT يستخدم - و_ بدلاً من + و/، ويحذف حشوة =.
using System;
using System.Text;
static string DecodeJwtPayload(string token)
{
string[] parts = token.Split('.');
if (parts.Length != 3)
throw new ArgumentException($"Invalid JWT: expected 3 segments, got {parts.Length}");
// أخذ الحمولة (المقطع الثاني)
string payload = parts[1];
// استبدال المحارف الآمنة لعناوين URL بـ Base64 القياسي
payload = payload.Replace('-', '+').Replace('_', '/');
// إضافة حشوة لتصبح مضاعفاً للأربعة
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
return Encoding.UTF8.GetString(bytes);
}
// اختبار برمز بشكل حقيقي
string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
+ ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
+ ".SIGNATURE_PLACEHOLDER";
Console.WriteLine(DecodeJwtPayload(token));
// {"sub":"usr-672","iss":"auth.example.com","exp":1741956800,"roles":["admin","billing"]}ملاحظة سريعة: هذا يقرأ الحمولة فقط. لا يتحقق من التوقيع. للتحقق من المصادقة في الإنتاج، استخدم مكتبة مناسبة مثل Microsoft.IdentityModel.JsonWebTokens. لكن للتصحيح والتسجيل وتأكيدات الاختبار، هذا الأسلوب اليدوي كافٍ تماماً.
تحليل حمولة JWT المفككة إلى كائن مُكتَّب
بمجرد حصولك على سلسلة JSON، قم بإلغاء تسلسلها مع System.Text.Json للوصول إلى المطالبات الفردية دون معالجة السلاسل:
using System;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
record JwtPayload(
[property: JsonPropertyName("sub")] string Subject,
[property: JsonPropertyName("iss")] string Issuer,
[property: JsonPropertyName("exp")] long Expiration,
[property: JsonPropertyName("roles")] string[] Roles
);
// بعد DecodeJwtPayload() من أعلاه
string json = DecodeJwtPayload(token);
var claims = JsonSerializer.Deserialize<JwtPayload>(json)!;
Console.WriteLine($"User: {claims.Subject}");
Console.WriteLine($"Issuer: {claims.Issuer}");
Console.WriteLine($"Expires: {DateTimeOffset.FromUnixTimeSeconds(claims.Expiration):u}");
Console.WriteLine($"Roles: {string.Join(", ", claims.Roles)}");
// User: usr-672
// Issuer: auth.example.com
// Expires: 2025-03-14 12:00:00Z
// Roles: admin, billingالأخطاء الشائعة
وقعت في كل واحد من هذه الأخطاء في خدمات C# على الإنتاج. الأول والثاني مسؤولان عن غالبية الأخطاء المرتبطة بـ Base64 التي أراها في مراجعات الكود. كل خطأ يبدو واضحاً بمعزل، لكن من السهل إغفاله عندما تكون منغمساً في ميزة أكبر وفك تشفير Base64 مجرد خطوة واحدة في خط الأنابيب.
المشكلة: سلاسل Base64 المقروءة من ملفات الإعداد أو متغيرات البيئة أو مدخلات المستخدم كثيراً ما تحتوي على أسطر جديدة لاحقة. Convert.FromBase64String() ترفض أي محرف خارج أبجدية Base64، بما في ذلك \r\n.
الحل: استدعِ .Trim() أو .Replace() لإزالة المسافات البيضاء قبل فك التشفير.
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
string cleaned = encoded.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);
Console.WriteLine(Encoding.UTF8.GetString(decoded));// متغير البيئة يحتوي على سطر جديد لاحق
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
byte[] decoded = Convert.FromBase64String(encoded);
// FormatException: The input is not a valid Base-64 stringالمشكلة: رموز JWT وبعض حمولات API تستخدم - و_ (الأبجدية الآمنة لعناوين URL). المفكك القياسي يقبل فقط + و/ — يطلق FormatException عند أول محرف -.
الحل: استبدل - بـ + و_ بـ / قبل فك التشفير. أصلح الحشوة أيضاً.
string payload = "eyJzdWIiOiJ1c3ItNjcyIn0";
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] decoded = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(decoded));
// {"sub":"usr-672"}// حمولة JWT — تستخدم Base64 الآمن لعناوين URL string payload = "eyJzdWIiOiJ1c3ItNjcyIn0"; byte[] decoded = Convert.FromBase64String(payload); // FormatException
المشكلة: استدعاء Encoding.UTF8.GetString() على بايتات صورة أو protobuf مفككة يُنتج هراءً. الأسوأ أن تحويل تلك السلسلة مرة أخرى إلى بايتات يُفسد البيانات صمتاً لأن تسلسلات UTF-8 غير الصالحة تُستبدل.
الحل: احتفظ بالبيانات الثنائية كـ byte[] عبر خط الأنابيب بالكامل. استدعِ GetString() فقط عندما تعلم أن المحتوى نصي.
byte[] decoded = Convert.FromBase64String(pngBase64);
// اكتب البايتات مباشرة — بدون تحويل إلى سلسلة
File.WriteAllBytes("image.png", decoded);byte[] decoded = Convert.FromBase64String(pngBase64);
string imageStr = Encoding.UTF8.GetString(decoded); // يُفسد البيانات الثنائية
File.WriteAllText("image.png", imageStr); // ملف تالفالمشكلة: بعض واجهات برمجة .NET Framework القديمة تعتمد افتراضياً على صفحة كود ANSI الحالية للنظام، التي تختلف بين الأجهزة. خادم Windows بصفحة كود 1252 وحاوية Linux بـ UTF-8 ينتجان سلاسل مختلفة من نفس البايتات.
الحل: حدد Encoding.UTF8 دائماً بشكل صريح. لا تعتمد أبداً على الافتراضي للنظام الأساسي.
byte[] decoded = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(decoded); // متسق عبر Windows وLinux وmacOS
byte[] decoded = Convert.FromBase64String(encoded); // Encoding.Default يتفاوت بين الأنظمة الأساسية string result = Encoding.Default.GetString(decoded);
مقارنة الدوال
يوفر .NET دوالاً لفك تشفير Base64 أكثر مما يُدرك معظم المطورين. يغطي الجدول أدناه كل خيار مدمج بالإضافة إلى أكثر بديلين من طرف ثالث شيوعاً. عمود "التخصيص" هو الأهم في الخدمات عالية الإنتاجية — دالة تُخصص byte[] جديداً عند كل استدعاء تضغط على جامع القمامة في الحلقات المكثفة.
للعمل اليومي: Convert.FromBase64String(). للمسارات الحرجة حيث تتحقق من مدخلات المستخدم: TryFromBase64String(). للبرمجيات الوسيطة في ASP.NET Core التي تعمل على بايتات الطلب الخام: Base64.DecodeFromUtf8(). للملفات الكبيرة: CryptoStream + FromBase64Transform. BouncyCastle منطقي فقط إذا كان موجوداً بالفعل في شجرة تبعياتك لعمليات تشفيرية أخرى.
للتحقق السريع دون ترجمة أي شيء، مفكك تشفير Base64 الإلكتروني أسرع من كتابة تطبيق console لمرة واحدة.
الأسئلة الشائعة
كيف أفك تشفير سلسلة Base64 إلى نص في C#؟
استدعِ Convert.FromBase64String() للحصول على مصفوفة بايت، ثم مرِّرها إلى Encoding.UTF8.GetString(). يجب أن يتطابق الترميز مع ما استُخدم أثناء التشفير — UTF-8 هو الخيار الآمن الافتراضي لجميع الأنظمة الحديثة تقريباً. إذا كان المدخل قد يحتوي على مسافات بيضاء أو فواصل أسطر، استدعِ .Trim() أو احذفها قبل فك التشفير.
using System; using System.Text; string encoded = "cG9zdGdyZXM6eGs5bVAycVI="; byte[] bytes = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(bytes); Console.WriteLine(result); // postgres:xk9mP2qR
ما الفرق بين Convert.FromBase64String() وConvert.TryFromBase64String()؟
تُطلق FromBase64String() استثناء FormatException عند مدخل غير صالح. أما TryFromBase64String() فتُعيد قيمة bool وتكتب النتيجة في Span<byte> يوفره المُستدعي، مما يجعلها مناسبة للمسارات الحرجة التي تريد فيها تجنب تكاليف الاستثناءات. تتطلب TryFromBase64String() الإصدار .NET 5 أو أحدث.
using System;
string input = "maybe-not-valid-base64!!";
Span<byte> buffer = stackalloc byte[256];
if (Convert.TryFromBase64String(input, buffer, out int written))
Console.WriteLine($"Decoded {written} bytes");
else
Console.WriteLine("Invalid Base64 input");كيف أفك تشفير حمولة JWT بصيغة Base64url في C#؟
قسِّم الرمز المميز على النقاط، خذ المقطع الثاني، استبدل - بـ + و_ بـ /، أضف حشوة = حتى يصبح الطول مضاعفاً للأربعة، ثم استدعِ Convert.FromBase64String(). تستخدم رموز JWT الأبجدية الآمنة لعناوين URL، التي لا يتعامل معها مفكك الشفرة القياسي في .NET مباشرةً.
string token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNjcyIn0.SIG";
string payload = token.Split('.')[1];
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(bytes));
// {"sub":"usr-672"}كيف أختار الترميز المناسب عند فك تشفير Base64 في C#؟
استخدم Encoding.UTF8 كخيار افتراضي — فهو يتعامل مع ASCII وجميع محارف Unicode متعددة البايت. استخدم Encoding.ASCII فقط عندما تكون متأكداً من أن البيانات نصية ASCII نقية ذات 7 بت. استخدم Encoding.Unicode (وهو UTF-16LE في .NET) فقط عندما كانت البيانات الأصلية مُرمَّزة بصيغة UTF-16، وهو ما يحدث أحياناً مع السلاسل الداخلية لنظام Windows وصادرات PowerShell.
لماذا تُطلق Convert.FromBase64String() استثناء FormatException؟
ثلاثة أسباب شائعة: المدخل يحتوي على مسافات بيضاء أو فواصل أسطر (احذفها قبل فك التشفير)، المدخل يستخدم محارف آمنة لعناوين URL مثل - و_ (استبدلهما بـ + و/ على التوالي)، أو الحشوة ناقصة أو غير صحيحة (يجب أن يكون الطول الإجمالي مضاعفاً للأربعة بعد الحشوة). على خلاف Java، لا يوجد في .NET مفكك MIME مدمج يتسامح مع المسافات البيضاء — يجب عليك تنظيف المدخل بنفسك أو استخدام CryptoStream مع FromBase64Transform بوضع IgnoreWhiteSpaces.
// إصلاح المسافات البيضاء
string cleaned = rawInput.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);هل يمكنني فك تشفير بيانات Base64 كبيرة عبر البث في C#؟
نعم. استخدم CryptoStream مع FromBase64Transform من System.Security.Cryptography. يفك هذا التشفير البيانات في أجزاء أثناء القراءة، مما يجعل استخدام الذاكرة ثابتاً بغض النظر عن حجم الملف. مرِّر FromBase64TransformMode.IgnoreWhiteSpaces إذا كان المدخل يحتوي على فواصل أسطر. في .NET 6+، يمكنك أيضاً استخدام نمط IAsyncEnumerable مع Base64.DecodeFromUtf8() لمعالجة يدوية مجزأة، رغم أن CryptoStream أبسط لفك تشفير الملفات.
using System.IO;
using System.Security.Cryptography;
using var input = File.OpenRead("payload.b64");
using var transform = new FromBase64Transform();
using var cryptoStream = new CryptoStream(input, transform, CryptoStreamMode.Read);
using var output = File.Create("payload.bin");
cryptoStream.CopyTo(output);أدوات ذات صلة
- Base64 Encoder — رمِّز النص أو البيانات الثنائية إلى Base64 في المتصفح، مفيد لإنشاء تجهيزات اختبار للصقها في اختبارات وحدة C#.
- JWT Decoder — فكِّ تشفير وافحص الثلاثة مقاطع JWT مرة واحدة، مع فحص الحمولة حقلاً بحقل — أسرع من كتابة دالة مساعدة في C# عندما تحتاج فقط لقراءة رمز مميز.
- URL Decoder — فكِّ ترميز النسبة المئوية للسلاسل المُرمَّزة بعناوين URL، مفيد عندما تمزج استجابات API بيانات Base64url مع معاملات استعلام مُرمَّزة بنسبة مئوية.
- JSON Formatter — بعد فك تشفير حمولة JWT أو إعداد API بصيغة Base64، الصق JSON هنا للطباعة الجميلة والتحقق من البنية.