简体   繁体   中英

How to preserve the "Name" and "BaseType" of PowerShell Variables for System.* BaseTypes

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.

The BaseType property value in the output from a .GetType() call is the type that the input type is derived from , ie 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] eg:

# [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) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM