क्या आपको Base64 फ़ॉर्मेट के साथ काम करना होता है? तो यह साइट आपके लिए एकदम सही है! अपने डेटा को एन्कोड या डिकोड करने के लिए, हमारे बेहद आसान ऑनलाइन टूल का इस्तेमाल करें।

C# (CSharp) में Base64 डिकोडिंग: एक सम्पूर्ण गाइड

इसे पहचानने में पल भर लगता है: अक्षरों और अंकों की लंबी धार, कभी-कभार + या /, और शायद आख़िर में लटक रहे एक-दो =। किसी API रिस्पॉन्स, ईमेल एटैचमेंट, कॉन्फ़िग फ़ाइल और JWT के बीच कहीं, किसी ने बाइनरी डेटा को टेक्स्ट में पैक कर दिया, और अब उसे खोलने का काम आपका है। यह C# में Base64 की डिकोडिंग वाली दिशा है, और सबसे पहला अच्छा समाचार यह है कि आपको फ्रेमवर्क के सिवाय कुछ भी ज़रूरत नहीं। यह डिकोडर बीस साल से ज़्यादा समय से System नेमस्पेस में बसा है, और हर आधुनिक .NET रनटाइम आज भी इसे साथ में देता है, मूल वाले से ज़्यादा विकल्पों और बेहतर स्पीड के साथ।

एक तेज़ दोहराई, क्योंकि इस साइट का होम पेज इस फ़ॉर्मैट को पूरा-पूरा समझाता है: 64 चिह्नों की वर्णमाला से लिए चार चर तीन बाइट्स का डेटा उठाते हैं, और पूंछ पर एक-दो = चर बाकी बचे बाइट्स का निशान करते हैं। डिकोडिंग उसी सौदे को उल्टा चलाती है, इसलिए रिज़ल्ट इनपुट का करीब तीन-चौथाई आकार का आता है। सवाल का स्वरूप समझ कर, चलिए कुछ पैकेज खोलते हैं।

डिकोडर परिवार: अपना विकल्प पहचान लें

पहले उदाहरण से पहले, यहाँ डिकोडिंग वाली API की पूरी क़ौम है, जिनमें से आप चुन सकते हैं, और हर एक किस परिस्थिति के लिए बनी है। यहाँ सूचीबद्ध सब कुछ .NET रनटाइम का अपना हिस्सा है, सिवाय URL-सुरक्षित क्लास के, जो पुराने फ्रेमवर्क्स पर एक छोटे से NuGet पैकेज के साथ आती है:

API कब से उपलब्ध इसे किस काम के लिए
Convert.FromBase64String(string) .NET Framework 1.1 (2003) क्लासिक। एक स्ट्रिंग अंदर, एक ताज़ा byte[] बाहर। साधारण व्हाइटस्पेस छोड़ देता है, बाक़ी सब पर थ्रो करता है।
Convert.FromBase64CharArray(char[], int, int) .NET Framework 1.1 (2003) वही डिकोड, लेकिन एक चर बफ़र के उस हिस्से से पढ़ता है जो आप पहले से पकड़े हुए हैं।
Convert.TryFromBase64String, Convert.TryFromBase64Chars .NET Core 2.1 (2018) एक्सेप्शन की जगह Boolean, और लिखने के लिए एक स्पैन जो आप देते हैं। अविश्वसनीय इनपुट के लिए दोस्ताना दरवाज़ेदार।
System.Buffers.Text.Base64 .NET Core 2.1 (2018) सख़्त स्पैन API: एक्सेप्शन की जगह स्टेटस कोड्स, इन-प्लेस डिकोडिंग, और IsValid प्री-चेक्स।
System.Buffers.Text.Base64Url .NET 9 (2024) URL-सुरक्षित वर्णमाला (+ और / की जगह - और _), पैडिंग के साथ या बिना। .NET Framework 4.6.2+ और .NET Standard 2.0 पर: Microsoft.Bcl.Memory NuGet पैकेज।
FromBase64Transform + CryptoStream .NET Framework 1.1 (2003) स्ट्रीमिंग डिकोडिंग: फ़ाइल से फ़ाइल, नेटवर्क से डिस्क, चंक-दर-चंक, पूरे पेलोड को लोड किए बिना।

अगर आपका प्रोजेक्ट 2018 के बाद के किसी .NET वर्ज़न को टारगेट करता है, तो पहली चार पंक्तियाँ बॉक्स में पहले से हैं। Base64Url को .NET 9 या उससे नया ज़रूरत है, या उससे पुरानी किसी भी चीज़ पर Microsoft.Bcl.Memory पैकेज। और आगे की एक नोट: .NET 11 लाइब्रेरियाँ, इस लेख लिखते समय प्रिव्यू में हैं, सामान्य रिलीज़ 2026 के अंत में उम्मीद है, मौजूदा टाइप्स में और Base64 सुविधा API और ओवरलोड जोड़ती हैं, तो यह परिवार बढ़ता ही जाएगा। इस लेख में बाक़ी कुछ भी पैकेज मांगता नहीं।

सबसे बड़ा कामगार: Convert.FromBase64String

C# की डिकोडिंग वाली ज़िंदगी का नब्बे प्रतिशत एक ही कॉल है। इसे एक स्ट्रिंग दें, और वह वापस वही बिल्कुल सटीक बाइट्स सौंपेगा जो अंदर पैक थे:

using System;
using System.Text;
string packed = "TWFu";
byte[] bytes = Convert.FromBase64String(packed);
string text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
// Man

तीन बातें याद रखने लायक हैं। पहली, रिटर्न वैल्यू बाइट्स हैं, टेक्स्ट नहीं: यह byte[] है, डिकोडर सिरे-से-सिर बाइट-ओरिएंटेड है, और यही बिल्कुल वह चीज़ है जो चाहिए, क्योंकि पेलोड एक वाक्य हो, PNG हो, सर्टिफ़िकेट हो, या हैश हो, इनमें से किसी को भी ख़ास नहीं मानना चाहिए। बाइट्स से वापस पढ़ने लायक टेक्स्ट तक का कदम एक अलग, जान-बूझ कर लिया गया कदम है Encoding के रास्ते, और बस उसी कदम पर कैरेक्टर सेट के फैसले बसते हैं (उसके बारे में नीचे और)। दूसरी, डिकोडर हर कॉल पर एक ताज़ा ऐरे अलोकेट करता है, डिकोड की गई लंबाई के बराबर साइज़ में, इसलिए वह कभी भी बेकार की खाली क्षमता वाला बफ़र नहीं सौंपता। तीसरी, करार छोटा और ईमानदार है: ख़ाली स्ट्रिंग ख़ाली ऐरे में डिकोड होती है, null रेफ़रेंस ArgumentNullException थ्रो करती है, और जो कुछ भी वैध Base64 नहीं है वह FormatException थ्रो करता है। बाक़ी सब इन तीन नियमों के विस्तार हैं।

क्या क्षमा करता है, क्या नहीं

यहाँ C# का डिकोडर अपना पक्का पन दिखाता है, और काफ़ी शख़्सियत वाला। बिल्कुल एक चीज़ में ही वह दयालु है - व्हाइटस्पेस - और बाक़ी सब में कठोर। डिकोडर स्ट्रिंग में कहीं भी आए तो बस चार चर छोड़ देता है: स्पेस (U+0020), टैब (U+0009), लाइन फ़ीड (U+000A) और कैरिएज रिटर्न (U+000D)। यह नीति ईमेल को दिया गया एक जान-बूझ कर इशारा है, जहाँ Base64 पेलोड्स 76-चर की पंक्तियों में लपेटे आते हैं, और इसका मतलब यह है कि एक MIME-रैप्ड एटैचमेंट ज़ीरो प्रीप्रोसेसिंग में डिकोड हो जाता है। 64-चिह्न वर्णमाला से बाहर कुछ भी, लंबाई के नियम तोड़ने वाला कुछ भी, या ग़लत जगह पैडिंग वाला कुछ भी, सबको एक्सेप्शन का हक़ मिलता है। एक ही डिकोडर को कुछ अलग-अलग इनपुट पर चलते देखें:

इनपुट नतीजा
"TWFu" Man में डिकोड होता है (3 बाइट्स)।
"TWF\nu" (बीच में एक न्यूलाइन) Man में डिकोड होता है। व्हाइटस्पेस डिकोडर के लिए अदृश्य है।
"TWFu\u00A0" (आख़िर में नॉन-ब्रेकिंग स्पेस) FormatException। सिर्फ़ ऊपर दिए चार व्हाइटस्पेस चर ही छोड़े जाते हैं; NBSP उनमें से नहीं है।
"TWE" (लंबाई 3, 4 का गुणज नहीं) FormatException। पेलोड की लंबाई, व्हाइटस्पेस न गिनते हुए, 4 की गुणज होनी चाहिए।
"TWFu=" (डेटा के बाद अतिरिक्त पैडिंग) FormatException। ज़्यादा से ज़्यादा दो पैडिंग चर, और बस सबसे आख़िर में।
"-_88" (URL-सुरक्षित वर्णमाला) FormatException। स्टैंडर्ड डिकोडर को सिर्फ़ स्टैंडर्ड वर्णमाला के 64 चर पता हैं।
null ArgumentNullException: Value cannot be null. (Parameter 's')

