HiddenString.ps1

class HiddenString {
    hidden [SecureString]$SecureString = [SecureString]::new()
    hidden $_Length = $($this | Add-Member ScriptProperty 'Length' { $This.SecureString.Length })

    hidden New([Object]$Object, [Bool]$Warn) {
        if ($Object -is [SecureString]) { $This.SecureString = $Object }
        elseif ($Object -is [HiddenString]) { $This.SecureString = $Object.SecureString }
        elseif ($Object -is [Byte[]]) {
            $String = [System.Text.StringBuilder]::new()
            foreach ($Byte in $Object) {
                $x2 = '{0:x2}' -f $Byte
                [void]$String.Append($x2)
            }
            $This.SecureString = ConvertTo-SecureString $String
        }
        elseif ($Object) { $This.Add($Object, $Warn) }
    }
    HiddenString()                       { $This.New($Null,   $True) }
    HiddenString([Object]$Object)        { $This.New($Object, $True) }
    HiddenString([Object]$Object, $Warn) { $This.New($Object, $Warn) }
    static [HiddenString]FromBase64Cypher([string]$String) { return [System.Convert]::FromBase64String($String) }

    [Void]Clear() { $This.SecureString.Clear() }
    [Void]Add([Char[]]$Characters) { $This.Add($Characters, $True) }
    [Void]Add([Char[]]$Characters, [bool]$Warn) {
        if ($Warn -and $Characters.Count -gt 1) { Write-Warning 'For better obscurity, use a hidden or secure string for input.' }
        $Characters.ForEach{ $This.SecureString.AppendChar($_) }
    }
    [Bool]Equals($Object) { return $This.SecureString.Equals($Object.SecureString)}
    [SecureString]ToSecureString() { return $This.SecureString }
    [Byte[]]GetBytes() {
        $Hexadecimal = $This.SecureString |ConvertFrom-SecureString
        return ([regex]::matches($Hexadecimal, '.{2}')).foreach{ [byte][Convert]::ToInt64($_, 16) }
    }
    [String]ToBase64Cypher() { return [Convert]::ToBase64String($This.GetBytes()) }
    [String]Reveal() { return $This.Reveal($True) }
    [String]Reveal($Warn) {
        if ($Warn) { Write-Warning 'For better obscurity, use a secure string output.' }
        $Ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($This.SecureString)
        $String = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
        return $String
    }
    [Void]Dispose() { $This.SecureString.Dispose() }
}

class HiddenStringConverter : System.Management.Automation.PSTypeConverter
{
    [bool] CanConvertFrom([object]$sourceValue, [Type]$destinationType) {
        return $false
    }
    [object] ConvertFrom([object]$sourceValue, [Type]$destinationType, [IFormatProvider]$formatProvider, [bool]$ignoreCase) {
        throw [NotImplementedException]::new() 
    }
    [bool] CanConvertTo([object]$sourceValue, [Type]$destinationType) {
        return $destinationType -eq [System.Security.SecureString] -or $destinationType -eq [byte[]]
    }
    [object] ConvertTo([object]$sourceValue, [Type]$destinationType, [IFormatProvider]$formatProvider, [bool]$ignoreCase) {
        if     ($destinationType -eq [System.Security.SecureString]) { return $sourceValue.SecureString }
        elseif ($destinationType -eq [byte[]]) { return $sourceValue.GetBytes() }
        else { throw [NotImplementedException]::new() }
    }
}
Remove-TypeData -TypeName HiddenString
Update-TypeData -TypeName HiddenString -TypeConverter HiddenStringConverter