28

I'm attempting to decode an attachment in an email file from base64 and save it to disk.

For testing purposes, this is my code. Where input.txt contains just the base64 encoded data, which is an HTML file.

$file = "C:\input.txt"
$data = Get-Content $file
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($data)) > out.html

The decoding works fine, and it generates a new file that contains all of the lines, and is visibly identical to the original attachment. The problem is that the output file is twice the size (actually (filesize*2)+6 bytes, in this case).

Am I decoding this improperly? I've also tried UTF8 instead of ASCII... same result.

user2766136
  • 541
  • 1
  • 5
  • 8

6 Answers6

26

Well I got it working. Who knew Out-File re-encoded to Unicode by default? Solved by doing the following:

$file = "C:\input.txt"
$data = Get-Content $file
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($data)) | Out-File -Encoding "ASCII" out.html
user2766136
  • 541
  • 1
  • 5
  • 8
20

On Windows 10, using PowerShell you can do:

certutil -decode in.b64 out.txt
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68
marc
  • 301
  • 2
  • 3
  • This is the best answer. System.Convert is ridiculously slow and never finishes with a 10Mb file. The same file takes about one second with certutil. – Sagebrush Gardener Mar 01 '23 at 16:20
19

This one-liner preserves the original encoding of the base64 encoded file, so it will work with binary files such as a PDF or ZIP. Change ".\input.txt" and output.bin as needed - this will take .\input.txt, base 64 decode it, and then write the bytes out to output.bin exactly as they were when the file was encoded.

$file = ".\input.txt"; [System.Convert]::FromBase64String((Get-Content $file)) | Set-Content output.bin -Encoding Byte
NYCdotNet
  • 4,500
  • 1
  • 25
  • 27
3

Old question, new answer. Solution that preserv newlines

// Generate Content
ssh-keygen -t rsa -b 2048 -f example_rsa
// Encode
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($(Get-Content -Path example_rsa -Encoding utf8 -Raw))) >> encoded
// Decode
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($(Get-Content -Path encoded -Encoding utf8 -Raw))) >> decoded
Random_Guy
  • 31
  • 1
  • Unfortunately, both commands will write the BOM to the file ... see https://stackoverflow.com/questions/5596982/using-powershell-to-write-a-file-in-utf-8-without-the-bom Use `[IO.File]::WriteAllLines`to avoid it. – Liviu Dec 01 '22 at 21:30
1

I know it's an old question, but i found another answer. You can use unicode at the base64 conversion, it will fit after.

$file = "C:\temp\input.txt"
$data = Get-Content $file
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($data)) > out.html
Stoffi
  • 595
  • 6
  • 9
1

This function should behave the same as the linux base64 command, at least in regards to encoding and decoding. It can also accept values from pipes.

function base64
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]
        $str,
        [switch]
        $decode = $false
    )
    if ($decode)
    {
        return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($str))
    }
    else
    {
        return [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($str))
    }
}
PS > base64 -decode YXNk
asd
PS > base64 YXNk
WVhOaw==
PS > echo YXNk | base64
WVhOaw==
PS > echo YXNk | base64 -decode
asd

Note that Get-Content returns an Object[] by default

PS > $z=Get-Content .\README.md; $z.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Piping this into the func above will only convert the first line.
To convert an entire file, instead use Get-Content -Raw

Get-Content -Raw README.md | base64
TeamDman
  • 649
  • 6
  • 26