एक और अजीबोगरीज़ याद रखने लायक है: हर फ़ॉर्मैट-अपराध को वही एक ही एरर मैसेज मिलता है, The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters। मैसेज तीनों संभावित कारणों की सूची देता है, यह नहीं कहता कि आपने कौन सा हिट किया, और यह भी नहीं बताता कि कहाँ। अगर आप किसी फेल होने वाले पेलोड की डीबगिंग कर रहे हैं, तो पहले चरों की गिनती करें, फिर वर्णमाला जाँचें, फिर उसी क्रम में पैडिंग जाँचें।

बिना एक्सेप्शन के डिकोडिंग: Try API

एक्सेप्शन-चालित कंट्रोल फ़्लो एक बिल्कुल ठीक पैटर्न है, लेकिन हाई-वॉल्यूम या अविश्वसनीय इनपुट के लिए Try परिवार का नागरिक बेहतर है। यह .NET Core 2.1 में जुड़ा, और दो रूपों में आता है: एक जो स्ट्रिंग से पढ़ता है, और एक जो चर स्पैन से। दोनों उस बफ़र में लिखते हैं जो आप देते हैं, और बताते हैं कि उसका कितना हिस्सा भर पाया:

using System;
using System.Text;
string payload = "TWFu"; // कोई भी पेलोड, चाहे वैध हो या न हो
Span<byte> buffer = stackalloc byte[4096];
if (Convert.TryFromBase64String(payload, buffer, out int written))
{
  string text = Encoding.UTF8.GetString(buffer[..written]);
  Console.WriteLine(text);
}
else
{
  Console.WriteLine("Not a valid Base64 payload.");
}

दो व्यवहार ऐसे हैं जिनसे Try वेरिएंट किसी अलग प्रजाति जैसे लगते हैं। ग़लत इनपुट थ्रो करने की जगह false लौटाता है, इसलिए ख़राब पेलोड्स की धार आपको सिर्फ़ एक ब्रांच की कीमत पर मिलती है, एक्सेप्शन नहीं। एक चेतावनी: null इनपुट करार का हिस्सा नहीं है - वह ArgumentNullException थ्रो करती है - इसलिए Try का दरवाज़ेदार टूटे पेलोड्स को ढकता है, और ऐसा मान जो शायद ही मौजूद हो, उसे पहले भी अपनी null जाँच की ज़रूरत होगी। जुड़वाँ मीथड Convert.TryFromBase64Chars वही काम ReadOnlySpan<char> से करता है, जो तब हाथी आता है जब पेलोड किसी बड़े चर बफ़र में बसा हो और आप पहले से एक सबस्ट्रिंग काटकर निकालना न चाहें। आउटपुट बफ़र की साइज़ बड़ी-दम से रखें: डिकोड की गई लंबाई (व्हाइटस्पेस न गिने हुए) इनपुट लंबाई के ज़्यादा से ज़्यादा तीन-चौथाई होगी, और written आउट-पैरामीटर आपको बिल्कुल बताता है कि कितना बाहर आया।

System.Buffers.Text.Base64 से स्पैन-आधारित डिकोडिंग

जब आप अलोकेशन गिन रहे हों, या जब आप चाहें कि डिकोडर अपनी गड़बड़ियाँ बयान करे, उन्हें थ्रो करने के बजाय, तब System.Buffers.Text.Base64 क्लास ही वह औज़ार है। यह .NET Core 2.1 से स्टैंडर्ड लाइब्रेरी में एक स्टैटिक क्लास है, और यह मैनेज्ड ऐरे के बजाय स्पैन पर काम करता है। इसकी डिकोड मीथड एक OperationStatus वैल्यू लौटाती है, जिसके चार मूड होते हैं: Done (सफलता), DestinationTooSmall (आपका बफ़र छोटा था), NeedMoreData (इनपुट अभी 4 का गुणज नहीं है, पढ़ते रहिए), और InvalidData (यह Base64 नहीं है)। आख़िरी बूलियन पैरामीटर, isFinalBlock, ही उन दोनों को अलग करती है: यह डिकोडर को बताती है कि और इनपुट आने वाला है या नहीं। यहाँ वन-शॉट रूप है, साइज़ इसी क्लास के अपने हेल्पर से लगाया गया है:

using System.Buffers;
using System.Buffers.Text;
using System.Text;
string payload = "TWFu";
byte[] input = Encoding.ASCII.GetBytes(payload);
byte[] output = new byte[Base64.GetMaxDecodedFromUtf8Length(input.Length)];
OperationStatus status = Base64.DecodeFromUtf8(input, output,
  out int consumed, out int written, isFinalBlock: true);
if (status == OperationStatus.Done)
{
  Console.WriteLine(Encoding.UTF8.GetString(output.AsSpan(0, written)));
  // Man
}

इस क्लास के दो और मेम्बर्स एक-एक पराग्राफ़ के हक़दार हैं। पहला IsValid है, जो पेलोड को डिकोड किए बिना वैलिडेट करता है। यह बाइट-स्पैन और चर-स्पैन दोनों रूपों में आता है, और एक ओवरलोड नतीजे के साथ-साथ डिकोड की गई लंबाई भी बताता है, ताकि आप एक ही जाँच से बफ़र की साइज़ लगा सकें:

using System.Buffers.Text;
string payload = "TWFu";
if (Base64.IsValid(payload, out int decodedLength))
{
  Console.WriteLine("Valid, decodes to " + decodedLength + " bytes.");
  // Valid, decodes to 3 bytes.
}
else
{
  Console.WriteLine("Rejecting payload before allocating anything.");
}

दूसरा DecodeFromUtf8InPlace है, उस स्थिति के लिए जहाँ Base64 टेक्स्ट पहले से किसी ऐसे बफ़र में बसा हो जो आपका हो, और आप उसे ओवरराइट करके कोई दिक्कत न महसूस करें। डिकोडिंग डेटा को सिकोड़ती है, इसलिए रिज़ल्ट उसी बफ़र के सबसे आगे लिखा जाता है, और मीथड बताती है कि वह कितना लंबा है:

using System.Buffers;
using System.Buffers.Text;
using System.Text;
byte[] data = Encoding.ASCII.GetBytes("TWFu");
OperationStatus status = Base64.DecodeFromUtf8InPlace(data, out int written);
if (status == OperationStatus.Done)
{
  Console.WriteLine(Encoding.ASCII.GetString(data, 0, written));
  // Man, अब उसी बफ़र के पहले तीन बाइट्स में बसा
}

एक ऐसा व्यवहार जो जेब में रख लेना चाहिए: यह क्लास भी वही चार साधारण व्हाइटस्पेस चर (स्पेस, टैब, लाइन फ़ीड, कैरिएज रिटर्न) छोड़ देती है, इसलिए लाइन-रैप्ड पेलोड बिल्कुल उतना ही अच्छे से डिकोड होता है। वह जहाँ ज़रूरी है वहाँ सख़्त है: अगर पेलोड की व्हाइटस्पेस-बिना लंबाई 4 की गुणज नहीं है, तो वह आख़िरी ब्लॉक पर InvalidData है, और स्टैंडर्ड वर्णमाला से बाहर के चर सीधे-सीधे मना हो जाते हैं। इस क्लास में कहीं भी चुपचाप सफ़ाई नहीं है।

URL-सुरक्षित Base64: Base64Url क्लास

उसी 64 मानों के लिए दूसरी वर्णमाला मौजूद है, और C# की वेब ज़िंदगी में आप उसे बार-बार मिलेंगे। स्टैंडर्ड वर्णमाला में मान 62 और 63 + और / होते हैं, दो ऐसे चर जो URL में मुसीबत बनाते हैं: क्वेरी स्ट्रिंग में + आमतौर पर स्पेस की तरह डिकोड हो जाता है, और / और = दोनों को परसेंट-एन्कोडिंग की ज़रूरत होती है। RFC 4648, सेक्शन 5, इसका ठिकाना लगाता है: - और _ डाल देता है, जो किसी भी URL संदर्भ में कोई ख़ास मतलब नहीं रखते, और आख़िरी = पैडिंग को वैकल्पिक बना देता है। इसका नाम base64url है, और यह JWTs, API टोकन, फ़ाइल अपलोड ID और बहुत सारे URL की वर्णमाला है (YouTube के 11-चर वीडियो पहचान चिह्न बिना पैडिंग के base64url हैं)।

