Tag Archives: function

Encrypting strings with custom keys in powershell

Since the ConvertTo-SecureString is not really secure, and neither is the EncodedCommand (base64string) I made two short functions to encrypt and decrypt strings in order to send them across the void unharmed.

Function Encrypt-String{
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("String")]
    [String]$PlainTextString,
    
    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [byte[]]$EncryptionKey
)
    Try{
        $secureString = Convertto-SecureString $PlainTextString -AsPlainText -Force
        $EncryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $EncryptionKey

        return $EncryptedString
    }
    Catch{Throw $_}

}

this function accepts a string, a key and outputs the encryptedstring.

To encrypt call the function with a plaintext string and a key. the key must be a byte array with 16,24 or 32 bytes (128,192 or 256 bits)

Calling the function looks like this.

$string = @'
I am about to become encrypted!
'@

[Byte[]]$Key = 117,9,103,192,133,20,53,149,81,95,108,34,81,224,226,220,56,68,133,120,139,241,176,239,171,54,231,205,83,57,51,255

$EncryptedString = Encrypt-String -PlainTextString $string -Key $Key

The output becomes

PS> $encryptedString
76492d1116743f0423413b16050a5345MgB8AG8AVABnAGcAagArAFYAUgBoAGkAYwBOAFIAZgBTAGEAQQB1AGsAMQBmAHcAPQA9
AHwAZQBiADIAOAA4ADQAYwA5ADEAMABlAGMAOABmADUAOAAyAGIANQAzADIAOABjADIAOAAxADUAZgAxADYAMgAyAGQAMgA2AGUA
ZQA2ADYANAA2AGUAYQA0AGMAMgBmADMAYgAwADIAMgAxAGQAOQAxADcANwBhADgANwAxADcAZgBjADEANwAxADcANwA1ADgANwBh
ADMAMgA3AGEAMABmADcAYQAzADcAYQBiADgANAAwADkAOQA4ADgANgA1AGYAMwA5ADMANAA3ADkAZgAwAGIAZAA1AGQAZgA2AGEA
MABmADIANwBmADkANQBhAGYAOAAxAGYANgA0ADAAOAAxAA==

Now to the decrypt. This function needs the exact encryptedstring and the exact key that the string was encoded with.

Function Decrypt-String{
Param(
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("String")]
    [String]$EncryptedString,

    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [byte[]]$EncryptionKey
)
    Try{
        $SecureString = ConvertTo-SecureString $EncryptedString -Key $EncryptionKey
        $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)
        [string]$String = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

        Return $String
    }
    Catch{Throw $_}

}

To call this function simply input the encryptedString and key and you get the pre-encrypted string as the return value.

PS> Decrypt-String -EncryptedString $EncryptedString -EncryptionKey $Key
I am about to become encrypted!

Both functions accept value by pipeline so a Get-content can be used like this.

Get-Content C:\temp\encrypted.txt | Decrypt-String -EncryptionKey $Key

An overkill version of this is to double encode/encrypt. First convert the string to a base64 (encodedCommand style) then encode it with the key. This really adds no further security except to obfuscate the encrypted package and in case someone gets the key to decrypt they have to convert the encodedcommand to a string from base 64

Function Encrypt-String{
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("String")]
    [String]$PlainTextString,
    
    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [byte[]]$EncryptionKey
)
    Try{
        $bytes = [System.Text.Encoding]::Unicode.GetBytes($PlainTextString)
        $encodedCommand = [Convert]::ToBase64String($bytes)
        $secureString = Convertto-SecureString $encodedCommand -AsPlainText -Force
        $EncryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $EncryptionKey

        return $EncryptedString
    }
    Catch{Throw $_}

}


Function Decrypt-String{
Param(
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("String")]
    [String]$EncryptedString,

    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [byte[]]$EncryptionKey
)
    Try{
        $SecureString = ConvertTo-SecureString $EncryptedString -Key $EncryptionKey
        $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)
        [string]$String = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

        $data = @()
        [convert]::FromBase64String($encodedCommand) | ForEach-Object{
            If($_ -ne 0){
                $data += [char]$_
            }
        }

        Return ($data -join '')
    }
    Catch{Throw $_}

}

SQL queries in powershell

I was trying to query the SCCM database and sort the results with Powershell to get a full report on all the connections to a collection. To do this i needed to inject a SELECT query and retrieve an object from the database which proved to be a bit difficult. Below is how i resolved it.

To connect to a SQL you need a client, in powershell and .NET we have the System.Data.SQLClient namespace

$connection = New-Object System.Data.SqlClient.SqlConnection

Then i needed a connection string to open the database.

$connection.ConnectionString = "$ConnectionString"
$connection.Open()

Now we need to inject the command we want. Fairly simple, we create a SQLCommand and connect it to the already established connection.

$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $Connection

Then we insert our command

$Command.CommandText = $SQLQuery

What we do after is execute the command and create a counter of the columns in the result.

$Reader = $Command.ExecuteReader()
$Counter = $Reader.FieldCount

Now we create the DataTable and use the counter to populate the columns. We name them after each column in the query.

$DataTable = New-Object system.Data.DataTable
for($i = 0; $i -lt $counter; $i++){
    $Column = New-Object system.Data.DataColumn $Reader.GetName($i),([string])
    $DataTable.Columns.Add($Column)
}

While the Reader is active it processes a loop for the length of counter and ejects the results into new rows in the DataTable.
To get the correct formatting, we place everything into a string and format accordingly. Name and value.
We then use the Invoke-Expression cmdlet to execute the formatted string and add values to the rows

while ($Reader.Read()) {
    $Data = @()
    for ($i = 0; $i -lt $Counter; $i++) {
        $Data += [String]$Reader.GetValue($i)
    }
    $DataTable.Rows.Add($Data)
}

Last but not least, we close the reader and the connection to the database and output the finished table

$Reader.Close()
$Connection.Close()
   
Return $DataTable

And we’re done!

The finished function will now look like this:

Function Get-SQLQuery{
    Param(
        [Parameter(Mandatory=$true)]
        [String]$SQLQuery,
        [Parameter(Mandatory=$true)]
        [String]$ConnectionString
    )
    $connection = New-Object System.Data.SqlClient.SqlConnection
    $connection.ConnectionString = "$ConnectionString"
    $connection.Open()
    $Command = New-Object System.Data.SQLClient.SQLCommand
    $Command.Connection = $Connection
    $Command.CommandText = $SQLQuery
    $Reader = $Command.ExecuteReader()
    $Counter = $Reader.FieldCount
    
    $DataTable = New-Object system.Data.DataTable
    for($i = 0; $i -lt $counter; $i++){
        $Column = New-Object system.Data.DataColumn $Reader.GetName($i),([string])
        $DataTable.Columns.Add($Column)
    }
    
    while ($Reader.Read()) {
        $Data = @()
        for ($i = 0; $i -lt $Counter; $i++) {
            $Data += [String]$Reader.GetValue($i)
        }
        $DataTable.Rows.Add($Data)
    }
    $Reader.Close()
    $Connection.Close()
    
    Return $DataTable
}

First set your connectionstring and Select query then invoke the command like this:

Get-SQLQuery -ConnectionString $connectionstring -SQLQuery $SelectQuery

Examples of connectionstrings can be found at Connectionstrings.com
The usual string is the trusted which uses your credentials

If you are using the function a lot, concider adding it to your Powershell profile. For more information check out my previous post on WGET and profiles

Beta testing and error handling

Here is a simple snippet that you can run to output all exceptions in a script.

$ErrorActionPreference = "SilentlyContinue"

IWillProduceAnError

#----------------------------------------------------------------
# Debug function, dumps all errors in $Error variable to file
#----------------------------------------------------------------

Function Dump-Errors(){
    Param(
        [Parameter(Position=0, Mandatory = $True)]
        [String]$Logfile
    )
    $Error | Add-Content $Logfile
    $Error.Clear()
}

Dump-Errors -logfile "c:\temp\script.log"

If you look at the code, the cmdlet “IWillProduceAnError” is most likely not recognized and will produce an exception.
However, you have the $ErrorActionPreference set to SilentlyContinue so nothing will show for the user.

This is where the dump-errors function will help you catch errors you would otherwise never see.

Place the function in your code and make sure the last line to run is the function.
It will output all exceptions in the $Error variable to a file. You can then later collect the file from the users you know are beta testing and use the Try/Catch statements to correct your code. Combine this with the Powershell auto updater in silent mode and the user will never know you updated the code.

Windows forms, tips and trix – Buttons

Ever written a GUI to your application? Ever think there are alot of unnecessary lines?
Here is a smart way to add basic buttons without too many lines.

