0

Most Power Shell variables are a "String, char Object[] array, or Byte[] array". Example:

$string = "hello world"
$string.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

# OR
$char = 1,2,3,4,5,6
$char.GetType()

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

But doing things like Encryption with Power Shell such as RSA returns the Base Type of

$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider
$rsa.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    RSACryptoServiceProvider                 System.Security.Cryptography.RSA

Trying to extract or save the "System.Security.Cryptography.RSA" $rsa BaseType and restore it from a variable breaks the Base Type. Only way that seems to work is to export the $rsa to a xml file and import it later as

Export-Clixml -InputObject $rsa -Path .\rsa.xml
$imported_rsa = Import-Clixml -Path ".\rsa.xml"

However you can't do it with a variable if the rsa.xml is stored as a string. Getting this error.

$xml_string = @"
<xml-data>
"@

$xml_string.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

$imported_rsa = Import-Clixml $xml_string
Import-Clixml: Cannot find drive. A drive with the name '<Objs Version="1.1.0.1" xmlns="http' does not exist.

Doing it with PSSerializer looks correct but is converted to the wrong type of System.Object

$rsa

CspKeyContainerInfo  : 
KeyExchangeAlgorithm : RSA
KeySize              : 1024
LegalKeySizes        : {System.Security.Cryptography.KeySizes}
PersistKeyInCsp      : False
PublicOnly           : False
SignatureAlgorithm   : http://www.w3.org/2000/09/xmldsig#rsa-sha1

$rsa.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    RSACryptoServiceProvider                 System.Security.Cryptography.RSA

$xml_string = @"
<xml-data>
"@

[Management.Automation.PSSerializer]::Deserialize($xml_string)

KeyExchangeAlgorithm : RSA
KeySize              : 1024
LegalKeySizes        : {System.Security.Cryptography.KeySizes}
PersistKeyInCsp      : False
PublicOnly           : False
SignatureAlgorithm   : http://www.w3.org/2000/09/xmldsig#rsa-sha1

[Management.Automation.PSSerializer]::Deserialize($xml_string).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     PSObject                                 System.Object

Attempting to save System.Security.Cryptography.RSA BaseType by converting it to a format that can be move around. Breaks once you try to move it back its System.Security.Cryptography.RSA BaseType. The only thing that seems to work is to export the $rsa variable as a XML file and Import it. However try to do it completely by storing data in variables which leads to the wrong GetType. Breaking the $rsa and not able to decrypt or encrypt anything after the fact.

  • 1
    `Import-CliXml` and `Export-CliXml` uses `PSSerializer` and both output `PSObject` as type when deserialized. Not all objects can be serialized and deserialized into the same object they were before serialization. See https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_output?view=powershell-7.2#deserialized-objects – Santiago Squarzon Jan 28 '23 at 20:02
  • 1
    It's not really clear to me what you want to achieve. Can you explain the broader picture? The "why" is mising here. – zett42 Jan 28 '23 at 20:35
  • Thant would explain why trying to export and Import just the $rsa.Decrypt part looking at the type looks weird. However exporting the whole thing to a file and importing it seems to work best. ` $rsa.Decrypt.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- False False PSMethod1 System.Management.Automation.PSMethod $dec_only.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True PSObject System.Object ` – IO_ASCII Jan 28 '23 at 20:45
  • Attempt to archive the encryption $rsa object that is created and or just the decryption part if I wanted to pass it over to another machine or script. That isn't apart of the one that encrypted it. – IO_ASCII Jan 28 '23 at 20:47
  • 1
    I really dont understand what you're trying to achieve, I agree with zett42's comment. Why do you want to serialize the object? You only need the keys afaik and those can be serialized. Then you can instantiate a new object anywhere using those keys – Santiago Squarzon Jan 28 '23 at 21:09
  • trying to just past over the $rsa or $rsa.Decrypt part `$rsa=New-Object System.Security.Cryptography.RSACryptoServiceProvider` `$plaintext=[System.Text.Encoding]::UTF8.GetBytes("hi")` `$ciphertext=$rsa.Encrypt($plaintext, $false)` `Export-Clixml -InputObject $rsa -Path .\xml.xml` `$passed_rsa=Import-Clixml -Path .\xml.xml` `$encode_text=$passed_rsa.Decrypt($ciphertext, $false)` `$clear_text=[System.Text.Encoding]::UTF8.GetString($encoded_text);$clear_text` But without having to save the $rsa to a xml file. Kinda like `$decrypt=` `$encoded_test=$decrypt($ciphertext, $false)` – IO_ASCII Jan 28 '23 at 21:31
  • Again, you dont need to serialize the object. You just only need to serialize the private key. Then you can create a new object and import that private key which would allow you to deserialize the `$ciphertext`. I can post an example of what I mean if that can help you – Santiago Squarzon Jan 28 '23 at 22:00
  • Is this what you're trying to achieve? https://gist.github.com/santisq/a351df6384324f9a6e4e9a5427977c2b#file-testrsa-ps1 – Santiago Squarzon Jan 28 '23 at 22:40

1 Answers1

1

The BaseType property value in the output from a .GetType() call is the type that the input type is derived from, i.e. its immediate ancestor in the .NET inheritance hierarchy.

You can easily show the entire hierarchy, starting with the input object's own time and eventually ending in System.Object, the root type of all .NET types - via the intrinsic pstypenames property;[1] e.g.:

# [System.Security.Cryptography.RSACryptoServiceProvider]::new() is PSv5+
# shorthand for: New-Object System.Security.Cryptography.RSACryptoServiceProvider
[System.Security.Cryptography.RSACryptoServiceProvider]::new().pstypenames

Output:

System.Security.Cryptography.RSACryptoServiceProvider
System.Security.Cryptography.RSA
System.Security.Cryptography.AsymmetricAlgorithm
System.Object

Given this relationship, there's usually no need to convert a given object to any of its base types, because it is an instance of these types (just of a more specialized (derived) kind), which you can verify with -is, the type(-inheritance) / interface test operator:

$instance = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
# -> $true
$instance -is [System.Security.Cryptography.RSA]

Serializing and later deserializing objects (whether via Export-Clixml or directly via [System.Management.Automation.PSSerializer]), using PowerShell's XML-based CLIXML format, is an entirely separate issue, and, as noted in the comments:

  • Except for a few well-known types, deserialization results in method-less emulations of the original objects and their properties, using PowerShell's "property-bag" type, [psobject] / [pscustomobject]; that is, the original type identity is lost - see this answer for details.

[1] Note that it is possible for this property to contain virtual entries inserted for the benefit of PowerShell's ETS (Extended Type System).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Avoiding the use of Export-Clixml with trying to store the $rsa `[System.Security.Cryptography.RSA]` in a base64 enocded string also breaks it. `$instance = [System.Security.Cryptography.RSACryptoServiceProvider]::new();` `$base = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($instance));` `$i_rsa = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($base));` `$i_rsa;` `$i_rsa.GetTyype;` `$i_rsa -is [System.Security.Cryptography.RSA];` output: `System.Security.Cryptography.RSACryptoServiceProvider` `False` – IO_ASCII Jan 28 '23 at 22:15
  • @IO_ASCII, you cannot meaningfully serialize an arbitrary .NET instance with `[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($instance))`. This implicitly calls `.ToString()` on `$instance`, which in many cases - including this one - simply reports the _type name_, not a meaningful representation of the instance; try `[System.Security.Cryptography.RSACryptoServiceProvider]::new().ToString()` – mklement0 Jan 28 '23 at 22:21