.NET 9 से स्टैंडर्ड लाइब्रेरी इसके लिए एक पक्की क्लास देती है: System.Buffers.Text.Base64Url। यह Base64 क्लास की URL-सुरक्षित जुड़वाँ है, अपने डिकोड, वैलिडेट और लंबाई हेल्परों के साथ:

using System.Buffers.Text;
using System.Text;
string token = "-__8";
byte[] bytes = Base64Url.DecodeFromChars(token);
Console.WriteLine(BitConverter.ToString(bytes));
// FB-FF-FC

ध्यान दें कि क्लासिक API उस उदाहरण के साथ क्या नहीं कर पाता था। वही तीन बाइट्स स्टैंडर्ड वर्णमाला में +//8 में एन्कोड होते हैं, और Convert.FromBase64String("+//8") चलता है, लेकिन Convert.FromBase64String("-__8") थ्रो करता है, क्योंकि URL-सुरक्षित चर उसकी वर्णमाला से बाहर हैं। और base64url पेलोड्स आमतौर पर बिना पैडिंग के आते हैं, जो क्लासिक डिकोडर भी मना कर देता है, क्योंकि वह चारों के पूरे समूह पर अडिग रहता है। Base64Url क्लास इस सवाल के दोनों रूपों को जन्म-सिद्ध ढंग से संभालती है: यह TWE (तीन चर, बिना पैडिंग) को दो बाइट्स Ma में डिकोड करती है, और TWE= को भी उतना ही अच्छे से।

अगर आपका प्रोजेक्ट पुराने रनटाइम पर चलता है, तो दो अमली रास्ते हैं। .NET Framework 4.6.2 और उससे ऊपर, Microsoft.Bcl.Memory NuGet पैकेज जोड़ लें, जिसे Microsoft खास तौर पर Base64Url बैकपोर्ट करने के लिए ही छोड़ती है (कुछ और आधुनिक टाइप्स के साथ):

dotnet add package Microsoft.Bcl.Memory

या, बिल्कुल बिना किसी पैकेज के, क्लासिक डिकोडर को सौंपने से पहले पेलोड को नॉर्मलाइज़ कर दें: URL-सुरक्षित चरों को वापस उनके स्टैंडर्ड जुड़वाओं से बदलें, और ग़ायब पैडिंग भर दें। यह छोटा सा हेल्पर C# कोड में सबसे आम हाथ-से-बनाया base64url डिकोडर है, और इतना जानने लायक है क्योंकि यह .NET Framework 1.1 से हर रनटाइम पर चलता है:

using System;
using System.Text;
string segment = "TWE";
segment = segment.Replace('-', '+').Replace('_', '/');
segment += new string('=', (4 - segment.Length % 4) % 4);
byte[] bytes = Convert.FromBase64String(segment);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
// Ma

(4 - length % 4) % 4 फ़ॉर्मूला ही पूरा पैडिंग का हिसाब-किताब है: यह ज़ीरो, एक या दो = चर जोड़ता है ताकि लंबाई 4 की गुणज पर बसे, और बाहर वाला मोड्यूलो पहले से पैडेड इनपुट को अतिरिक्त चर पाने से रोकता है।

बाइट्स से शब्दों तक: टेक्स्ट, Unicode और कैरेक्टर सेट

डिकोडिंग आपको बाइट्स देता है, और बाइट्स बिल्कुल न्यूट्रल चीज़ हैं। वे तभी "टेक्स्ट" बनते हैं जब आप उन्हें पढ़ने के लिए एक कैरेक्टर सेट चुनते हैं, और वह चयन आपका करना है, क्योंकि Base64 उस कैरेक्टर सेट के बारे में कोई जानकारी नहीं ढोता जो मूल लेखक ने इस्तेमाल किया था। अमल में इसका मतलब यह है: जब तक कोई वजह न हो, UTF-8 मानिए, और कोड में उसे स्पष्ट भी लिखिए, क्योंकि एक स्पष्ट Encoding.UTF8 कॉल का फ़र्क़ इसमें है कि कार्यक्रम बेहूदाई से सही है या डिज़ाइन से सही है:

using System;
using System.Text;
string original = "h\u00e9llo \u4e16\u754c";
byte[] utf8 = Encoding.UTF8.GetBytes(original);
string packed = Convert.ToBase64String(utf8);
byte[] decoded = Convert.FromBase64String(packed);
string restored = Encoding.UTF8.GetString(decoded);
Console.WriteLine(restored == original);
// True: h\u00e9llo \u4e16\u754c, रउंड ट्रिप बिल्कुल सही

सूक्ष्म फँसाव यह है जब बाइट्स वैध UTF-8 न हों, क्योंकि पेलोड वाक़ई Latin-1 का था, या बाइनरी का, या बस ख़राब। डिफ़ॉल्ट रूप से, .NET का UTF-8 डिकोडर हर ख़राब सिक्वेंस को Unicode रिप्लेसमेंट चर (U+FFFD) से बदल देता है और आगे बढ़ जाता है। न एक्सेप्शन, न चेतावनी: डेटा बस ग़ायब हो जाता है, आपके डेटाबेस में सवाल चिह्नों में बदल कर। अगर आपको जानना है कि यह कब हो रहा है, तो एन्कोडिंग को सख़्त फ़ॉलबैक के साथ बनाइए, जो चुपचाप रिप्लेसमेंट को शोर मचाने वाली DecoderFallbackException में बदल देता है:

using System.Text;
byte[] bytes = Convert.FromBase64String("//4="); // बाइट्स FF FE, वैध UTF-8 नहीं
Encoding strictUtf8 = Encoding.GetEncoding(
  "utf-8",
  new EncoderExceptionFallback(),
  new DecoderExceptionFallback());
string text = strictUtf8.GetString(bytes);
// DecoderFallbackException थ्रो होता है, क्योंकि FF FE कोई UTF-8 सिक्वेंस नहीं है

ऐसे पेलोड्स के लिए जिनमें आप फेल होने के बजाय बच जाना चाहते हैं, रिप्लेसमेंट फ़ॉलबैक्स नरम विकल्प हैं, और रिप्लेसमेंट टेक्स्ट आप खुद चुन सकते हैं:

using System.Text;
byte[] bytes = Convert.FromBase64String("//4="); // बाइट्स FF FE, वैध UTF-8 नहीं
Encoding forgivingUtf8 = Encoding.GetEncoding(
  "utf-8",
  EncoderFallback.ReplacementFallback,
  new DecoderReplacementFallback("[bad]"));
string text = forgivingUtf8.GetString(bytes);
Console.WriteLine(text);
// चुपचाप U+FFFD रिप्लेसमेंट की जगह [bad][bad]

एक और C#-ख़ास इतिहास का पाठ: Encoding.Default अलग-अलग रनटाइम पर अलग मतलब रखता है। Windows पर .NET Framework में यह सिस्टम की ANSI कोड पेज होती है (अक्सर Windows-1252), जबकि .NET (Core) पर यह BOM-बिना UTF-8 है। इसलिए जो कोड किसी पेलोड को Encoding.Default से रउंड ट्रिप करे, वह 2010 की मशीन पर एक बाइट्स बना सकता है और 2025 की मशीन पर दूसरा, और Base64 जो भी सेट आप देंगे, उसे खुशी-खुशी एन्कोड कर देगा। अगर कभी आपको कोई डिकोड स्ट्रिंग दिखे जिसमें एक्सेंटेड मोजीबैक भर हो, तो Encoding.Default ही पहली जगह है जहाँ नज़र डालें।

फ़ाइलें और बाइनरी पेलोड्स

फ़ाइलें सबसे सीधी-सधीरी डिकोडिंग की लक्ष्य हैं, क्योंकि यहाँ कैरेक्टर सेट का सवाल ही नहीं उठता: जो बाइट्स आप डिकोड करते हैं, वही फ़ाइल है, बाइट-दर-बाइट, जीरो समेत। पैटर्न दो कॉल और एक फ़ाइल है, और यह इमेज अपलोड से लेकर बैकअप औज़ारों तक सब में दिखता है:

using System.IO;
string b64 = File.ReadAllText("payload.b64");
byte[] original = Convert.FromBase64String(b64);
File.WriteAllBytes("restored.bin", original);
Console.WriteLine("Restored " + original.Length + " bytes.");