function CreateButton{
  param(
    $name,
    $text,
    $size,
    $location,
    $onclick,
    $Form
  )
  $name = New-Object System.Windows.Forms.Button
  $name.Text = "$text"
  $name.Location = "$location"
  $name.Size = "$size"
  $name.add_Click($onclick)
  $Form.Controls.Add($name)
}

This requires a specific type of code to be run on click. We declare a variable as code within curlybrackets as shown below. In this case we use System.Windows.Forms.SaveFileDialog and save a richtextbox named $consolewindow.

$SaveButton_Click = {
  $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
  $SaveFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
  $SaveFileDialog.ShowDialog()
  $ConsoleWindow.Text | Out-File $SaveFileDialog.FileName
  $SaveFileDialog.Dispose()
}

To add a button with this information we need to create a main form and the console window with the code below.

[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

# Main form settings
$Form = New-Object System.Windows.Forms.Form
$Form.ClientSize = "1000,620"
$Form.DesktopLocation = "100,100"
$Form.MaximizeBox = $False
$Form.ShowIcon = $False
$Form.FormBorderStyle = "FixedSingle"
$Form.Name = "Example"
$Form.Text = "Example"

# Console window settings
$ConsoleWindow = New-Object System.Windows.Forms.RichTextBox
$ConsoleWindow.Size = "1000,250"
$ConsoleWindow.Location = "0,320"
#$ConsoleWindow.ReadOnly = $True #commented out for manual input.
$ConsoleWindow.BackColor = "Black"
$ConsoleWindow.ForeColor = "White"
$Form.Controls.Add($Consolewindow)

I’ve added a few extra rows just to make it look good for the example.
Now let’s create a button

CreateButton -name "SaveButton" -text "Save to file" -size "100,30" -location "50,50" -form $form -onclick $SaveButton_Click

Then we load the forms window

$Form.ShowDialog()

 

Normally you might not have a use for this function but when creating dynamic buttons you could have a use for it. To create a range of buttons from an array, use something like this.

$x = 0
$y = 0
for($i=0; $i -lt 20;$i++){
  CreateButton -name $Buttons[$i] -text $Buttons[$i] -size "100,30" -location "$x,$y" -form $form -onclick $Button_Click[$i]
  $x += 100
  if($x -gt "900"){$y += 30;$x = 0}
}

This example takes the first 20 from an array and puts them on the main form in a orderly fashion as to not overcrowd the main window. The example has no practical use, but you could extract nestled objects and create dynamic buttons with a bit of work.

The full script would look like this:
Note that i’m using the same function for all buttons.

function CreateButton{
  param(
    $name,
    $text,
    $size,
    $location,
    $onclick,
    $Form
  )
  $name = New-Object System.Windows.Forms.Button
  $name.Text = "$text"
  $name.Location = "$location"
  $name.Size = "$size"
  $name.add_Click($onclick)
  $Form.Controls.Add($name)
}
$SaveButton_Click = {
  $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
  $SaveFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
  $SaveFileDialog.ShowDialog()
  $ConsoleWindow.Text | Out-File $SaveFileDialog.FileName
  $SaveFileDialog.Dispose()
}
[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

# Main form settings
$Form = New-Object System.Windows.Forms.Form
$Form.ClientSize = "1000,620"
$Form.DesktopLocation = "100,100"
$Form.MaximizeBox = $False
$Form.ShowIcon = $False
$Form.FormBorderStyle = "FixedSingle"
$Form.Name = "Example"
$Form.Text = "Example"

# Console window settings
$ConsoleWindow = New-Object System.Windows.Forms.RichTextBox
$ConsoleWindow.Size = "1000,250"
$ConsoleWindow.Location = "0,320"
#$ConsoleWindow.ReadOnly = $True #commented out for manual input.
$ConsoleWindow.BackColor = "Black"
$ConsoleWindow.ForeColor = "White"
$Form.Controls.Add($Consolewindow)

$Buttons = @("1".."26")
$x = 0
$y = 0
for($i=0; $i -lt 20;$i++){
  if($buttons[$i] -eq $null){break}
  CreateButton -name $Buttons[$i] -text $Buttons[$i] -size "100,30" -location "$x,$y" -form $form -onclick $SaveButton_Click
  $x += 100
  if($x -gt "900"){$y += 30;$x = 0}
}

$Form.ShowDialog()