1

I have a PSCustomObject that I need to serialize on disk in JSON but one property needs to be obfuscated before being written and of course, deobfuscated when read later on.

I found that JavaScriptSerializer could do the trick when customized with a JavaScriptConverter (both ways, read and write), but all the examples I find are in C#, for example this very interesting thread

is it possible to write such a JavaScriptConverter in Powershell (maybe using a Class in place of the PSCustomObject)?

as a matter of example, let say that the PSCustomObject is @{Username:"foo";Password:"bar"} and that I want the JSON file to be

{
    "Username": "foo",
    "Password": "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000b83de0765b9a2a4088e073b1166fd67e0000000002000000000003660000c000000010000000790fcfe1dce43342e8d444757f46c8d50000000004800000a0000000100000002d0020ca9088b3d85b27f597847a3dc908000000ca11862bdb95757b140000002802e326a0041e039d8ff9c41ff46ec24c1f"
}

with Password in the JSON file being the SecureString version of the original password property. the need is for Windows Powershell 5.1 only, not powershell 6+

Peyre
  • 397
  • 2
  • 14
  • 1
    Sure it's possible. What's the real question you want to ask? :) – Mathias R. Jessen Mar 20 '23 at 15:52
  • I agree with @Mathias but in your given example, how do you know that `"Password"` is a securestring and not just a (very long) plain text password? – iRon Mar 20 '23 at 15:58
  • @MathiasR.Jessen if it's possible to do it in Powershell, and as I'm not fluent in C#, I would greatly appreciate any pointer to powershell examples or how-to, as I was not able to find some by myself – Peyre Mar 20 '23 at 15:59
  • You can interate through the `Json` object like [here](https://stackoverflow.com/a/74352656/1701026) but again, how do you want to differentiate between a `string` and a `securestring`? – iRon Mar 20 '23 at 16:03
  • @iRon usually, ```securestring``` are longer than the maximum password length (127 char if memory serves). and if the JavaScriptConverter can not convert back the ```securestring```, it can assume there was a problem and return ```null```. in anyway, the example is just... an example and the JavaScriptConverter can also be used the serialize/deserialize ```enum``` in textual forms as stated in the quoted thread. – Peyre Mar 20 '23 at 16:16
  • Without touching the Json parser, you might simply serialize the credentials and convert it to json: `$YourObject.Credential = System.Management.Automation.PSSerializer]::Serialize($credential); YourObject |ConvertTo-Json -Depth 9` and deserialize it when you retrieve it: `$YourObject = $YourJson | ConvertFrom-Json; [System.Management.Automation.PSSerializer]::Deserialize($YourObject.Credential)` – iRon Mar 20 '23 at 16:21
  • @iRon thanks a lot for the hint for the credential but the main goal of the question is to learn how to write a JavaScriptConverter and customize the JavaScriptSerializer in powershell, if it is possible. – Peyre Mar 20 '23 at 16:32

1 Answers1

1

The implementation in PowerShell would look more or less like this, clearly not perfect but might help you get started. Note, this example focuses on serialization and deserialization of a PSCredential object, not a PSCustomObject.

  • Class
Add-Type -AssemblyName System.Web.Extensions

class SecureStringConverter : System.Web.Script.Serialization.JavaScriptConverter {
    SecureStringConverter() { }

    [System.Collections.Generic.IEnumerable[Type]] get_SupportedTypes() {
        return [type[]] [pscredential]
    }

    [object] Deserialize(
        [System.Collections.Generic.IDictionary[string, object]] $dict,
        [type] $type,
        [System.Web.Script.Serialization.JavaScriptSerializer] $serializer
    ) {
        return [pscredential]::new(
            $dict['UserName'],
            [System.Management.Automation.PSSerializer]::Deserialize($dict['Password'])
        )
    }

    [System.Collections.Generic.IDictionary[string, object]] Serialize(
        [object] $object,
        [System.Web.Script.Serialization.JavaScriptSerializer] $serializer
    ) {
        $dict = [System.Collections.Generic.Dictionary[string, object]]::new()
        $dict['UserName'] = $object.UserName
        $dict['Password'] = [System.Management.Automation.PSSerializer]::Serialize($object.Password)
        return $dict
    }
}
  • Serialization
$obj = [pscredential]::new(
    'SomeUser',
    (ConvertTo-SecureString 'bar' -AsPlainText -Force))

$serializer = [Web.Script.Serialization.JavaScriptSerializer]::new()
$serializer.RegisterConverters(
    [System.Web.Script.Serialization.JavaScriptConverter[]]@(
    [SecureStringConverter]::new()))

$json = $serializer.Serialize($obj)
  • Deserialization
$deserialized = $serializer.Deserialize($json, [pscredential])
$deserialized.GetNetworkCredential().Password # Should be `bar`
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 1
    Stupidly, I was trying to do it old-school including C# code in the script instead of using a class. Using a class make the translation C# ➔ PS straightforward. I can now explore the capabilities of JavaScriptConverter by myself . Thanks a lot for your help. – Peyre Mar 21 '23 at 11:06
  • @Peyre glad the answer was helpful. It is definitely worth to learn how to do it but for the particular case of a `SecureString` it gets tricky to do it. As you can see I had to use the `PSSerializer`. Using `ConvertFrom-SecureString` to get the encrypted string would fail in this case. – Santiago Squarzon Mar 21 '23 at 12:49
  • yes, that's strange as ```PSSerializer``` return the very same data as ```ConvertFrom-SecureString``` with just the unicode-encoded ```Export-CliXml``` _decoration_ around it. – Peyre Mar 21 '23 at 15:51