दो अमली नोट्स। अगर फ़ाइल में व्हाइटस्पेस या लाइन ब्रेक्स हो सकते हैं (वह टेक्स्ट फ़ाइल है, तो ज़्यादातर ऐसा ही होगा), तो क्लासिक डिकोडर उसे बिल्कुल मुफ़्त संभाल लेता है, जैसा कि आपने पहले देखा। और अगर पेलोड बड़ा है, तो स्ट्रिंग के रास्ते ही न जाएँ: फ़ाइल-से-स्ट्रिंग का कदम छोड़कर सीधे स्ट्रीम से डिकोड करें, जो अगला सेक्शन है। ऐसे डिकोड पेलोड के लिए जो टेक्स्ट है और जिसका कैरेक्टर सेट आप बस जानते हैं, फ़ाइल का उदाहरण ही पूरा हल है, और कैरेक्टर सेट सेक्शन का Encoding.UTF8.GetString कदम डिकोड और इस्तेमाल के बीच ठीक उसी जगह बैठ जाता है।

स्ट्रीम से डिकोडिंग: FromBase64Transform

Convert मीथड्स ऐसे पेलोड्स के लिए बनी हैं जो एक स्ट्रिंग में बैठ जाते हैं, और आधिकारिक दस्तावेज़ीकरण यही बिल्कुल उन्हीं शब्दों में कहता है: स्ट्रीमिंग डेटा के लिए ट्रांसफ़ॉर्म क्लास इस्तेमाल करें। FromBase64Transform .NET Framework 1.1 (2003) से System.Security.Cryptography का हिस्सा है, और यह CryptoStream में प्लग होता है, जो फ्रेमवर्क का सामान्य-उद्देश्य पाइप है जो डेटा को बहते-बहते बदलता है। पूरा फ़ाइल-से-फ़ाइल डिकोड चार पंक्तियों का सेटअप है:

using System.IO;
using System.Security.Cryptography;
using FileStream source = File.OpenRead("payload.b64");
using FromBase64Transform transform =
  new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using CryptoStream reader = new CryptoStream(source, transform, CryptoStreamMode.Read);
using FileStream target = File.Create("payload.bin");
reader.CopyTo(target);
Console.WriteLine("Done, " + target.Length + " bytes written.");

कन्स्ट्रक्टर को एक मोड मिलती है, और दोनों मोड्स का नाम जानने लायक है। IgnoreWhiteSpaces (डिफ़ॉल्ट, जो क्लासिक डिकोडर की व्हाइटस्पेस नीति से मेल खाती है) स्ट्रीम बहते हुए चारों साधारण व्हाइटस्पेस चर छोड़ देती है, जो ईमेल-रैप्ड या न्यूलाइन-भरे पेलोड्स के लिए बिल्कुल वही है जो चाहिए। DoNotIgnoreWhiteSpaces सख़्त है: पहला वर्णमाला-से-बाहर चर जिससे वह मुखातिम हो, FormatException थ्रो कर देता है, और यह वही है जो चाहिए जब पेलोड में भटकता हुआ स्पेस बग होना चाहिए, कंधे उचलने की बात नहीं। गद्दे के नीचे ट्रांसफ़ॉर्म इनपुट को चारों के समूहों में प्रोसेस करता है और हर समूह के बनाए तीन बाइट्स वापस सौंपता है, पूंछ का काम TransformFinalBlock संभालता है। आप उन मीथड्स को खुद दुर्लभ ही कॉल करते हैं, क्योंकि CryptoStream आपके लिए कर देता है, लेकिन चारों-के-समूह की बात मायने रखती है: अगर कभी आप ट्रांसफ़ॉर्म को हाथ से खिलाएँ, तो चार की गुणज में खिलाएँ, वरना आख़िरी अधूरा समूह फाइनल ब्लॉक में बैठ जाएगा।

JWTs: तीन सेगमेंट्स, एक डॉट

JSON Web Token C# वेब डेवलपमेंट में सबसे ज़्यादा ट्रैफ़िक वाला base64url पेलोड है, और उसका स्वरूप धोखा देने लायक आसान है: डॉट से अलग किए गए तीन सेगमेंट्स। पहला एन्कोडेड हेडर है, दूसरा एन्कोडेड पेलोड (इसे क्लेम्स भी कहते हैं), और तीसरा सिग्नेचर। JWS विनिर्देश के अनुसार, पहले दो में से हर एक UTF-8 JSON डॉक्युमेंट का base64url है, बिना पैडिंग के। बाँटना और डिकोड करना C# के दो पंक्तियों का काम है:

using System;
using System.Buffers.Text;
using System.Text;
using System.Text.Json;
string jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsIm5hbWUiOiJBZGEifQ.c2lnbmF0dXJl";
string[] parts = jwt.Split('.');
string headerJson = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[0]));
string payloadJson = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[1]));
using JsonDocument doc = JsonDocument.Parse(payloadJson);
Console.WriteLine(doc.RootElement.GetProperty("name").GetString());
// Ada

.NET 9 से पहले के रनटाइम पर, वही काम URL-सुरक्षित सेक्शन के नॉर्मलाइज़ेशन हेल्पर के रास्ते चलता है: - और _ को वापस + और / से बदलें, सेगमेंट को 4 की गुणज तक पैड करें, और Convert.FromBase64String से डिकोड करें। दोनों रास्ते आपको वही JSON देते हैं; जो भी आपकी टारगेट फ्रेमवर्क से मेल खाए, वही चुनें।

एक सीमा जो हमेशा साफ़ रखें: JWT डिकोड करना JWT को सत्यापित करना नहीं है। ऊपर वाला डिकोड किसी टोकन के क्लेम्स को खुशी-खुशी पढ़ लेगा जिसका सिग्नेचर कचरा हो, क्योंकि सिग्नेचर पहले दो सेगमेंट्स पर एक अलग क्राइप्टोग्राफिक जाँच है। प्रोडक्शन टोकन काम के लिए, हाथ से पार्स करना बिल्कुल छोड़ दें: System.IdentityModel.Tokens.Jwt पैकेज (Microsoft.IdentityModel परिवार से) पार्सिंग, वैलिडेशन और एक्सपायरेशन तीनों एक साथ संभालता है, और इसकी base64url हैंडलिंग बिल्कुल वही वर्णमाला है जो इस सेक्शन ने बताई है। डीबगिंग और छोटे-मोटे औज़ारों के लिए हाथ से डिकोड करें; हर उस चीज़ के लिए जिस तक यूज़र पहुँच सकता है, लाइब्रेरी से सत्यापित करें।

डेटा URIs और एम्बेडेड इमेजेस

C# का एक पूरा वर्ग है जिसका काम data: URI को पाना है, क्योंकि HTML, CSS और बहुत सारी वेब API इन्हें इस्तेमाल करके बाइनरी कंटेंट को इनलाइन एम्बेड करती हैं। यह स्कीम, जिसे RFC 2397 ने मानक बनाया, है data:[mediatype][;base64],payload: पहली कॉमा से पहले सब कुछ मेटाडेटा है (MIME टाइप और ;base64 फ़्लैग), और उसके बाद सब कुछ पेलोड है। जब ;base64 फ़्लैग मौजूद हो, तो पेलोड एक Base64 स्ट्रिंग है, और कॉमा पर काटना ही पूरा पार्स है:

using System;
using System.Text;
string dataUri = "data:image/png;base64,iVBORw0KGgo=";
int comma = dataUri.IndexOf(',');
string mediaType = dataUri[..comma];        // data:image/png;base64
string b64 = dataUri[(comma + 1)..];        // iVBORw0KGgo=
byte[] imageBytes = Convert.FromBase64String(b64);
Console.WriteLine(imageBytes.Length);
// 8: PNG सिग्नेचर बाइट्स 89 50 4E 47 0D 0A 1A 0A

उदाहरण में iVBORw0KGgo= प्रीफिक्स आठ-बाइट PNG मैजिक नंबर की Base64 रूप है, और यह एक काम-आने वाला फिंगरप्रिंट है: असली PNG के लिए कोई भी डेटा URI इसी तरह से शुरू होता है, इसलिए अविश्वसनीय HTML पार्स करते समय यह एक तेज़ सैनिटी चेक है। C# डेवलपर्स के लिए दो अमली नोट्स। पहला, Uri क्लास .NET पर डेटा URIs को जन्म-सिद्ध ढंग से समझती है: new Uri("data:text/plain;base64,TWFu") बिना दिक्कत पार्स होता है और Scheme == "data" रिपोर्ट करता है, इसलिए अगर आपका कोड URIs पर राउट्स करता है, तो डेटा URIs पाइपलाइन में सामने आएंगे, और आपको तय करना होगा कि उनका क्या करना है। दूसरा, याद रखें कि डेटा URI वाक़ई में क्या है: फ़ाइल की पूरी कॉपी, एक-तिहाई फूलकर, आपके डॉक्युमेंट के अंदर बसी हुई। 4 KB फ़ेविकॉन के लिए यह ठीक है, 4 MB लोगो के लिए दर्दनाक है, इसलिए जब आप खुद इन्हें जनरेट कर रहे हों (एन्कोडिंग लेख उस पक्ष को कवर करता है), तो एन्कोड करने से पहले इमेज की साइज़ तय करें।

