PowerShell में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड
आपके पास एक स्ट्रिंग, फ़ाइल, सर्टिफिकेट या टोकन है, और वायर के दूसरी तरफ़ वाला इसे अक्षरों और अंकों की एक लंबी धार के रूप में माँगता है: प्रिंट होने लायक, ईमेल, URL या कॉन्फ़िग फ़ाइल में पेस्ट हो सके, और एक भी बिनरी बाइट न हो जो ट्रांसपोर्ट तोड़ दे। यह ही Base64 है। यह एक अनुवाद है, न कि कंप्रेशन, और न ही कोई लॉक: तीन इनपुट बाइट्स चार आउटपुट चर बन जाते हैं, इसलिए जो टेक्स्ट आप भेजते हैं वह अपने स्रोत से करीब 33% बड़ा निकलता है, 64 चरों की वर्णमाला के साथ, जिसमें आख़िरी पैडिंग के लिए बराबर का चिह्न भी जुड़ा है।
इस साइट का होम पेज वर्णमाला, बिट गणित और वैरिएंट्स को विस्तार से ढकता है। यह लेख एन्कोडिंग की दिशा को PowerShell की तरफ़ से ढकता है: वह एक ही .NET मेटोड जिसे आप कॉल करेंगे, वह गायब क़दम जो हर किसी को पहली स्क्रिप्ट में गिराता है, प्रोटोकॉल-दर-प्रोटोकॉल अलग-अलग लाइन-व्रैप रिवाज़, URL-सुरक्षित वर्णमाला, और वह कुछ असली काम जहाँ PowerShell में एन्कोडिंग सावधान को इनाम देती है और लापरवाह को दंड।
मेटोड और गायब क़दम
PowerShell के पास अपना कोई Base64 कमांडलेट नहीं है। यह काम एक मेटोड से होता है, जो 2003 में .NET Framework 1.1 से .NET फ्रेमवर्क का हिस्सा है, यानी PowerShell के अपने रिलीज़ होने से तीन साल पहले:
$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello")
[System.Convert]::ToBase64String($bytes)
# SGVsbG8=
यह पूरा API है: एक बाइट्स ऐरे अंदर, एक स्ट्रिंग बाहर, हर ऑपरेटिंग सिस्टम पर हर PowerShell में, क्योंकि यह बस .NET है। गायब क़दम है उदाहरण की पहली लाइन, और यहीं शुरुआती लोग अपना पहला घंटा खो देते हैं। मेटोड आपकी स्ट्रिंग नहीं लेता। वह बाइट्स लेता है, और "मेरी स्ट्रिंग किन बाइट्स का मतलब रखती है" यह एक एन्कोडिंग प्रश्न है जिसे सिर्फ़ आप जवाब दे सकते हैं। यही वह अनुबंध है उन ओवरलॉड्स का जो आप PowerShell से असल में कॉल कर सकते हैं:
| आप क्या पास करते हैं | आपको क्या मिलता है |
|---|---|
byte[] |
स्टैंडर्ड Base64 की एक लंबी लाइन, जहाँ लंबाई माँगती है वहाँ = पैडिंग के साथ |
byte[] साथ में InsertLineBreaks |
वही डेटा, 76 चर पर टूटा, लाइनों के बीच CRLF |
byte[], ऑफ़सेट, काउंट |
सिर्फ़ ऐरे का माँगा गया टुकड़ा, एन्कोड किया हुआ |
ऐसी स्ट्रिंग जैसे "Hello" |
एक कन्वर्ज़न एक्सेप्शन। PowerShell खुद स्ट्रिंग को बाइट्स ऐरे में नहीं बदल सकता |
$null |
एक ArgumentNullException, जो आपको MethodInvocationException के अंदर सँवाँकर मिलती है |
उस टेबल में क्या नहीं है, नोटिस करें: कोई ओवरलॉड जो "इस टेक्स्ट को एन्कोड करो" बोलता है, वह नहीं है। PowerShell में टेक्स्ट एन्कोड करना हमेशा दो-क़दम का काम है। आप एन्कोडिंग का फैसला करते हैं, आप बाइट्स बनाते हैं, और बस तब Base64 मेटोड बातचीत में आता है। स्क्रिप्ट में ये दोनों फैसले दिखने में अलग रखें, क्योंकि दूसरा दिखता ही नहीं और पहली जगह में ही बग रहते हैं।
टेक्स्ट एन्कोड करना: पहले एन्कोडिंग चुनें
आधुनिक इंटरनेट पार करने वाली किसी भी चीज़ के लिए सुरक्षित डिफ़ॉल्ट है UTF-8। वेब API, JSON, JWTs, ब्राउज़र या सर्वर के लिखी हर चीज़ जो पिछले दशक में बनाई गई, Base64 के नीचे UTF-8 बाइट्स की उम्मीद करेगी, और दो-क़दम पैटर्न ही वह आदत है जो आपको बनानी चाहिए:
$text = "Hello, PowerShell!"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$encoded = [System.Convert]::ToBase64String($bytes)
# SGVsbG8sIFBvd2VyU2hlbGwh
जब आप किसी अलग एन्कोडिंग की ओर हाथ बढ़ाते हैं, तो आमतौर पर आप किसी लीगेसी सिस्टम की सेवा कर रहे होते हैं, और नीचे की टेबल प्राक्टिकल गाइड है:
| एन्कोडिंग | कब इस्तेमाल करें | ग़लत चुनने पर |
|---|---|---|
UTF8 |
वेब API, JSON, JWTs, आधुनिक सब कुछ। डिफ़ॉल्ट चुनाव | दूसरी तरफ़ का डिकोडर आपके टेक्स्ट की जगह मोज़िबेक देखता है |
Unicode (UTF-16LE) |
कन्ज़्यूमर कोई Windows या .NET कंपोनेंट है जो .NET स्ट्रिंग्स को एन्कोड करता है, या -EncodedCommand |
आपका पेलोड कन्ज़्यूमर की उम्मीद से दोगुना लंबा होता है, और हैरानियों से भरा |
ASCII |
क्लासिक 7-बिट प्रोटोकॉल, जैसे HTTP Basic क्रेडेंशियल्स | मान 127 से ऊपर की हर चीज़ एन्कोडिंग होने से पहले ही बदल दी जाती है |
Latin1 |
लीगेसी यूरोपीय सिस्टम, जो UTF-8 से पहले के हैं | हर चर पर एक बाइट, और हर नॉन-Latin-1 चर प्रश्न-चिह्न बन जाता है |
एक उपयोगी डीबगिंग चाल दोनों दिशाओं में काम करती है: Base64 की पैडिंग और लंबाई आपको बताती हैं कि कितने बाइट्स एन्कोड किए गए थे, और डिकोड टेक्स्ट की शक्ल बताती है कि वह किस दुनिया से आया - दो-बाइट वाली या एक-बाइट वाली। एक ऐसा पेलोड जो साइज़ में शक़-शक़ से गुणज हो और सामान्य चरों के बीच-बीच में खाली दिखने वाले चरों से भरा हो, वह आमतौर पर UTF-16 है जिसने UTF-8 का कॉस्ट्यूम पहना है, या उसका उल्टा।
UTF-16 की हैरानी
PowerShell स्ट्रिंग्स को अंदर से UTF-16 में स्टोर करता है, और यह तथ्य Base64 काम में एक खास, बहुत आम जगह से बह कर आता है: आप ऐसे कन्ज़्यूमर के लिए Base64 बना रहे हैं जो खुद कोई .NET या Windows कंपोनेंट है, और Unicode से एन्कोड कर रहे हैं, क्योंकि .NET स्ट्रिंग्स वही हैं। कुछ कन्ज़्यूमर्स के लिए यह सही इंतुआन है, और बाक़ी हर एक के लिए साइज़ दोगुना करने वाली ग़लती। वही चार दिखने वाले चर, दो एन्कोडिंग:
$same = "Café"
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($same))
# Q2Fmw6k= पाँच बाइट्स
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($same))
# QwBhAGYA6QA= आठ बाइट्स
वही टेक्स्ट, दोगुना साइज़, और दोनों स्ट्रिंग्स एक-दूसरे की जगह नहीं चल सकतीं: कन्ज़्यूमर जो एक की उम्मीद करे और दूसरा पाए, वह ज़ोर-शोर से फेल नहीं होगा; बस गार्बेज पढ़ लेगा। वह नियम जो आपको इससे काटने से बचाता है: एन्कोडिंग को प्रोटोकॉल का हिस्सा समझें, किसी लोकल डिटेल नहीं। अगर मिलाने वाला सिस्टम ब्राउज़र, REST API या आधुनिक सर्वर है, तो वह UTF-8 है, जब तक कि डॉक्यूमेंटेशन कुछ और न कहे। अगर वह PowerShell होस्ट खुद है -EncodedCommand के ज़रिए, या Windows-अकेले पाइपलाइन में .NET स्ट्रिंग, तो वह UTF-16LE है। जब प्रोटोकॉल कुछ न कहे, तो दूसरी तरफ़ से पूछें कि वह GetString को क्या खिलाएगा, क्योंकि बस वह ही प्रश्न है जो असल में तय करता है।
अंक, बाइट्स, और बाक़ी सब
मेटोड की घोषणा बाइट्स ऐरे लेने की है, लेकिन PowerShell की टाइप-कन्वर्ज़न इसमें बड़ा दिल की है कि क्या-क्या इसकी गिनती में आता है, और किनारों को जानना आपकी हैरानियों से बचाता है:
[System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# AQID+vs=
[System.Convert]::ToBase64String([char[]]"Café")
# Q2Fm6Q== हर चर पर एक बाइट, चर का मान अंक के रूप में
[System.Convert]::ToBase64String([int[]](72, 101, 108, 108, 111))
# SGVsbG8=
[System.Convert]::ToBase64String(123)
# ew== जहाँ पूरा ऐरे माँगा जाए, वहाँ एक अकेला अंक भी स्वीकार हो जाता है
उस ब्लॉक के दो किनारे ध्यान देने लायक हैं। चरों के ऐरे हर चर को उसके अंकीय मान से एक बाइट में बदल देते हैं, जो Latin टेक्स्ट के लिए बिल्कुल वही है जो इस चाल को इस्तेमाल करने वाले लीगेसी सिस्टम उम्मीद करते हैं, और उससे आगे की हर चीज़ के लिए यह ख़ामोशी से ग़लत बाइट्स पैदा कर देता है। 255 से बड़ा इंटीजर वह किनारा है जो ज़ोर-शोर से फेल होता है: PowerShell की बाइट-कन्वर्ज़न 0-255 से बाहर के मानों को एक्सेप्शन के साथ मना कर देती है, इसलिए 256 स्क्रिप्ट को कास्ट पर ही रोक देता है, ख़ामोशी से आपके डेटा को भ्रष्ट होने से पहले। अगर आपका स्रोत अंक हैं, तो कास्ट खुलकर बताएं: [byte[]](1, 2, 3) बिल्कुल वही कहता है जिसका मतलब रखता है।
मेटोड को स्ट्रिंग पास करें तो आपको वही ज़ोरदार ट्रीटमेंट, अलग कारण से, मिलती है: यह जानने का कोई रास्ता नहीं है कि स्ट्रिंग किन बाइट्स का मतलब रखती है, इसलिए PowerShell की कन्वर्ज़न इंजन हार मान लेता है। $null पास करें तो .NET कुछ करने से पहले ही एक्सेप्शन फेंक देता है। दोनों सही व्यवहार हैं, और दोनों ही वजह हैं कि पहले सेक्शन का दो-क़दम पैटर्न ही वह एकमात्र पैटर्न है जो रखने लायक है।
लाइन व्रैपिंग: 76, 64, और कोई नहीं
डिफ़ॉल्ट रूप से एन्कोडर एक लंबी लाइन देता है, आप जितना भी डेटा दें। कुछ किलोबाइट की फ़ाइल के लिए यह ठीक है। लेकिन ऐसे डेटा के लिए जिसे इंसान पढ़ेगा, ईमेल में पेस्ट होगा, या सोर्स-कंट्रोल डिफ़ में तुलना होगी, आठ मलियन चरों की दीवार एक प्राक्टिकल मुसीबत है, और रिवाज़ है रैप करना। PowerShell और वे प्रोटोकॉल जो वह संभालता है, तीनों चौड़ाईं जानते हैं, और ये एक-दूसरे की जगह नहीं चलतीं:
| चौड़ाई | किसकी उम्मीद है | लाइन एंडिंग |
|---|---|---|
| 76 चर | MIME, मेल, और ज़्यादातर टेक्स्ट ट्रांसपोर्ट। InsertLineBreaks का डिफ़ॉल्ट |
CRLF |
| 64 चर | PEM फ़ाइलें: सर्टिफिकेट, प्राइवेट की, और -----BEGIN परिवार का बाक़ी |
रिवाज़ से LF |
| कोई नहीं | API, टोकन, कॉन्फ़िग फ़ाइलें, जहाँ पेलोड मशीन संभालती है | लाइन ही नहीं |
बिल्ट-इन व्रैप एक पैरामीटर का बदलाव है, और बिल्कुल यह ही वह है जो मेल-शैली के पेलोड के लिए चाहिए:
$text = "The quick brown fox jumps over the lazy dog. Base64 output arrives wrapped at different widths depending on who is reading it."
$wrapped = [System.Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes($text),
[Base64FormattingOptions]::InsertLineBreaks)
# हर लाइन पर 76 चर, बीच में CRLF, बिल्कुल जैसा MIME उम्मीद करता है
PEM बिल्ट-इन का इस्तिثना है, क्योंकि OpenSSL और पूरा -----BEGIN इकोसिस्टम 64 चर पर रैप करता है, और कोई .NET फ़्लैग वह चौड़ाई नहीं देता। लूप छोटा है और यही स्टैंडर्ड रीसिपी है:
$der = [System.IO.File]::ReadAllBytes("./certificate.der")
$b64 = [System.Convert]::ToBase64String($der)
$lines = for ($i = 0; $i -lt $b64.Length; $i += 64) {
$b64.Substring($i, [Math]::Min(64, $b64.Length - $i))
}
$pem = @("-----BEGIN CERTIFICATE-----") + @($lines) + @("-----END CERTIFICATE-----")
Set-Content -Path "./certificate.pem" -Value ($pem -join "`n")
चौड़ाई का अर्थ रखने का कारण यह है कि Base64 के चार चरों के ग्रुप लाइन ब्रेक्स को मानते नहीं, इसलिए डिकोडर ब्रेक्स को पूरी तरह अनदेखा भी कर सकता है, या ज़ोर-शोर से लागू भी। इस साइट के इस्तेमाल वाला डिकोडर इन्हें अनदेखा करता है, लेकिन सख़्त कन्ज़्यूमर - और सर्टिफिकेट और मेल की दुनिया में ऐसे केसेस की कमी नहीं - एक अनपेक्षित ब्रेक को पराया चर समझकर पेलोड अस्वीकार कर देते हैं। जब आप चौड़ाई चुनते हैं, तो आप कन्ज़्यूमर के साथ एक अनुबंध कर रहे होते हैं, और स्क्रिप्ट में एक कमेंट जो बताए कि आप किसके साथ करार कर रहे हैं, वह डलर-डलर की क़ीमत रखता है।
base64url: दो चर और एक पैडिंग फैसला
स्टैंडर्ड Base64 का प्लस और स्लैश URL के अंदर बस परसेंट-एन्कोडिंग के बाद ही क़ानूनी हैं, और बराबर चिह्न की पैडिंग किसी फ़ील्ड सेपरेटर जैसी पढ़ी जाती है। इसलिए RFC 4648 ने एक URL- और फ़ाइल-नाम-सुरक्षित वर्णमाला दी: वही 64 चर, सिर्फ़ इतना फ़र्क़ कि प्लस की जगह हाइफ़न आया और स्लैश की जगह अंडरस्कोर, और पैडिंग आमतौर पर छोड़ दी जाती है, क्योंकि डेटा की लंबाई उसे ज़रूरी नहीं रखती। हर API टोकन और JWT जिससे आपने कभी काम किया है, इसी वैरिएंट में लिखा है, जिसे स्टैंडर्ड base64url कहने पर ज़ोर देता है, बस base64 नहीं।
PowerShell का स्टैंडर्ड एन्कोडर स्टैंडर्ड वर्णमाला देता है, इसलिए base64url में बदलाव है दो चरों का बदले-बदल और एक पैडिंग फैसला:
$bytes = [System.Text.Encoding]::UTF8.GetBytes("Париж encoded 大阪")
$standard = [System.Convert]::ToBase64String($bytes)
$standard
# 0J/QsNGA0LjQtiBlbmNvZGVkIOWkp+mYqg== स्टैंडर्ड वर्णमाला, पैडिंग समेत
$url = $standard.Replace("+", "-").Replace("/", "_").TrimEnd("=")
$url
# 0J_QsNGA0LjQtiBlbmNvZGVkIOWkp-mYqg URL-सुरक्षित शक्ल, पैडिंग निकाली गई
base64url की दुनिया में पैडिंग निकाल देना सुरक्षित है, क्योंकि कन्ज़्यूमर स्ट्रिंग की लंबाई से फिर से गिन लेता है कि पैडिंग क्या होती। हर जगह ऐसा सच नहीं है, इसलिए फैसला खुलकर करें: टोकन, JWT सेगमेंट्स और URL में बेठने के लिए पैडिंग छोड़ें, सख़्त स्टैंडर्ड-वर्णमाला कन्ज़्यूमर को फ़ीड करने वाली हर चीज़ के लिए रखें, और लिख लें कि आपने कौन-सा चुना। .NET रनटाइम में इस वर्णमाला के लिए एक खास क्लास है, System.Buffers.Text.Base64Url (.NET 9 में जुड़ी), मेटोड्स के साथ जो ReadOnlySpan<T> पैरामीटर के चारों तरफ़ बनी हैं। वर्तमान PowerShell (7.4 और उससे आगे, जब वह किसी .NET वर्ज़न पर चल रहा हो जिसमें वह क्लास हो) इन्हें असल में सीधे हाथ से कॉल कर सकता है - [System.Buffers.Text.Base64Url]::EncodeToString($bytes) आज काम करता है, क्योंकि मेटोड बाइंडर अब ऐरे आर्गुमेंट को अप्रत्यक्ष रूप से स्पैन में बदल देता है - लेकिन हर बार जब स्क्रिप्ट को Windows PowerShell 5.1, किसी पुरानी PowerShell 7.x रिलीज़, या .NET 9 से पहले के रनटाइम वाले होस्ट पर चलनी हो, तब हाथ बस दो-चर बदले-बदल की ओर बढ़ाएं, और वह हर एक में काम करता है।
JWT बनाना
JSON Web Token base64url का फ़्लैगशिप असल-दुनिया उपयोग है, और एन्कोडिंग पाइपलाइन का एक अच्छा पूरा टेस्ट भी, क्योंकि JWT तीन एन्कोडेड सेगमेंट हैं जो डॉट से जुड़े होते हैं: हेडर, पेलोड, और सिग्नेचर। पहले दो base64url में कॉम्पैक्ट JSON हैं, और तीसरा है पहले दो के बिल्कुल सटीक टेक्स्ट पर एक हैश का बिनरी आउटपुट। यहाँ एक पूरा HS256 टोकन है, PowerShell में शुरुआत से अंत तक बनाया गया:
$header = @{ alg = "HS256"; typ = "JWT" } | ConvertTo-Json -Compress
$payload = @{ sub = "1234567890"; name = "John Doe"; iat = 1516239022 } | ConvertTo-Json -Compress
function UrlEncode64([byte[]]$bytes) {
$standard = [System.Convert]::ToBase64String($bytes).TrimEnd("=")
return $standard.Replace("+", "-").Replace("/", "_")
}
$left = (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($header))) + "." + (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($payload)))
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes("secret"))
$signature = UrlEncode64 ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($left)))
$jwt = $left + "." + $signature
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UifQ.6MWZy9doHbfyomJd4soTRUQft7PmRM2EyxxT3SLoiyE
# PowerShell 7.4 पर; सेगमेंट्स का अंदरूनी की-ऑर्डर - और इसलिए सिग्नेचर भी - वर्ज़न के हिसाब से अलग हो सकता है
सिग्नेचर सेगमेंट पर नज़र डालें: base64url वर्णमाला की एक धार, वही जहाँ प्लस हाइफ़न की शक्ल में और स्लैश अंडरस्कोर की शक्ल में दिखता है। उस उदाहरण के बारे में तीन बातें आपकी प्रोडक्शन इनसिडेंट से बचाएँगी। पहली: सिग्नेचर का हिसाब पहले दो के बिल्कुल सटीक JSON टेक्स्ट पर चलता है, उनकी की-ऑर्डर और स्पेसिंग समेत, इसलिए जो JSON आप साइन करते हैं और जिसके बराबर आप जाँच करते हैं, दोनों को बाइट-दर-बाइट एक जैसे होने चाहिए। PowerShell का ConvertTo-Json की-ऑर्डर आपकी जगह तय करता है, और वह आपके नियंत्रण में नहीं है, इसलिए टोकन के सेगमेंट्स का क्रम हाथ से न बदलें और साइनिंग और जाँच के बीच उन्हें रिफ़ॉर्मेट न करें। दूसरी: -Compress सजावटी नहीं है: ऐसा टोकन जिसके हेडर या पेलोड में एक भी स्पेस हो, वह कभी कॉम्प्लाइंट इम्प्लीमेंटेशन के मुक़ाबले में सत्यापित नहीं होगा, क्योंकि स्टैंडर्ड शक्ल कॉम्पैक्ट है। तीसरी: टाइमस्टैम्प iat Unix इपॉक से सेकंड हैं, और Get-Date से बने पेलोड को कन्वर्ट किए बिना छोड़ दिया तो वह सालों की दूरी से बाहर रहेगा। डिकोड की दिशा - किसी और के बने टोकन में झाँकना - sister साइट के संबंधित लेख में पूरी तरह कवर है।
फ़ाइलें और बाइट स्ट्रीम
फ़ाइलें ही सबसे आम पेलोड हैं, और पाइपलाइन छोटी है। फ़ाइल को बाइट्स के रूप में पढ़ें, एन्कोड करें, टेक्स्ट लिखें। दो लाइनें जो असल में मायने रखती हैं: पढ़ना, जो बाइट-रीड होना ही चाहिए, और लिखना, जो आमतौर पर आख़िर में नई लाइन नहीं जोड़नी चाहिए:
$bytes = [System.IO.File]::ReadAllBytes("./photo.png")
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path "./photo.b64" -Value $encoded -NoNewline
$encoded.Length
# वह टेक्स्ट साइज़ जो आप भेजने वाले हैं
दो प्राक्टिकल नोट्स। पहला गणित है: Base64 सब कुछ बड़ा कर देता है, और 10 मेगाबाइट की फ़ाइल के लिए जो टेक्स्ट आप भेजते हैं वह करीब 13.4 मेगाबाइट होता है। अगर ट्रांसपोर्ट में साइज़ की हद है, या यह टेक्स्ट ईमेल बॉडी या URL में जा रहा है, तो हिसाब एन्कोड से पहले करें, एरर के बाद नहीं। दूसरा आख़िरी नई लाइन है: Set-Content डिफ़ॉल्ट रूप से एक जोड़ देता है, और भले ही इस साइट के डिकोडर और ज़्यादातर आधुनिक डिकोडर उसे अनदेखा करें, कुछ सख़्त कन्ज़्यूमर नहीं करते। -NoNewline आपको कुछ नहीं लूटता, और सवाल ही हटा देता है।
PowerShell 6 और नए वर्ज़न एक दूसरा पढ़ने का रास्ता देते हैं जो भाषा के अंदर ही रहता है: Get-Content -AsByteStream -Raw फ़ाइल को एक ही कॉल में एक बाइट्स ऐरे के रूप में लौटाता है, जो .NET के ReadAllBytes की साफ़ बिकल्प शक्ल है, और इस काम के लिए बिल्कुल एक जैसा व्यवहार करता है। Windows PowerShell 5.1, जिसमें -AsByteStream नहीं है, वहाँ .NET वाला पढ़ना ही एकमात्र विकल्प है, और वही वह है जो शेल के हर वर्ज़न पर एक जैसा व्यवहार करता है।
सर्टिफिकेट: PEM और PFX से टेक्स्ट तक
रोज़मर्रा के ऑपरेशन में सर्टिफिकेट सबसे भारी एन्कोडिंग नागरिक हैं, क्योंकि डिप्लॉयमेंट इन्हें टेक्स्ट की शक्ल में ढोना पसंद करते हैं। PEM सर्टिफिकेट कवच लाइनों के बीच एक रैप किया हुआ Base64 बॉडी है, और व्रैपिंग सेक्शन की रीसिपी ही पूरा एक्सपोर्ट है:
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([System.IO.File]::ReadAllBytes("./certificate.der"))
$cert.Subject
# CN=example.org
$b64 = [System.Convert]::ToBase64String($cert.RawData)
# सर्टिफिकेट की बिनरी शक्ल की एक लंबी लाइन
PFX फॉर्मेट दूसरा कामगार है: एक बिनरी फ़ाइल जिसमें सर्टिफिकेट अपनी प्राइवेट की के साथ साथ बैठा है, और बस इसीलिए यही वह फॉर्मेट है जो आप डिप्लॉयमेंट स्क्रिप्ट्स और कॉन्फ़िग स्टोर्स के अंदर Base64 टेक्स्ट की शक्ल में मिलते देखेंगे। इसे एन्कोड करना पिछले सेक्शन की सादा फ़ाइल-पाइपलाइन है, और PowerShell 7 में पढ़ने की दिशा एक कमांडलेट का काम है:
$pfxBytes = [System.IO.File]::ReadAllBytes("./certificate.pfx")
$pfxB64 = [System.Convert]::ToBase64String($pfxBytes)
# बंडल की टेक्स्ट शक्ल, कॉन्फ़िग फ़ाइल के लिए तैयार
Get-PfxCertificate -FilePath "./certificate.pfx" -Password (ConvertTo-SecureString "secret" -AsPlainText -Force)
# चालू सर्टिफिकेट, कोई मैन्युअल डिकोड ज़रूरी नहीं
सुरक्षा का एक वाक्य, साफ़ कहे, क्योंकि Base64 उल्टा मानने को दावत देता है: Base64 में PFX एक टेक्स्ट में बसी प्राइवेट की है। एन्कोडिंग सीक्रेट की शक्ल बदलती है, उसकी गोपनीयता की नहीं, इसलिए चैट विंडो, टिकट या कॉमिट में पेस्ट किया हुआ Base64 PFX वही है जो चैट विंडो, टिकट या कॉमिट में पेस्ट की गई प्राइवेट की। टेक्स्ट शक्ल को बिल्कुल उतनी ही सावधानी से पकड़ें जितनी बिनरी शक्ल को, और दोनों की जगह सर्टिफिकेट स्टोर या सीक्रेट्स मैनेजर को प्राथमिकता दें।
Basic Auth, Data URI, और पुरानी आदतें
Base64 उस स्टैंडर्ड्स दस्तावेज़ से भी पुराना है जिसने इसे नाम दिया। 1996 के MIME परिवार के RFCs ने इसे ईमेल में डाला, और HTTP Basic ऑथेंटिकेशन ने इसे शुरुआती वेब के हर हेडर-आदान-प्रदान में पका दिया, जहाँ क्लाइंट आज भी क्रेडेंशियल जोड़ी को एक Base64 स्ट्रिंग के रूप में एन्कोड करता है:
$credential = [System.Text.Encoding]::UTF8.GetBytes("alice:s3cret!")
[System.Convert]::ToBase64String($credential)
# YWxpY2U6czNjcmV0IQ==
# भेजा जाता है: Authorization: Basic YWxpY2U6czNjcmV0IQ==
यहाँ सही वर्णमाला स्टैंडर्ड है, प्लस और स्लैश समेत, क्योंकि हेडर URL नहीं है और उसे सुरक्षित वर्णमाला की ज़रूरत नहीं। यही मेकानिज़म data URI में भी दिखता है, जिस तरह कोई दस्तावेज़ अपनी बिनरी इनलाइन एम्बेड करता है, और शक्ल है एक साधा प्रीफिक्स और बाइट्स के स्टैंडर्ड Base64 का जोड़:
$dataUri = "data:application/octet-stream;base64," + [System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# data:application/octet-stream;base64,AQID+vs=
ये दोनों आदतें जानने लायक हैं, कम इसलिए कि आप इनको बनाएँगे, ज़्यादा इसलिए कि आप इनसे मिलेंगे: जब कोई हेडर या लिंक अंदर लंबी Base64 धार रखता हो, तो पहली जाँच इन दो फॉर्मेट्स की है, और दोनों एक साधे डिकोड की दूरी पर हैं उस चीज़ से जो वे दिखाते हैं। वही तो फॉर्मेट का सारा मक़सद है, और वही वजह है कि इस साइट का डिकोड पक्ष मौजूद है।
एन्कोडेड कमांड और Windows टूलबॉक्स
वर्ज़न 1.0 से PowerShell के पास एन्कोड करने का एक बिल्ट-इन कारण रहता आया है: होस्ट का अपना -EncodedCommand पैरामीटर। आप pwsh को एक Base64 स्ट्रिंग सौंपते हैं, वह बाइट्स को UTF-16LE के रूप में डिकोड करता है, और परिणाम कमांड के रूप में चलता है। डॉक्यूमेंटेड उद्देश्य है ऐसी कमांड जो बाहरी शेल की क्वोटिंग से लड़ती हैं, और एन्कोडिंग की तरफ़ दो लाइन है:
$command = "Write-Host 'Hello from the encoded side'"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgACcASABlAGwAbABvACAAZgByAG8AbQAgAHQAaABlACAAZQBuAGMAbwBkAGUAZAAgAHMAaQBkAGUAJwA=
pwsh -NoProfile -EncodedCommand $encoded
# Hello from the encoded side
एन्कोडिंग वाली लाइन को ध्यान से पढ़ें, क्योंकि यही वह है जिसे हर कोई ग़लत करता है: पेलोड UTF-16LE होना चाहिए, यानी Unicode एन्कोडिंग, UTF-8 नहीं। ग़लत वाली से एन्कोड करें तो होस्ट आपके बाइट्स को फिर भी UTF-16LE के रूप में डिकोड कर लेता है और मोज़िबेक से बनी कमांड चला देता है, और जो एरर बनता है वह उस ग़लती की पक्की तस्वीर है। डिकोड लेख वह विफलता पूरी तरह कवर करता है, और इस तरफ़ का इलाज एक ही शब्द है: Unicode।
भाषा के बाहर, नेटिव टूल हर एक अपने ख़ामोश एन्कोडिंग फैसले के मालिक हैं। Windows पर certutil -encode infile outfile.b64 स्टैंडर्ड Base64 फ़ाइल बनाता है, जिसमें PEM की उम्मीद वाली कवच लाइनें होती हैं, -f मौजूदा आउटपुट ओवरराइट करता है, और याद रखने लायक फ़्लैग है -unicodetext, जो certutil को आउटपुट फ़ाइल Unicode में लिखवाता है (Microsoft के डॉक्स के मुताबिक: "आउटपुट फ़ाइल Unicode में लिखें") - एक स्विच जो एन्कोडिंग फैसला छुपा लेता है। Linux पर क्लासिक यूटिलिटी है base64 -w 0 file, जहाँ -w 0 वह भार-उठाता हिस्सा है: बिन इसकी GNU base64 76 चर पर रैप करके आपको MIME-शैली की फ़ाइल सौंपता है, जबकि आपको एक लाइन चाहिए थी। macOS पर BSD फ्लेवर को कोई ऐसा फ़्लैग नहीं चाहिए, क्योंकि वह डिफ़ॉल्ट रूप से एक बिना-टूटी लाइन देता है।
जब आउटपुट बहुत बड़ा हो तो एन्कोडिंग
रोज़मर्रा के साइज़ के लिए सब-पढ़ो-सब-एन्कोड-करो वाली पाइपलाइन ही तेज़ और सादी है, और वह सही रास्ता है तब तक जब तक कि फ़ाइल मेमोरी में आराम से संभालने से बड़ी न हो जाए, या डेटा डाउनलोड या सॉकेट से टुकड़ों में न आ रहा हो। तब डॉक्यूमेंटेड टूल है स्ट्रीमिंग जोड़ी: System.Security.Cryptography.ToBase64Transform, जो CryptoStream में सँवाँ होती है, जहाँ आप राव बाइट्स अंदर लिखते हैं और बाहर Base64 टेक्स्ट आता है, और हर पल ज़िंदा सिर्फ़ एक छोटा बफ़र होता है:
$source = [System.IO.File]::OpenRead("./photo.png")
$destination = [System.IO.File]::Create("./photo.b64")
$transform = [System.Security.Cryptography.ToBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new($destination, $transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$buffer = New-Object byte[] 65536
while (($read = $source.Read($buffer, 0, $buffer.Length)) -gt 0) {
$stream.Write($buffer, 0, $read)
}
$stream.Dispose()
$source.Dispose()
$destination.Dispose()
वन-शॉट मेटोड से एक फ़र्क़ लिखने लायक है: स्ट्रीम एक लगातार लाइन देती है, बिल्कुल बिना व्रैप के, इनपुट कितना भी बड़ा हो। व्हिटस्पेस अनदेखा करने वाले डिकोडर को फ़र्क़ नहीं पड़ता, लेकिन अगर अंतिम गंतव्य PEM फ़ाइल है, तो बाद में रिज़ल्ट पर व्रैपिंग सेक्शन का 64-कॉलम लूप चलाएं। और C# में स्टैंडर्ड शक्ल वही ToBase64Transform + CryptoStream पैटर्न है जो C# लेख में दिखता है; PowerShell इसे सीधे चलाता है, ऊपर के अनुसार।
एन्कोडेड पेलोड कहाँ ग़लत होते हैं
- स्ट्रिंग एन्कोड करना, बाइट्स नहीं।
ToBase64String("Hello")एक कन्वर्ज़न एक्सेप्शन फेंकता है, यानी मेटोड आपको बता रहा है कि पहला फैसला, एन्कोडिंग, अभी तक नहीं हुआ। उसे स्क्रिप्ट में दिखने लायक बना दें और एरर गायब हो जाता है। - UTF-8 वादे के जगह UTF-16। पेलोड उम्मीद से दोगुना लंबा होता है और कन्ज़्यूमर गार्बेज पढ़ लेता है। एन्कोडिंग प्रोटोकॉल का हिस्सा है, और आधुनिक इंटरनेट की लगभग हर वायर पर प्रोटोकॉल कहता है UTF-8।
- 5.1 का पढ़ना। Windows PowerShell 5.1 BOM-रहित टेक्स्ट फ़ाइल को आपकी स्क्रिप्ट के देखने से पहले ही मशीन की ANSI कोड पेज से पढ़ लेता है, इसलिए UTF-8 स्रोत फ़ाइल एन्कोडिंग क़दम से पहले ही भ्रष्ट हो सकती है। 5.1 पर टेक्स्ट को एक्स्प्लिसिट UTF-8 रीड से पढ़ें और रिज़ल्ट के पहले चरों को जाँचें।
- ग़लत व्रैप चौड़ाई। MIME 76 माँगता है, PEM 64, API कोई नहीं, और सख़्त कन्ज़्यूमर अनपेक्षित लाइन ब्रेक को पराया चर मानता है। चौड़ाई कन्ज़्यूमर से चुनें और कमेंट में कह दें।
- बदले-बदल की ग़लत तरफ़ पैडिंग। बराबर चिह्न निकालना base64url टोकन के लिए सही है और स्टैंडर्ड पैडिंग की उम्मीद वाले कन्ज़्यूमर के लिए ग़लत। वर्णमाला का बदले-बदल और पैडिंग फैसला दो चुनाव हैं, एक नहीं।
- जिस पर साइन किया उसे रिफ़ॉर्मेट करना। JWT सिग्नेचर बिल्कुल सटीक JSON टेक्स्ट को ढकता है, की-ऑर्डर और स्पेसिंग समेत। क्लेम्स की जगहें बदलें या एक स्पेस जोड़ें, तो टोकन सत्यापित होना बंद कर देता है, और एरर मैसेज भी कारण के बिल्कुल कहीं नहीं आता।
- 255 से आगे के मान। इंटीजर को बाइट में बदलने पर व्रैपिंग की जगह एक्सेप्शन फेंका जाता है, इसलिए 256 स्क्रिप्ट को कास्ट पर रोक देता है। अगर आपका स्रोत डेटा अंक है, तो कास्ट खुलकर बताएं और ग़लती को वही बने रहने दें जो आपकी नज़र में आए।
- कॉस्ट्यूम पर भरोसा करना। Base64 न एन्क्रिप्शन है, न कंप्रेशन: यह एक अनुवाद है जो डेटा को तिहाई बढ़ाता है। Base64 में सिक्रेट, सादा टेक्स्ट में सिक्रेट है, और Base64 में फ़ाइल वह फ़ाइल है जो 33% ज़्यादा जगह माँगती है।
भरोसेमंद एन्कोडरों के लिए नियम
- बाइट्स को जानबूझकर बनाएं। किसी भी एन्कोडिंग स्क्रिप्ट की पहली लाइन खुलकर बताई
GetBytesया बाइट-रीड हो, कभी यह उम्मीद नहीं कि PowerShell स्ट्रिंग को सही बाइट्स में बदल देगा। - वर्णमाला और चौड़ाई का नाम उस कोड के बगल वाले कमेंट में लिखें जो चुनाव करता है: स्टैंडर्ड या base64url, 76 पर रैप, 64 पर, या बिल्कुल नहीं। पढ़ने वाला वह इंसान है जो छह महीने बाद स्क्रिप्ट पढ़ेगा, और वह इंसान आप हैं।
- टेक्स्ट फ़ाइल
-NoNewlineके साथ लिखें, जब तक कि कन्ज़्यूमर आख़िरी ब्रेक की ख़ास उम्मीद न करे, और लाइन एंडिंग (LF या CRLF) वह चुनें जो कन्ज़्यूमर का डॉक्यूमेंटेशन माँगता है। - बनते वक़्त राउंड ट्रिप का टेस्ट करें: एन्कोड करें, डिकोड करें, बाइट्स की तुलना करें। तीस सेकंड की
Compare-Object, दोनों बाइट्स ऐरे पर, एन्कोडिंग की ग़लती, व्रैप की ग़लती और बाइट-ऑर्डर की ग़लती को एक साथ पकड़ लेती है, जब तक कि कारण अभी ताज़ा हो। - साइज़ लॉग करें, पेलोड नहीं। पहले बाइट्स की गिनती और बाद में चरों की गिनती करीब 1.33 के अनुपात पर बैठनी चाहिए, और अगर न बैठे, तो साइज़ का मेल-न-भटना आपको बता देगा कि कहाँ देखें, बिना लॉग में कभी डेटा के।
PowerShell को अपना एन्कोडर कैसे मिला
PowerShell में Base64 एन्कोडिंग का सबसे छोटा सच्चा इतिहास यह है कि PowerShell ने कभी खुद का नहीं लिखा। जिस मेटोड को आप कॉल करते हैं, Convert.ToBase64String, वह 2003 में .NET Framework 1.1 के साथ निकला, और नवंबर 2006 के वर्ज़न 1.0 से हर PowerShell ने बस वह .NET एक्सपोज़ किया जिस पर वह चलता है। इस प्रोजेक्ट को बनते वक़्त Monad कहा जाता था, पहली बार जनता के सामने अक्टूबर 2003 में Professional Developers Conference में दिखाया गया, और रिलीज़ तक वह .NET एन्कोडर, जो वह कवर करता है, तीन साल पुराना और वेब ट्रैफ़िक संभाल चुका था।
फॉर्मेट उसी साल स्टैंडर्ड बना जब शेल लॉन्च हुआ। RFC 4648, जो अक्टूबर 2006 में प्रकाशित हुआ, ने वर्णमाला, पैडिंग के नियम, डिकोडिंग की सख़्ती और base64url वैरिएंट तय किए, और यह आज भी बिल्कुल वही व्यवहार बयान करता है जो .NET जोड़ी लागू करती है। उससे पहले आए MIME RFCs, 1996 के, ने 76-चर व्रैप को ईमेल में डाल दिया था, और बस इसीलिए आज भी वह चौड़ाई InsertLineBreaks का डिफ़ॉल्ट है। जब अगस्त 2016 में PowerShell Core के रूप में PowerShell ओपन-सोर्स और क्रॉस-प्लेटफ़ॉर्म बना, तो एन्कोडर बिना बदलाव के Linux और macOS तक आया, क्योंकि बदलने को कुछ ही था।
जो बाद में बदला, वह .NET में हुआ, और ज़्यादातर PowerShell की पहुँच से बाहर। रनटाइम को नई वर्ज़न में तेज़, स्पैन-आधारित Base64 हेल्पर मिले, जिनमें Base64Url क्लास और Try-प्रीफिक्स वाले डिकोड मेटोड्स भी शामिल हैं। स्पैन byref-समान टाइप्स हैं, और पुरानी PowerShell रिलीज़ असल में उनसे बंध ही नहीं पाती थीं, लेकिन वर्तमान PowerShell का मेटोड बाइंडर अब एक अप्रत्यक्ष ऐरे-से-स्पैन कन्वर्ज़न करता है, इसलिए इन्हें काफ़ी नए होस्ट वाली स्क्रिप्ट से कॉल किया जा सकता है। हर पुरानी चीज़ के लिए कम्युनिटी का जवाब है PowerShell Gallery का Microsoft.PowerShell.TextUtility मॉड्यूल, जिसका ConvertTo-Base64 उसी .NET मेटोड का कवर करता है, -Text पैरामीटर जोड़ता है जिसका डिफ़ॉल्ट UTF-8 है, और 76-कॉलम व्रैप के लिए -InsertBreakLines स्विच। अगर आपको कमांडलेट शक्ल ज़्यादा पसंद है तो Install-Module -Name Microsoft.PowerShell.TextUtility से इंस्टॉल करें, और ध्यान रखें कि मॉड्यूल अब अर्शिव है और इसकी देखभाल अब ज़ोर-शोर से नहीं होती, और यही एक और वजह है कि नई स्क्रिप्ट्स के लिए बिल्ट-इन मेटोड ही सिफ़ारिश बनी रहता है।
ये अंक और नाम याद रखने हैं
- हर तीन इनपुट बाइट्स चार आउटपुट चर बन जाते हैं, इसलिए एन्कोड डेटा मूल से करीब 33% बड़ा निकलता है, और पैडिंग कभी दो बराबर चिह्न से ज़्यादा नहीं होती।
- डिफ़ॉल्ट आउटपुट एक बिना-टूटी लाइन है।
InsertLineBreaks76 चर पर रैप करता है CRLF के साथ, 1996 का MIME रिवाज़। PEM 64 माँगता है, और कोई बिल्ट-इन फ़्लैग वह चौड़ाई नहीं देता। - base64url है स्टैंडर्ड Base64 जिसमें प्लस और स्लैश की जगह हाइफ़न और अंडरस्कोर आ गए हैं, पैडिंग आमतौर पर निकाल दी गई, और यह हर JWT और API टोकन की वर्णमाला है।
- "Café" UTF-8 में पाँच बाइट्स है और UTF-16LE में आठ। वही दिखने वाला टेक्स्ट, दोगुना साइज़, और दोनों एन्कोडिंग वायर पार एक-दूसरे की जगह नहीं चल सकतीं।
-EncodedCommandपहली PowerShell रिलीज़ से मौजूद है, और उसके पेलोड को UTF-16LE होना ज़रूरी है, UTF-8 नहीं। वह एकमात्र शब्द जो इस तरफ़ की सबसे आम ग़लती ठीक करता है, वह हैUnicode।certutil -encodeएन्कोडिंग फैसला-unicodetextके अंदर छुपा सकता है, और GNUbase64को 76-कॉलम व्रैप की जगह एक लाइन देने के लिए-w 0चाहिए।- .NET के स्पैन-आधारित Base64 हेल्पर, जिनमें
Base64Urlभी शामिल है, कभी PowerShell से पहुँच से बाहर थे, क्योंकि स्पैन byref-समान टाइप्स हैं जिनसे पुराना मेटोड बाइंडर बंध ही नहीं पाता था। वर्तमान PowerShell (7.4+, एक ऐसे .NET रनटाइम पर जिसमें वह क्लास हो) अब ऐरे आर्गुमेंट को स्पैन पैरामीटर के साथ बिना शिकायत से जोड़ देता है, इसलिए सीधा कॉल आज काम करता है - लेकिन दो चरों का बदला-बदला ही वह एकमात्र रीसिपी है जो हर वर्ज़न में काम करती है, पुराने-नए सब में। - एक अकेला बाइट, 123,
ew==में एन्कोड होता है: उस नियम का सबसे छोटा संभव उदाहरण कि आउटपुट की लंबाई आपको इनपुट की लंबाई बयान कर देती है।
तीर को पलट देना
इस लेख की हर चीज़ इसी बारे में है कि आपके हाथ का डेटा लेकर उसे Base64 स्ट्रिंग में बदलें। दर्पण का काम - स्ट्रिंग लेकर अपना डेटा वापस पाना - अपनी ख़ुद की समस्याओं की पूरी कतार रखता है: ऐसा डिकोडर जो चार तरह के व्हिटस्पेस को अनदेखा करता है, एक एरर मैसेज जो तीन गुनाहों को ढकता है, JWT जिसमें झाँका जाए, सर्टिफिकेट जिसका कवच उतारा जाए, और -EncodedCommand जिसे समझाया जाए। उस दिशा को अपनी पूरी जगह मिलती है, अपने फँदों और अपने इतिहास के साथ, sister साइट के संबंधित लेख में, PowerShell में Base64 डिकोडिंग, जो नीचे लिंक है।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: PowerShell में Base64 डिकोडिंग: एक सम्पूर्ण गाइड