HTTP: Basic Auth और API एक्सचेंजेस

Base64 HTTP में कम से कम एक ऐसी जगह पर बुना है, जो आप किसी भी API काम में हाथ लगाएँगे: Basic ऑथेंटिकेशन स्कीम। क्लाइंट Authorization: Basic भेजता है, और उसके बाद username:password की Base64 एन्कोडिंग, जो कॉलन से जोड़ी गई है। सर्वर पक्ष पर, आने वाले हेडर को डिकोड करना इसलिए यही है: Basic प्रीफिक्स हटाएँ, डिकोड करें, और पहली कॉलन पर बाँटें:

using System;
using System.Text;
string header = "Basic YWRhOnMzY3JldA==";
string encoded = header["Basic ".Length..].Trim();
string credentials = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
int colon = credentials.IndexOf(':');
string user = credentials[..colon];
string password = credentials[(colon + 1)..];
Console.WriteLine(user);      // ada
Console.WriteLine(password);  // s3cret

UTF-8 वाला कदम जितना दिखता है उससे ज़्यादा मायने रखता है: RFC 7617 वाक़ई में कैरेक्टर सेट पिन नहीं करता, बैकवर्ड कॉम्पाटिबिलिटी के लिए डिफ़ॉल्ट को अनिश्चित छोड़ता है और सिर्फ़ एक सलाह-स्वरूप UTF-8 संकेत की इज़ाज़त देता है, लेकिन वही संकेत हर आधुनिक सर्वर की उम्मीद है, इसलिए एक्सेंटेड चर वाले यूज़रनेम से एक अलग (और सही) बाइट स्ट्रिंग बनती है, वही यूज़रनेम Latin-1 में पढ़े तो। Basic ऑथेंटिकेशन की डिकोड वाली ओर इस पैटर्न का आसान सिरे का हिस्सा है; ASP.NET Core में आप इसे आमतौर पर ऑथेंटिकेशन हैंडलर्स के रास्ते मिलेंगे, राव हेडर्स से नहीं, लेकिन नीचे चलती वही डिकोड लॉजिक है, और यही वह तरह का कोड है जो आपको तब ज़रूरत पड़ता है जब आप इंटीग्रेशन टेस्ट्स लिखते हैं जो किसी API सर्वर की नकल करते हैं। उल्टा काम, क्लाइंट पक्ष पर हेडर बनाना, एन्कोडिंग पक्ष पर एक पंक्ति है, और एन्कोडिंग लेख में उसे पूरा उदाहरण मिलता है।

ईमेल: MIME और लाइन-रैप्ड पेलोड्स

ईमेल वही जगह है जहाँ Base64 ने अपना ख़्याला कमाया, और यह आज भी उन पेलोड्स का बहुत बड़ा स्रोत है जो C# सर्विस पाती हैं। SMTP मूलतौर पर 7-बिट प्रोटोकॉल था, इसलिए बाइनरी एटैचमेंट राव नहीं जा सकते: MIME विनिर्देश (RFC 2045) इन्हें Content-Transfer-Encoding: base64 हेडर के साथ Base64 में एन्कोड करता है, आउटपुट को 76 चर पर रैप करता है, और पंक्तियों के बीच कैरिएज-रिटर्न-लाइन-फ़ीड जोड़े लगाता है। एक असली एटैचमेंट बॉडी इसलिए 76-चर की पंक्तियों की एक क़तार जैसी दिखती है, और C# के लिए अच्छा समाचार यह है कि क्लासिक डिकोडर पहले से जानता है कि इसे कैसे पढ़ना है: क्योंकि वह स्ट्रिंग में कहीं भी व्हाइटस्पेस छोड़ देता है, आप इसे पूरा रैप्ड बॉडी सौंप सकते हैं, न्यूलाइन्स समेत, और वह उसे ऐसे डिकोड कर देगा मानो लाइन ब्रेक्स कभी थे ही नहीं:

using System;
using System.Text;
string attachmentBody = "TWFu\r\nTWFu\r\nTWFu";
byte[] bytes = Convert.FromBase64String(attachmentBody);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
// ManManMan

ऐसे पेलोड्स के लिए जो स्ट्रीम से आते हैं, स्ट्रिंग से नहीं, FromBase64Transform अपने व्हाइटस्पेस-नज़रअंदाज़ मोड के साथ वही कहानी है, बस स्ट्रीमिंग के कपड़ों में। और जब आपको बॉडी डिकोड करने से आगे जाना पड़े, जब MIME स्ट्रक्चर को टहलना हो, हेडर्स को पार्स करना हो, नेस्टेड मल्टीपार्ट सेक्शंस को संभालना हो, या किसी असली .eml फ़ाइल से हर एटैचमेंट निकालना हो, तब C# की इकोसिस्टम का जवाब MimeKit पैकेज है: यह .NET की स्टैंडर्ड MIME लाइब्रेरी है, यह Base64 और quoted-printable कंटेंट ट्रांसफ़र एन्कोडिंग्स को अंदर ही संभालती है, और "बस बॉडी डिकोड करो" आपकी समस्या का वर्णन बंद करने के पल के लिए बस वही औज़ार है। फ्रेमवर्क की अपनी MailMessage क्लास सरल एटैचमेंट्स को आपके लिए डिकोड कर देगी, लेकिन आधुनिक मानकों की रोशनी में इसका MIME सपोर्ट जान-बूझ कर मज़बूत-से-कम है।

PEM सर्टिफ़िकेट्स

PEM TLS दुनिया का कवच-रूप है: -----BEGIN CERTIFICATE----- और -----END CERTIFICATE----- मार्करों के बीच एक Base64 बॉडी, 64 चर पर रैप्ड, जैसे कि RFC 7468 ने तय किया है। C# डेवलपर्स इसे हर HTTPS एंडपॉइंट के पीछे बसे सर्टिफ़िकेट फ़ाइल के रूप में मिलते हैं, और यहाँ की डिकोड की कहानी उम्मीद से अच्छी है, क्योंकि .NET 6 से फ्रेमवर्क आपके लिए PEM पार्स करता है, Base64 बॉडी समेत:

using System.IO;
using System.Security.Cryptography.X509Certificates;
string pem = File.ReadAllText("server.pem");
X509Certificate2 certificate = X509Certificate2.CreateFromPem(pem);
Console.WriteLine(certificate.Subject);
// CN=server.example.com

उसमें कहीं भी मैन्युअल Base64 नहीं: CreateFromPem मार्कर ढूँढता है, बॉडी को अनरैप करता है, डिकोड करता है, और आपको एक जीवित सर्टिफ़िकेट वापस सौंपता है। (इस परिवार के जुड़वाँ प्राइवेट कीज़ के लिए और सर्टिफ़िकेट-प्लस-की के मिलाए रूप के लिए भी हैं, अगर आपकी इन्फ्रास्ट्रक्चर आपको वही सौंपती है।) अगर आप पुराने रनटाइम पर हैं, या आपको वे राव DER बाइट्स चाहिए जो कवच के अंदर बसे हैं, तो मैन्युअल रूप दो कदमों का स्ट्रिप-एंड-डिकोड है, और यह जानने लायक है क्योंकि वही पैटर्न किसी भी PEM-कवची हुई चीज़ के लिए चलता है:

using System;
using System.Text;
string pem = File.ReadAllText("server.pem");
string body = pem
  .Replace("-----BEGIN CERTIFICATE-----", "")
  .Replace("-----END CERTIFICATE-----", "")
  .Replace("\r", "")
  .Replace("\n", "");
byte[] der = Convert.FromBase64String(body);
Console.WriteLine(der.Length);
// कवच के अंदर डबी DER सर्टिफ़िकेट की लंबाई

इस कोने के फँसाव सब व्हाइटस्पेस के हैं: ज़्यादातर सर्टिफ़िकेट औज़ारों की वजह से PEM फ़ाइलें CRLF लाइन एंडिंग्स लेती हैं, इसलिए डिकोड से पहले \r और \n दोनों हटाएँ, बस लाइन फ़ीड नहीं। और सर्टिफ़िकेट बॉडी और प्राइवेट की बॉडी में भ्रम न करें, जिसके अलग मार्कर हैं और अलग कंटेंट; डिकोडर आपको इससे नहीं बचा पाएगा।

कॉन्फ़िग, एनवायरनमेंट वेरिएबल और डेटाबेसेस

C# एप्लिकेशन में Base64 का तीसरा घर स्टोरेज है: कॉन्फ़िग फ़ाइलें, एनवायरनमेंट वेरिएबल, और डेटाबेस कॉलम। पैटर्न हर जगह वही है। बाइनरी या सीक्रेट वैल्यू अंदर आते समय स्ट्रिंग में एन्कोड होती है, और बाहर जाते समय वापस बाइट्स में डिकोड। एनवायरनमेंट वेरिएबल सबसे दिखने वाले उदाहरण हैं, क्योंकि वे सिर्फ़ टेक्स्ट ही रख सकती हैं:

using System;
using System.Text;
string? encoded = Environment.GetEnvironmentVariable("API_KEY_B64");
if (encoded == null)
{
  throw new InvalidOperationException("Set the API_KEY_B64 environment variable first.");
}
byte[] keyBytes = Convert.FromBase64String(encoded);
string apiKey = Encoding.UTF8.GetString(keyBytes);
Console.WriteLine(apiKey.Length + " characters of API key, ready to use.");

डेटाबेस में वही आइडिया आमतौर पर byte[] प्रॉपर्टी के रूप में दिखता है, जिसे आप पोर्टेबिलिटी के लिए टेक्स्ट कॉलम में स्टोर करना चाहते हैं, और Entity Framework Core के पास बिल्कुल इसके लिए एक बिल्ट-इन तंत्र है: वैल्यू कन्वर्टर, जो हर रीड और राइट पर आपके एन्कोड और डिकोड फ़ंक्शनों को चुपचाप चलाता है:

using Microsoft.EntityFrameworkCore;
modelBuilder.Entity<Avatar>()
  .Property(a => a.ImageData)
  .HasConversion(
    v => Convert.ToBase64String(v),
    v => Convert.FromBase64String(v));

वही एक कन्वर्टर पूरा डेटाबेस इंटीग्रेशन है: ImageData आपके C# कोड में byte[] ही रहता है, और डेटाबेस को एक Base64 स्ट्रिंग दिखती है। इस सेक्शन के साथ दो चेतावनियाँ चलती हैं। पहली, किसी निश्चित चौड़ाई के कॉलम में एन्कोडेड टेक्स्ट के रूप में राव बाइनरी की तुलना में करीब एक-तिहाई कम डेटा बैठता है, 4-चर-प्रति-3-बाइट के टैक्स की वजह से, इसलिए अगर चौड़ाई फिक्स्ड है तो कॉलम को एन्कोडेड लंबाई के लिए साइज़ करें। दूसरी, और यही वह सुरक्षा वाली है: कॉन्फ़िग फ़ाइल में Base64 किसी वैल्यू को एक पंक्ति पर रखने की सुविधा है, उस वैल्यू की रक्षा नहीं। जो कोई भी कॉन्फ़िग फ़ाइल पढ़ सकता है, वही एक कमांड में की डिकोड कर सकता है, इसलिए असली सीक्रेट्स को सीक्रेट स्टोर में रहना चाहिए, और वहाँ की Base64 बस ट्रांसपोर्ट फ़ॉर्मैट है।

जब पेलोड बड़ा हो

Base64 डिकोडिंग में एक सुखद गुण है जो एन्कोडिंग में नहीं: आउटपुट हमेशा इनपुट से छोटा होता है, करीब उसके तीन-चौथाई। 10 मेगाबाइट का टेक्स्ट पेलोड करीब 7.5 मेगाबाइट बाइट्स में डिकोड होता है, इसलिए डिकोड कभी भी एन्कोड की तरह आपकी मेमोरी को फूल नहीं सकता। गणित, अगर आपको बफ़र की साइज़ पहले से लगानी है, दो कॉलों में से किसी एक तक आकर रुकता है: सख़्त स्पैन क्लास के लिए Base64.GetMaxDecodedFromUtf8Length, या सादा भागफल, क्लासिक API के लिए length / 4 * 3, और अगर इनपुट रैप्ड है तो व्हाइटस्पेस के लिए एक फ़ज। (हेल्पर अधिकतम संभव डिकोड लंबाई लौटाता है: असली लंबाई तभी उसके बराबर होती है जब आख़िरी समूह में पैडिंग न हो, और जब वह एक-दो पैड चरों पर ख़त्म हो तो एक-दो बाइट्स कम।)

लेकिन जब पेलोड वाक़ई बड़ा हो, तो सही कदम बड़ा बफ़र नहीं है - बफ़र ही नहीं है: स्ट्रिंग को बिल्कुल छोड़ दें और FromBase64Transform को सोर्स से टारगेट तक डिकोड स्ट्रीम करने दें, जैसे कि स्ट्रीम्स सेक्शन में दिखाया। एक ही नियम का ख़याल रखना है: चारों-के-समूह की एलाइनमेंट। Base64 स्ट्रीम को सिर्फ़ चार चरों की गुणज पर ही काटा जा सकता है (व्हाइटस्पेस गिनने के बाद), इसलिए अगर कभी आप ट्रांसफ़ॉर्म को हाथ से खिलाएँ, तो चार की गुणज वाले चंक्स में पढ़ें और बाक़ी बचा हुआ TransformFinalBlock को निकालने दें। सैकड़ों मेगाबाइट से नीचे की हर चीज़ के लिए, वन-शॉट डिकोड इतना तेज़ है कि यह ज़रूरत नहीं, ऑप्टिमाइज़ेशन है, लेकिन स्ट्रीमिंग रूप वही भी है जो मेमोरी सीमाओं के नीचे अच्छे से चलता है, और बड़े पेलोड्स को बस वही माहौल पसंद है।

आपके टर्मिनल में डिकोडर

हर भाषा में एक संतोष देने वाला पल आता है, जब 15 पंक्तियों का कंसोल प्रोग्राम एक कमांड-लाइन औज़ार बन जाता है, और C# का Base64 डिकोडर इसे करने का अच्छा मौका है, क्योंकि स्टैंडर्ड इनपुट से पढ़ने की वजह से वह शेल पाइप्स के लिए ड्रॉप-इन बन जाता है। यहाँ पूरा औज़ार है: यह पाइप (या किसी आर्गुमेंट) से Base64 पेलोड पढ़ता है, उसे डिकोड करता है, और राव बाइट्स को फ़ाइल में लिखता है:

using System;
using System.IO;
using System.Text;
string input = args.Length > 0 ? File.ReadAllText(args[0]) : Console.In.ReadToEnd();
byte[] bytes = Convert.FromBase64String(input.Trim());
File.WriteAllBytes("output.bin", bytes);
Console.Error.WriteLine("Wrote " + bytes.Length + " bytes to output.bin.");

एक बार बिल्ड करें, और वह शेल के अपने base64 औज़ार के बगल में बैठ जाता है, उन दिनों के लिए जब आपको खास तौर पर .NET रनटाइम का डिकोडर चाहिए: किसी फ़ाइल को इसमें पाइप करें, इसे दूसरे औज़ारों से जोड़ें, और सख़्त C# वैलिडेशन नियम (व्हाइटस्पेस-सहनशील, वर्णमाला-सख़्त, पैडिंग-सख़्त) आपके पाइपलाइन का हिस्सा बन जाते हैं। Trim() वहाँ चुपचाप काम कर रहा है, वह आख़िरी न्यूलाइन पकड़ रहा है जिसे टेक्स्ट एडिटर जोड़ना बहुत पसंद करते हैं, हालाँकि सच कहें तो डिकोडर उसे नज़रअंदाज़ कर ही देता। उन URL-सुरक्षित पेलोड्स के लिए जो API लॉग्स में बढ़ते-बढ़ते दिखने लगे हैं, वही ख़ाका जिसमें URL-सुरक्षित सेक्शन का Base64Url डिकोड है, पूरा बदलाव है।

स्पीड: क्या उम्मीद करें

आधुनिक .NET में Base64 तेज़ है, और बढ़ता ही जा रहा है। Convert मीथड्स और System.Buffers.Text क्लासों दोनों के रनटाइम इम्प्लेमेंटेशन हार्डवेयर जहाँ सपोर्ट करता है वहाँ SIMD वेक्टर इंस्ट्रक्शन से ऑप्टिमाइज़्ड हैं, और वे हर साइकिल में कई चर प्रोसेस करते हैं। अमल में इसका मतलब यह है कि कई-मेगाबाइट पेलोड्स एक साधारण डेस्कटॉप मशीन पर सिंगल-डिजिट से लो-डबल-डिजिट मिलीसेकंड में डिकोड हो जाते हैं, जो इतना तेज़ है कि किसी भी एप्लिकेशन जिसमें आप लिखेंगे, वहाँ Base64 डिकोडिंग प्रैक्टिकली फ्री है। इसलिए प्रैक्टिकल परफ़ॉर्मेंस सलाह आपकी कोड के शैप के बारे में है, डिकोडर के बारे में नहीं। हॉट पैथ्स पर Try मीथड्स या स्टेटस लौटाने वाले स्पैन मीथड्स को प्राथमिकता दें, जहाँ ख़राब इनपुट संभव हो और एक्सेप्शन महगे पड़ें। जब आप लूप में हज़ारों छोटे पेलोड्स डिकोड कर रहे हों, तो इन-प्लेस और स्पैन API के साथ बफ़र दोहराएँ, हर कॉल पर नई ऐरे अलोकेट करने के बजाय। और किसी पेलोड को दो बार डिकोड करना बिल्कुल मत कीजिए: एक बार की कीमत है, और जिस फ़ील्ड को आपने पहले ही डिकोड कर लिया, उसका दूसरा डिकोड शुद्ध बर्बाद है, जो प्रोफ़ाइल्स में एक रहस्यमयी दूसरे Base64 स्पाइक के रूप में दिखता है।

सुरक्षा: Base64 जो नहीं करता

Base64 के बारे में सबसे ज़रूरी सुरक्षा तथ्य वही है जो शुरुआती लोग सबसे अक्सर छूट जाते हैं: यह एन्कोडिंग है, एन्क्रिप्शन नहीं। Base64 स्ट्रिंग किसी भी इंसान, किसी भी औज़ार से, एक सेकंड के हिस्से में पढ़ी जा सकती है, और C# उसे पढ़ना एक पंक्ति बना देता है, जैसा कि पूरा यह लेख दिखा चुका है। Base64 में न की है, न एल्गोरिथम पैरामीटर, न कोई कमज़ोरी जिसे लूटा जा सके, क्योंकि वह कभी कुछ छुपाने की कोशिश ही नहीं कर रहा था: यह ट्रांसपोर्ट फ़ॉर्मैट है, बाइनरी को टेक्स्ट-ओनली चैनल्स में बचाए रखने का तरीक़ा। उसके अनुसार ही इससे व्यवहार करें। कभी पासवर्ड, टोकन या सीक्रेट को Base64 से "सुरक्षित" कॉन्फ़िग फ़ाइल में न डालें, क्योंकि वह सुरक्षा बस एक Convert.FromBase64String कॉल गहरी है। अगर वैल्यू सीक्रेट होनी चाहिए, तो उसे असली सुरक्षा चाहिए (सीक्रेट मैनेजर, एन्क्रिप्टेड स्टोर, कम से कम ऑपरेटिंग-सिस्टम एक्सेस कंट्रोल), और Base64 बस वह शैप है जो वह सफ़र करते समय पहनता है।

दूसरी सुरक्षा नोट आपकी अपनी डिकोड राह के बारे में है। हर पेलोड जो आप डिकोड करते हैं अविश्वसनीय इनपुट है, जब तक साबित न हो जाए कि नहीं, और दो फ़ेलियर मोड्स जिनके लिए डिज़ाइन करना है, वे हैं: शोर वाला (ख़राब इनपुट, जिसका जवाब क्लासिक API FormatException देता है, जिसे आपको कैच करके 400 में बदलना चाहिए, 500 नहीं) और चुप वाला (वैध Base64 जो ऐसे बाइट्स में डिकोड होता है जो आपकी उम्मीद का नहीं: UTF-8 नहीं, वह फ़ाइल टाइप नहीं जिसे आपने माँगा था, या आपकी बजट से लंबा)। भरोसे से पहले वैलिडेट करें: अलोकेट करने से पहले IsValid या Try परिवार से लंबाई जाँचें, इमेज या सर्टिफ़िकेट पार्सर को सौंपने से पहले डिकोड बाइट्स को एक उम्मीद की गई सिग्नेचर (PNG मैजिक, PKCS हेडर) के मिला कर देखें, और बफ़र एन्कोडेड लंबाई से डिकोड से पहले साइज़ करें, बाद में नहीं। Base64 कुछ भी वेल-फ़ॉर्म्ड है तो डिकोड कर देगा; तय करना कि आपकी एप्लिकेशन के लिए वेल-फ़ॉर्म्ड का मतलब क्या है, वह काम आपका है।

वे फँसाव जो तब तक जानने चाहिए, जब तक काटें

यह वही C#-ख़ास गड्ढे हैं जो असली कोड में बार-बार सामने आते हैं, और हर एक की फ्रेमवर्क के काम करने के तरीक़े में एक कंक्रिट वजह है:

  • स्ट्रिंग से गुज़रता बाइनरी। C# string UTF-16 कोड यूनिट्स की एक कतार है, और डिकोड Base64 वही नहीं है। जिस पल आप डिकोड बाइट्स को स्ट्रिंग वेरिएबल में ठूँसें (डिकोड PNG का Console.WriteLine, बाइनरी के साथ स्ट्रिंग कॉन्केट, कोई JSON लाइब्रेरी जो "टेक्स्ट" सीरियलाइज़ करे), कोई ना कोई डाउनस्ट्रीम चीज़ उसे तोड़-फोड़ देगी। डिकोड बाइनरी को byte[] में ही रखें, जब तक वह उस जगह तक नहीं पहुँच जाता जो वाक़ई बाइट्स चाहती है।
  • Encoding.Default का फ़ाट। जो कोड डिकोड बाइट्स को Encoding.Default से पढ़ता है, वह .NET Framework (Windows ANSI code page) और .NET (UTF-8) पर अलग-अलग टेक्स्ट बनाता है। वही पेलोड, दो अलग आउटपुट, कोई एक्सेप्शन नहीं। अपनी एन्कोडिंग को स्पष्ट रूप से पिन करें।
  • JWT सेगमेंट्स और क्लासिक डिकोडर। राव JWT सेगमेंट को Convert.FromBase64String में डालने पर दो तरह से एक साथ फेल होता है: -/_ चर स्टैंडर्ड वर्णमाला से बाहर हैं, और ग़ायब पैडिंग लंबाई के नियम को तोड़ती है। पहले नॉर्मलाइज़ करें, या Base64Url इस्तेमाल करें।
  • व्हाइटस्पेस जो दिखे, और व्हाइटस्पेस जो न दिखे। डिकोडर स्पेस, टैब, लाइन फ़ीड और कैरिएज रिटर्न छोड़ता है, और कुछ और नहीं। पेलोड में नॉन-ब्रेकिंग स्पेस, Unicode लाइन सेपरेटर, या वर्टिकल टैब (जो सब कुछ कुछ वेब पेजेस से कॉपी-पेस्ट में बच जाते हैं) एक FormatException है, कंधे उचलने की बात नहीं।
  • हर अपराध के लिए एक एरर मैसेज। क्लासिक डिकोडर का FormatException नहीं बताता कि कौन सा नियम टूटा या कहाँ। डीबग ऐसे करें: पहले लंबाई जाँचें, फिर वर्णमाला, फिर पैडिंग, उसी क्रम में, या बूलियन जवाब के लिए TryFromBase64String और IsValid पर चले जाएँ।
  • चुपचाप UTF-8 रिप्लेसमेंट। Encoding.UTF8.GetString ख़राब बाइट सिक्वेंसेस को शिकायत किए बिना U+FFFD में बदल देता है। अगर पेलोड वैध UTF-8 नहीं भी हो सकता है, तो कैरेक्टर सेट सेक्शन का सख़्त फ़ॉलबैक इस्तेमाल करें, वरना आप हफ़्तों बाद ग़ायब डेटा की जाँच कर रहे होंगे।
  • ग़लत जगह पर स्ट्रीम काटना। Base64 स्ट्रीम को सिर्फ़ चार चरों की गुणज पर काटा जा सकता है। स्ट्रीमिंग डिकोड को किसी और सीमा पर चंक करें, तो आख़िरी अधूरा समूह TransformFinalBlock में बैठ जाता है, जहाँ या तो वह ठीक है या वह आपकी एलाइनमेंट की गिनती तोड़ देता है।
  • PEM लाइन एंडिंग्स। सर्टिफ़िकेट फ़ाइलें CRLF लेती हैं। कवच को मैन्युअली अनरैप करते समय \n के साथ-साथ \r भी हटाएँ, वरना आपके "डिकोड" DER की पहली पंक्ति एक कैरिएज रिटर्न है जो डेटा बाइट के कपड़ों में लपेटा है।
  • डबल-एन्कोडिंग। अगर पेलोड आपको पहुँचने से पहले ही Base64 था (कोई कॉन्फ़िग जिसने Base64 स्ट्रिंग को Base64 किया, कोई API जिसने दूसरे एन्कोडर के आउटपुट को एन्कोड किया), तो एक डिकोड आपको और Base64 देगा, आपका डेटा नहीं। रउंड ट्रिप बस उतने ही डिकोड के बाद बंद होती है जितने एन्कोड हुए थे, और उस बग की एन्कोडर वाली ओर एन्कोडिंग लेख का विषय है।

C# में Base64 का छोटा सा इतिहास

C# में Base64 की कहानी .NET प्लेटफ़ॉर्म के बड़ने की कहानी भी है, और यह ज़्यादातर लोगों की उम्मीद से लंबी चली है:

  • .NET Framework 1.1, अप्रैल 2003। Convert.FromBase64String और उसके जुड़वाँ आते हैं, और उनके साथ वही डिज़ाइन आता है जो आज भी API को परिभाषित करता है: वर्णमाला के प्रति सख़्त, चारों व्हाइटस्पेस चरों के प्रति उदार, और अपने एररों के प्रति सीधा। अगले दो दशकों के ज़्यादा हिस्से में यह एक ही मीथड C# में "वह" Base64 डिकोडर थी।
  • .NET 2.0, 2005। Base64FormattingOptions एन्म Convert में जुड़ता है, और MIME-शैली के लाइन ब्रेक्स को एन्कोडिंग पक्ष पर ले आता है (और डिकोड पक्ष पर मेल खाने वाली व्हाइटस्पेस सहनशीलता भी, जहाँ वह पहले से चुपचाप काम कर रहा था)।
  • .NET Core 2.1, 2018। Span का दौर। Convert को Try मीथड्स और स्पैन-आधारित एन्कोड मिलते हैं, और नई System.Buffers.Text.Base64 क्लास अपने OperationStatus करार, इन-प्लेस डिकोडिंग और IsValid के साथ आती है, मेमोरी-फ़ोकस्ड रीराइट की ज़ीरो-अलोकेशन दुनिया के लिए बनी हुई।
  • .NET 5, 2020। हेक्स जुड़वाँ (Convert.ToHexString और दोस्त) शिप होते हैं, Base64 वाला वही डिज़ाइन पैटर्न 16-चिह्न वर्णमाला पर लगाया हुआ, यह संकेत कि कन्वर्जन-क्लास पैटर्न अब हाउस स्टाइल बन चुका है।
  • .NET 6, 2021। X509Certificate2.CreateFromPem PEM को फ़र्स्ट-क्लास इनपुट बना देता है, और मैन्युअल कवच-स्ट्रिपिंग कोड का पूरा वर्ग आधुनिक रनटाइम पर वैकल्पिक हो जाता है।
  • .NET 9, नवंबर 2024। System.Buffers.Text.Base64Url सालों के कम्युनिटी रिक्वेस्ट के बाद आख़िर बॉक्स में उतरता है, और Microsoft.Bcl.Memory पैकेज .NET Framework 4.6.2 और ऊपर वाले लीगेसी कोडबेस के लिए इसे बैकपोर्ट करता है।
  • .NET 11, इस लेख लिखते समय प्रिव्यू में। अगला रिलीज़, 2026 के अंत में उम्मीद, मौजूदा टाइप्स में और Base64 सुविधा API और ओवरलोड जोड़ता है, और एर्गोनॉमिक सरफ़स की ओर धीमे-धीमे मार्च को जारी रखता है।

यह भी ध्यान में रखने लायक है: एन्कोडिंग खुद इन सबसे बहुत पुरानी है। जिस चीज़ को हम आज MIME Base64 कहते हैं, उसका पहला मानकीकृत इस्तेमाल 1987 का Privacy-Enhanced Mail प्रोटोकॉल था (RFC 989), MIME ने 1993 में 76-चर लाइन-रैप्ड रूप को मानक बनाया, और RFC 4648 ने 2006 में फ़ॉर्मैट को उसका आधुनिक, वर्णमाला-जागरूक विनिर्देश दिया, URL-सुरक्षित वेरिएंट समेत। C# ने इन सबका उत्तराधिकार लिया: 30 साल पुराने ईमेल फ़ॉर्मैट में जो लाइन-रैपिंग और पैडिंग की हर अजीबोगरीज़ आप मिलेंगे, वह अजीबोगरीज़ C# डिकोडर के लिए बस वही सोच कर बनाई गई थी।

रुचिकर C# तथ्य

  • सबसे छोटा स्मोक टेस्ट। "TWFu" Man में डिकोड होता है। तीन बाइट्स, बिना पैडिंग, बिना बहाने। यह C# में Base64 डीबगिंग का हैलो वर्ल्ड है, और चार चरों में पूरा हैप्पी पैथ चलाता है।
  • डाक-इतिहास वाला डिकोडर। व्हाइटस्पेस सहनशीलता इम्प्लेमेंटेशन की दुर्घटना नहीं, MIME से उत्तराधिकृत एक डिज़ाइन फैसला है: 76-चर लाइन-रैप्ड पूरा ईमेल बॉडी, अपने सारे CRLF जोड़ों समेत, Convert.FromBase64String के लिए वैध एकल आर्गुमेंट है। डिकोडर उसी फ़ॉर्मैट के लिए बना था जिसे ईमेल तीस साल से इस्तेमाल कर रहा है।
  • एक एरर, तीन कारण। क्लासिक FormatException मैसेज उन तीनों फ़ेलियर मोड्स की सूची देता है जो वह रिपोर्ट कर रहा हो सकता है (ख़राब चर, ज़्यादा पैडिंग, ग़लत जगह पैडिंग), और नहीं कहता कि कौन सा फायर हुआ। यह API सरफ़स का एकमात्र एरर मैसेज है जो मल्टीपल-चॉइस क्वेश्चन की तरह काम करता है।
  • जो थोड़ा झूठ बोलता है नेमस्पेस। System.Buffers.Text ऐसा लगता है जैसे टेक्स्ट प्रोसेसिंग के बारे में हो, लेकिन यह वाक़ई में बाइनरी-से-टेक्स्ट कन्वर्जन का घर है, बड़े-मोटे मायने में: Utf8Parser और Utf8Formatter, जो नंबर और डेट्स को सीधे UTF-8 में पार्स करते हैं, Base64 क्लासों के ठीक पड़ोस में बसे हैं।
  • पैडिंग परिवार के एक पक्ष पर वैकल्पिक है। Base64Url क्लास AQIDBA (छह चर, बिना पैडिंग) और AQIDBA== (पैडिंग के साथ वही बाइट्स) को वही चारों बाइट्स में डिकोड करती है, जबकि क्लासिक डिकोडर सिर्फ़ पैडेड रूप मानता है। दो डिकोडर, दो करार, एक रनटाइम।
  • जो स्ट्रिंग्स होनी नहीं चाहिए। C# स्ट्रिंग में कानूनी रूप से NUL बाइट्स हो सकते हैं, इसलिए डिकोड बाइनरी का Encoding.UTF8.GetString एक "स्ट्रिंग" बना सकता है जो कंट्रोल चर से भरी हो, जिसे कंसोल, आपका CSV राइटर, और दुनिया के आधे JSON लाइब्रेरियाँ हर एक अलग ढंग से संभालेगा। टाइप सिस्टम इसे इज़ाज़त देता है; इकोसिस्टम ज़्यादातर नहीं।
  • अच्छी हालत में 1.1 का वारिसा। Convert.FromBase64CharArray की अप्रैल 2003 से वही थ्री-पैरामीटर साइग्नेचर है, जेनेरिक्स क्रांति, Span क्रांति और URL-सुरक्षित क्रांति को बखूबी झेलते हुए, एक भी ओवरलोड जुड़े बिना। C# का char-ऐरे दौर ख़त्म नहीं हुआ; वह बस आराम कर रहा है।
  • ग्यारह चर, आठ बाइट्स। YouTube के वीडियो पहचान चिह्न बिना पैडिंग के base64url हैं: 11 चर जो 8 बाइट्स में डिकोड होते हैं। Base64Url.GetMaxDecodedLength(11) आपको वह 8 बताता है, और डिकोड एक पंक्ति है, जो दिन का अच्छा ख़त्म करने का तरीक़ा है अगर आप वह तरह के इंसान हैं जो वह तरह की चीज़ें लिखते हैं।

दूसरी दिशा

यही थी डिकोडर वाली ओर, और यहीं अधिकांश दर्द बसता है, क्योंकि डिकोडिंग वही जगह है जहाँ आप दूसरों का डेटा मिलते हैं: उनकी पैडिंग के फैसले, उनके लाइन ब्रेक्स, उनकी वर्णमालाएँ, उनके टोकन। उल्टी दिशा, अपने बाइट्स को उठाकर उन्हें Base64 में पैक करना, एक शांत समस्या है, जिसके अपने फैसले करने हैं और अपने फँसाव भी। C# में Base64 एन्कोडिंग, 76-चर के सवाल से लेकर URL-सुरक्षित टोकन तक, नीचे लिंक किए गए साथी लेख में गहराई से कवर है, और एक बार यह जान लेने के बाद कि क्या ढूँढना है, यह एक छोटी, संतोष देने वाली पढ़ाई है।

अंतिम अपडेट: 2026-09-08

संबंधित लेख: C# (CSharp) में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड