BAMCIS.Crypto.psm1

$script:Header = "-----BEGIN {0}-----"
$script:Footer = "-----END {0}-----"

#region RSA

Function ConvertFrom-RSAPrivateKeyPEM {
    <#
        .SYNOPSIS
            Converts a PKCS#1 format PEM file to an RSACryptoServiceProvider object.
 
        .DESCRIPTION
            This cmdlet takes a PKCS#1 formatted RSA private key and converts it to
            an RSACryptoServiceProvider object.
 
        .PARAMETER PEM
            The PEM content of the RSA Private Key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN RSA PRIVATE KEY-----
            <base64encodedkeydata>
            -----END RSA PRIVATE KEY-----
            "@
 
            $RSA = ConvertFrom-RSAPrivateKeyPEM -PEM $Key
 
            This will convert the PEM file containing a private key to an RSACryptoServiceProvider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path
    )

    Begin {

    }

    Process {
        if ($PSCmdlet.ParameterSetName -eq "Path")
        {
            Write-Verbose -Message "Getting PEM data from $Path."

            $PEM = Get-Content -Path $Path -Raw
        }

        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "RSA PRIVATE KEY")
            $Footer = [System.String]::Format($script:Footer, "RSA PRIVATE KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input RSA Private Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }        

        Write-Verbose -Message $PEM

        [System.Collections.Hashtable]$Result = Read-ASN1Content -Base64String $PEM

        [System.Collections.Hashtable]$KeyParts = $Result["0"]["Data"]

        $KeyParts

        [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider -Version $KeyParts["0"]["Data"] `
            -Modulus $KeyParts["1"]["Data"] `
            -Exponent $KeyParts["2"]["Data"] `
            -D $KeyParts["3"]["Data"] `
            -P $KeyParts["4"]["Data"] `
            -Q $KeyParts["5"]["Data"] `
            -DP $KeyParts["6"]["Data"] `
            -DQ $KeyParts["7"]["Data"] `
            -IQ $KeyParts["8"]["Data"]

        Write-Output -InputObject $RSA
    }

    End {
    }
}

Function ConvertFrom-RSAPublicKeyPEM {
    <#
        .SYNOPSIS
            Converts a PKCS#1 format PEM file to an RSACryptoServiceProvider object.
 
        .DESCRIPTION
            This cmdlet takes a PKCS#1 formatted RSA public key and converts it to
            an RSACryptoServiceProvider object.
 
        .PARAMETER PEM
            The PEM content of the RSA Private Key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN RSA PUBLIC KEY-----
            <base64encodedkeydata>
            -----END RSA PUBLIC KEY-----
            "@
 
            $RSA = ConvertFrom-RSAPublicKeyPEM -PEM $Key
 
            This will convert the PEM file containing a public key to an RSACryptoServiceProvider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path
    )

    Begin {
    }

    Process {

        if ($PSCmdlet.ParameterSetName -eq "Path")
        {
            Write-Verbose -Message "Getting PEM data from $Path."

            $PEM = Get-Content -Path $Path -Raw
        }

        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "RSA PUBLIC KEY")
            $Footer = [System.String]::Format($script:Footer, "RSA PUBLIC KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input RSA Private Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }    

        Write-Verbose -Message $PEM

        $Results = Read-ASN1Content -Base64String $PEM

        [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider `
            -Modulus $Results["0"]["Data"]["0"]["Data"] `
            -Exponent $Results["0"]["Data"]["1"]["Data"]

        Write-Output -InputObject $RSA        
    }

    End {
    }
}

#endregion

#region DSA

Function ConvertFrom-DSAPrivateKeyPEM {
    <#
        .SYNOPSIS
            Converts a PEM file to an DSACryptoServiceProvider object.
 
        .DESCRIPTION
            This cmdlet takes a DSA Private Key PEM file and converts it to
            a DSACryptoServiceProvider object.
 
        .PARAMETER PEM
            The PEM content of the DSA Private Key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN DSA PRIVATE KEY-----
            <base64encodedkeydata>
            -----END DSA PRIVATE KEY-----
            "@
 
            $RSA = ConvertFrom-DSAPrivateKeyPEM -PEM $Key
 
            This will convert the PEM file containing a private key to an DSACryptoServiceProvider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.DSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.DSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path
    )

    Begin {
    }

    Process {
        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "DSA PRIVATE KEY")
            $Footer = [System.String]::Format($script:Footer, "DSA PRIVATE KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input DSA Private Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }    

        Write-Verbose -Message $PEM

        $Results = Read-ASN1Content -Base64String $PEM

        # The 4th item is the public exponent, which doesn't need to be sent
        # for a private key
        [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider `
            -Version $Results["0"]["Data"]["0"]["Data"] `
            -P $Results["0"]["Data"]["1"]["Data"] `
            -Q $Results["0"]["Data"]["2"]["Data"] `
            -G $Results["0"]["Data"]["3"]["Data"] `
            -X $Results["0"]["Data"]["5"]["Data"] 

        Write-Output -InputObject $DSA        
    }

    End {
    }
}

Function ConvertFrom-DSAPublicKeyPEM {
    <#
        .SYNOPSIS
            Converts a PEM file to an DSACryptoServiceProvider object.
 
        .DESCRIPTION
            This cmdlet takes a DSA Public Key PEM file and converts it to
            a DSACryptoServiceProvider object.
 
        .PARAMETER PEM
            The PEM content of the DSA Public Key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN DSA PUBLIC KEY-----
            <base64encodedkeydata>
            -----END DSA PUBLIC KEY-----
            "@
 
            $RSA = ConvertFrom-DSAPrivateKeyPEM -PEM $Key
 
            This will convert the PEM file containing a public key to an DSACryptoServiceProvider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.DSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.DSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path
    )

    Begin {
    }

    Process {
        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "DSA PUBLIC KEY")
            $Footer = [System.String]::Format($script:Footer, "DSA PUBLIC KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input DSA Public Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }    

        Write-Verbose -Message $PEM

        $Results = Read-ASN1Content -Base64String $PEM

        # First item is a sequence, it has 2 properties, a sequence and a bit string
        # The nested sequence has 2 properties, an OID and a sequence
        # This third sequence has 3 integer values, the common key data
        # The bit string contains an integer which is the public exponent
        # 0/Data -> 0/Data -> 1/Data -> 0, 1, 2
        # 0/Data -> 1/Data -> 0/Data
        [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider `
            -P $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["0"]["Data"] `
            -Q $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["1"]["Data"] `
            -G $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["2"]["Data"] `
            -Y $Results["0"]["Data"]["1"]["Data"]["0"]["Data"] 

        Write-Output -InputObject $DSA        
    }

    End {
    }
}

#endregion

#region PKCS#8 Format

Function ConvertFrom-PrivateKeyPEM {
    <#
        .SYNOPSIS
            Converts a PKCS#8 PEM private key to its appropriate crypto service provider.
 
        .DESCRIPTION
            This cmdlet converts a PKCS#8 PEM private key to its .NET crypto service provider object.
 
            Currently the cmdlet supports RSA and DSA keys.
 
        .PARAMETER PEM
            The PEM content of the private key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .PARAMETER AsICspAsymmetricAlgorithm
            Specifies that the output will be an ICspAsymmetricAlgorithm interface instead of the concrete crypto
            service provider object.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN PRIVATE KEY-----
            <base64encodedkeydata>
            -----END PRIVATE KEY-----
            "@
 
            $CSP = ConvertFrom-PrivateKeyPEM -PEM $Key
 
            This will convert the PEM file into the corresponding crypto service provider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider, System.Security.Cryptography.DSACryptoServiceProvider, System.Security.Cryptography.ICspAsymmetricAlgorithm
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider], [System.Security.Cryptography.ICspAsymmetricAlgorithm])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path,

        [Parameter()]
        [Switch]$AsICspAsymmetricAlgorithm
    )

    Begin {
    }

    Process {
        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "PRIVATE KEY")
            $Footer = [System.String]::Format($script:Footer, "PRIVATE KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input RSA Private Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }    

        Write-Verbose -Message $PEM

        [System.Collections.Hashtable]$Result = Read-ASN1Content -Base64String $PEM
            
        # Contains data about the key as well as the key data itself

        # This has a tag, length, and data property
        [System.Collections.Hashtable]$TopLevelSequence = $Result["0"]

        # This has numbered properties, i.e. 0, 1, 2
        [System.Collections.Hashtable]$SequenceData = $TopLevelSequence["Data"]

        [System.String]$OID = $SequenceData["1"]["Data"]["0"]["Data"]
        Write-Verbose -Message "Private Key OID: $OID"

        switch ($OID)
        {
            # RSA
            "1.2.840.113549.1.1.1" {
                # The number 2 index is an octet stream, it's data element is a hashtable with 1 property, "0", which is another hash table and represents a sequence,
                # This hash table has a data property whose value is another hash table, its keys are numbers 0 - 8 and each represents part of the RSA key
                $KeyParts = $SequenceData["2"]["Data"]["0"]["Data"]

                [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider -Version $KeyParts["0"]["Data"] `
                    -Modulus $KeyParts["1"]["Data"] `
                    -Exponent $KeyParts["2"]["Data"] `
                    -D $KeyParts["3"]["Data"] `
                    -P $KeyParts["4"]["Data"] `
                    -Q $KeyParts["5"]["Data"] `
                    -DP $KeyParts["6"]["Data"] `
                    -DQ $KeyParts["7"]["Data"] `
                    -IQ $KeyParts["8"]["Data"]

                if ($AsICspAsymmetricAlgorithm)
                {
                    Write-Output -InputObject ([System.Security.Cryptography.ICspAsymmetricAlgorithm]$RSA)
                }
                else
                {
                    Write-Output -InputObject $RSA
                }

                break
            }
            # DSA
            "1.2.840.10040.4.1" {
                [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider -Version $KeyParts["0"]["Data"] `
                    -P $KeyParts["1"]["Data"] `
                    -Q $KeyParts["2"]["Data"] `
                    -G $KeyParts["3"]["Data"] `
                    -X $KeyParts["5"]["Data"] 

                if ($AsICspAsymmetricAlgorithm)
                {
                    Write-Output -InputObject ([System.Security.Cryptography.ICspAsymmetricAlgorithm]$DSA)
                }
                else
                {
                    Write-Output -InputObject $DSA
                }

                break
            }
            default {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("Currently the only OID supported for PRIVATE KEY type PEM content is RSA and DSA.")) -ErrorAction Stop
            }
        }
    }

    End {
    }
}

Function ConvertFrom-PublicKeyPEM {
    <#
        .SYNOPSIS
            Converts a PEM file to its appropriate CryptoServiceProvider object, or ICspAsymmetricAlgorithm interface.
 
        .DESCRIPTION
            This cmdlet takes a PEM file contents and converts to its appropriate CryptoServiceProvider object, or ICspAsymmetricAlgorithm interface.
 
            This supports RSA and DSA keys.
 
        .PARAMETER PEM
            The PEM content of the Public Key. This can either be the complete PEM file contents
            or just the base64 encoded data.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .PARAMETER AsICspAsymmetricAlgorithm
            Specifies that the output will be an ICspAsymmetricAlgorithm interface instead of the concrete crypto
            service provider object.
 
        .EXAMPLE
            $Key = @"
            -----BEGIN PUBLIC KEY-----
            <base64encodedkeydata>
            -----END PUBLIC KEY-----
            "@
 
            $CSP = ConvertFrom-PublicKeyPEM -PEM $Key
 
            This will convert the PEM file into the corresponding crypto service provider object.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider, System.Security.Cryptography.DSACryptoServiceProvider, System.Security.Cryptography.ICspAsymmetricAlgorithm
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider], [System.Security.Cryptography.DSACryptoServiceProvider], [System.Security.Cryptography.ICspAsymmetricAlgorithm])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path,

        [Parameter()]
        [Switch]$AsICspAsymmetricAlgorithm
    )

    Begin {
    }

    Process {
        $PEM = $PEM.Replace("\r", "").Replace("\n", "").Replace("`r", "").Replace("`n", "")
    
        # If the PEM content doesn't have the header/footer info stripped, make
        # sure it's the right type of key
        if ($PEM.StartsWith("-----"))
        {
            $Header = [System.String]::Format($script:Header, "PUBLIC KEY")
            $Footer = [System.String]::Format($script:Footer, "PUBLIC KEY")
            $RegexStr = "(?:$Header)\s*(\S+)\s*(?:$Footer)"

            $Regex = New-Object -TypeName System.Text.RegularExpressions.Regex($RegexStr, @([System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
            [System.Text.RegularExpressions.Match]$DataMatch = $Regex.Match($PEM)

            if (-not $DataMatch.Success)
            {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The input RSA Private Key PEM was not formatted correctly.")) -ErrorAction Stop
            }
            else
            {
                $PEM = $DataMatch.Groups[1].Value
            }
        }    

        Write-Verbose -Message $PEM

        $Results = Read-ASN1Content -Base64String $PEM
                    
        # 0 is the top level sequence, it's data is another hashtable with 2 properties,
        # 0 is another sequence and 1 is a bit string with the key data
        # This 0 is a sequence that contains the OID
        $OID = $Results["0"]["Data"]["0"]["Data"]["0"]["Data"]

        Write-Verbose -Message "Public key OID: $OID"
            
        switch ($OID)
        {
            # RSA
            "1.2.840.113549.1.1.1" {
                # 1 is a bit string, its contents are a sequence
                # The sequence has 2 elements,
                $KeyParts = $Results["0"]["Data"]["1"]["Data"]["0"]["Data"]

                [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider `
                    -Modulus $KeyParts["0"]["Data"] `
                    -Exponent $KeyParts["1"]["Data"]

                if ($AsICspAsymmetricAlgorithm)
                {
                    Write-Output -InputObject ([System.Security.Cryptography.ICspAsymmetricAlgorithm]$RSA)
                }
                else
                {
                    Write-Output -InputObject $RSA
                }

                break
            }
            # DSA
            "1.2.840.10040.4.1" {
                [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider `
                    -P $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["0"]["Data"] `
                    -Q $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["1"]["Data"] `
                    -G $Results["0"]["Data"]["0"]["Data"]["1"]["Data"]["2"]["Data"] `
                    -Y $Results["0"]["Data"]["1"]["Data"]["0"]["Data"]                 

                if ($AsICspAsymmetricAlgorithm)
                {
                    Write-Output -InputObject ([System.Security.Cryptography.ICspAsymmetricAlgorithm]$DSA)
                }
                else
                {
                    Write-Output -InputObject $DSA
                }

                break
            }
            default {
                Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("Currently the only OID supported for PUBLIC KEY type PEM content is RSA.")) -ErrorAction Stop
            }
        }
    }

    End {
    }
}

#endregion

#region Certificates

Function ConvertFrom-CertificatePEM {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM
    )

    Begin {
    }

    Process {
        Write-Verbose -Message $PEM

        $Results = Read-ASN1Content -Base64String $PEM

        [System.Collections.Hashtable]$Cert = @{}

        $OID = $Results["0"]["Data"]["1"]["Data"]["0"]["Data"]

        Write-Verbose -Message "Certificate OID: $OID"

        $Data = $Results["0"]["Data"]["2"]["Data"]
                   
        $CertInfo1 = $Results["0"]["Data"]["0"]["Data"]["3"]["Data"]

        $Cert.Add("Info", @{})

        foreach ($Item in $CertInfo1.GetEnumerator())
        {
            $TempOID = $Item.Value["Data"]["0"]["Data"]["0"]["Data"]
            $TempStr = $Item.Value["Data"]["0"]["Data"]["1"]["Data"]
            $Cert["Info"].Add($TempOID, $TempStr)
        }

        $ExpiryInfo = $Results["0"]["Data"]["0"]["Data"]["4"]["Data"]

        [System.UInt64]$NET = $ExpiryInfo["0"]["Data"]

        [System.DateTime]$Epoch = New-Object -TypeName System.DateTime(1970, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
        $Cert.Add("Issued", $Epoch.AddMilliseconds($NET))


        $NLT = $ExpiryInfo["1"]["Data"]
        $Cert.Add("Expires", $Epoch.AddMilliseconds($NLT))

        Write-Output -InputObject $Cert
    }

    End {
    }
}

#endregion

#region Wrappers

Function ConvertFrom-PEM {
    <#
        .SYNOPSIS
            Creates an RSACryptoServiceProvider from PEM encoded input.
 
        .DESCRIPTION
            This cmdlet accepts an input PEM file or the string contents of a PEM file and converts
            them to the appropriate type of RSA Key as an RSACryptoServiceProvider object. You can supply
            text or files that contain a single key with the following formats:
 
            PUBLIC KEY
            RSA PUBLIC KEY
            PRIVATE KEY
            RSA PRIVATE KEY
 
            For the generic PUBLIC and PRIVATE key options, the key must be identified as an RSA private key
            through its OID.
 
        .PARAMETER PEM
            The contents of a PEM encoded file with the appropriate Header and Footer text (that is not base64 encoded), such as
            -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----
 
            The string can only contain a single key.
 
        .PARAMETER Path
            The path to a properly encoded PEM file with the appropriate Header and Footer text.
 
            The file can only contain a single key, subsequent keys in the same file will be ignored.
 
        .EXAMPLE
            [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = ConvertFrom-PEM -Path c:\myprivatekey.pem
 
            This creates an RSA key from the provided PEM file.
 
        .EXAMPLE
            $PublicKey = @"
            -----BEGIN RSA PUBLIC KEY-----
            MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
            D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
            luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
            o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
            gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
            Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
            -----END RSA PUBLIC KEY-----
            "@
 
            [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = ConvertFrom-PEM -PEM $PublicKey
 
            Creates an RSA Public Key from the PEM file content.
 
        .EXAMPLE
            $DSAPrivate = @"
            -----BEGIN DSA PRIVATE KEY-----
            MIIBugIBAAKBgQDRIicJ9PF+yKIW2WsumCdESAsrGUq0KgPRtl82V8bkL/nXpU3m
            kA29wflmABLpaqYEafUUXLFdAJ/gkXmw/edALVof/K3gmeDWRpOLK6HzgAHcCsKF
            6Uk7eGeBvldXsG3qwWZdY0EHUMFYiC2I/6GNZefaskCbh24CNlFf2wLUrwIVAMuK
            Wodox+9PT/gCbORbGLKAaAy3AoGAb0/9/tIBubGaEeRDOkEUOXIMfyICn4Jn/WWN
            9OHrRj0wNJm/UfYj3F9egQySBxUfnhCOKwxAMVu+xCtN6ih4DyJsvruEhXZvaBNz
            wxXBx+Zk6x1qC32HpcjEc6JGkusvHAkSX2cnCaaxWqIwSMdz3xBRkmjRQTt1Nxit
            jXA4RvQCgYB9AMxUbF9ju5RQ6spfEFo8GH/NcLldbW2FC7O1NDdi4YVRSFD/76u4
            2KpBK/lwVQe2Givx9YpCG3Wylgk4LdGaJe2+ZpOjZRd8Hj6tiSjng1S6qv1D3vfd
            ueUwoGI02RPlE/VCbmcHs91prU1iBiIDOm5SPQd1wETseHJngNQXHgIUIul8kXgM
            IK4wfSVcliKEgRAZC7c=
            -----END DSA PRIVATE KEY-----
            "@
 
 
            [System.Security.Cryptography.ICspAsymmetricAlgorithm]$CSP = ConvertFrom-PEM -PEM $PEM
 
            In this example instead of an RSA key, the PEM is a DSA key and it is cast to the ICspAsymmetricAlgorithm
            interface. This approach could be used to receive the output from ConvertFrom-PEM regardless of the
            key type (RSA, DSA, public, private, etc).
 
            ** Note the base64 string content in this example has been modified as to not expose a real private
                key, thus if you were to try to execute the above example, you would get a cryptographic exception
                stating that the signature was invalid.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider or System.Security.Cryptography.DSACryptoServiceProvider
 
            All output types implement the System.Security.Cryptography.ICspAsymmetricAlgorithm interface
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/23/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider], [System.Security.Cryptography.DSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Content")]
        [ValidateNotNullOrEmpty()]
        [System.String]$PEM,

        [Parameter(Mandatory = $true, ParameterSetName = "Path")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({
            Test-Path -Path $_
        })]
        [System.String]$Path
    )

    Begin {
    }

    Process {
        if ($PSCmdlet.ParameterSetName -eq "Path")
        {
            Write-Verbose -Message "Getting PEM data from $Path."

            $PEM = Get-Content -Path $Path -Raw
        }

        # Remove all new line encoding
        $PEM = $PEM.Replace("\n", "").Replace("\r", "").Replace("`n", "").Replace("`r", "")

        $Public = "(PUBLIC KEY|RSA PUBLIC KEY|DSA PUBLIC KEY)"
        $Private = "(PRIVATE KEY|RSA PRIVATE KEY|DSA PRIVATE KEY)"

        $Header = "-----BEGIN {0}-----"
        $Footer = "-----END {0}-----"

        # These have 2 capture groups, the header key type and the body
        $PublicRegex = New-Object -TypeName System.Text.RegularExpressions.Regex("^$([System.String]::Format($Header, $Public))\s*(.*?)\s*$([System.String]::Format($Footer, $Public))$", [System.Text.RegularExpressions.RegexOptions]::Singleline)
        $PrivateRegex = New-Object -TypeName System.Text.RegularExpressions.Regex("^$([System.String]::Format($Header, $Private))\s*(.*?)\s*$([System.String]::Format($Footer, $Private))$", [System.Text.RegularExpressions.RegexOptions]::Singleline)

        [System.Text.RegularExpressions.Match]$PublicMatch = $PublicRegex.Match($PEM)
        [System.Text.RegularExpressions.Match]$PrivateMatch = $PrivateRegex.Match($PEM)

        # Some type of public key or certificate
        if ($PublicMatch.Success)
        {
            $KeyType = $PublicMatch.Groups[1].Value
            $Data = $PublicMatch.Groups[2].Value

            Write-Verbose -Message $KeyType

            switch ($KeyType)
            {
                "CERTIFICATE" {
                    $Cert = ConvertFrom-CertificatePEM -PEM $Data
                   
                    Write-Output -InputObject $Cert
                    
                    break
                }
                "PUBLIC KEY" {
                    # This could be RSA or DSA
                    $CSP = ConvertFrom-PublicKeyPEM -PEM $Data
                    
                    Write-Output -InputObject $CSP

                    break
                }
                "RSA PUBLIC KEY" {
                    [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = ConvertFrom-RSAPublicKeyPEM -PEM $Data

                    Write-Output -InputObject $RSA

                    break
                }
                "DSA PUBLIC KEY" {
                    [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = ConvertFrom-DSAPublicKeyPEM -PEM $Data

                    Write-Output -InputObject $DSA

                    break
                }
            }

        }
        # Some type of private key
        elseif ($PrivateMatch.Success)
        {
            $KeyType = $PrivateMatch.Groups[1].Value
            $Data = $PrivateMatch.Groups[2].Value

            Write-Verbose -Message $KeyType
            
            switch ($KeyType) 
            {
                "RSA PRIVATE KEY" {
                    [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = ConvertFrom-RSAPrivateKeyPEM -PEM $Data

                    Write-Output -InputObject $RSA

                    break
                }
                "DSA PRIVATE KEY" {
                    [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = ConvertFrom-DSAPrivateKeyPEM -PEM $Data

                    Write-Output -InputObject $DSA

                    break
                }
                "PRIVATE KEY" {
                    $CSP = ConvertFrom-PrivateKeyPEM -PEM $Data

                    Write-Output -InputObject $CSP

                    break
                }
            }
        }
        # Unknown type
        else
        {
             Write-Error -Exception (New-Object -TypeName System.Security.Cryptography.CryptographicException("The presented PEM data is of an unknown type.")) -ErrorAction Stop
        }
    }

    End {
    }
}

#endregion

#region CSPs

Function New-RSACryptoServiceProvider {
    <#
        .SYNOPSIS
            Creates a new RSACryptoServiceProvider object from the specified parameters.
         
        .DESCRIPTION
            This cmdlet wraps creating the RSAParameters object and importing those into the RSACryptoServiceProvider object.
            The parameters are supplied as base64 encoded strings that were derived from byte arrays stored in
            big endian order. The parameters are trimmed down to the required lengths before being imported, so if
            the parameters are stored in PEM format with leading padding bytes, they are automatically trimmed and
            can be supplied as is.
 
            This cmdlet is typically called by other cmdlets in the BAMCIS.Crypto module, but can be called directly
            if you are manually parsing or creating RSACryptoServiceProviders from PEM or XML files.
 
            If only a modulus and exponent are provided, a public key is produced, otherwise all RSAParameter inputs
            are required to create an RSA private key.
 
        .PARAMETER Modulus
            The base64 encoded modulus for the RSA algorithm. This should be in big endian order and is expected to
            be 256 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Exponent
            The base64 encoded exponent for the RSA algorithm. This should be in big endian order and is expected to
            be 3 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Version
            The base64 encoded version of the RSA algorithm. This should be in big endian order and is expected to
            be 4 bytes long, but will be trimmed or padded if it is shorter or longer. This parameter is optional
            and is just displayed in verbose output.
     
        .PARAMETER D
            The base64 encoded D parameter, private exponent, for the RSA algorithm. This should be in big endian order and is expected to
            be 256 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER P
            The base64 encoded P parameter, prime1, for the RSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Q
            The base64 encoded Q parameter, prime2, for the RSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER DP
            The base64 encoded DP parameter, exponent1, for the RSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER DQ
            The base64 encoded DQ parameter, exponent2, for the RSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER IQ
            The base64 encoded IQ parameter, coefficient, for the RSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .EXAMPLE
            [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider -Modulus $Mod -Exponent $Ex
 
            This creates an RSA public key with the base64 encoded modulus and exponent provided in the variables
            $Mod and $Ex.
 
        .EXAMPLE
            [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-RSACryptoServiceProvider `
                -Modulus $Mod `
                -Exponent $Ex `
                -D $D `
                -P $P `
                -Q $Q `
                -DP $DP `
                -DQ $DQ `
                -IQ $IQ
 
            Creates an RSA Private key with the RSA Parameters provided. These parameters could be extracted from an
            XML file or decoded from a PEM ASN.1 data structure.
 
        .INPUTS
            None
         
        .OUTPUTS
            System.Security.Cryptography.RSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/23/2018
    #>

    [CmdletBinding(DefaultParameterSetName = "Public")]
    [OutputType([System.Security.Cryptography.RSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Modulus,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Exponent,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]$Version,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$D,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$P,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$Q,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$DP,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$DQ,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$IQ
    )

    Begin {
    }

    Process {
        if (-not [System.String]::IsNullOrEmpty($Version))
        {
            [System.Byte[]]$VersionBytes = [System.Convert]::FromBase64String($Version)
            
            $VersionBytes = Set-ByteArrayPadding -Length 4 -InputObject (Invoke-ByteArrayTrim -InputObject $VersionBytes -DesiredLength 4 -TrimStart)

            # Reverse here because we want to use ToUInt32 to convert it
            if ([System.BitConverter]::IsLittleEndian)
            {
                [System.Array]::Reverse($VersionBytes)
            }

            $VersionNumber = [System.BitConverter]::ToUInt32($VersionBytes, 0)

            Write-Verbose -Message "Algorithm version: $VersionNumber."
        }

        # All of the RSAParameters are big-endian
        # https://msdn.microsoft.com/en-us/library/ms867080.aspx

        # Length of 256
        [System.Byte[]]$ModulusBytes = [System.Convert]::FromBase64String($Modulus)

        # Trim from the start of a big endian array, then set the padding on the left
        # in case the array was actually less than the desired length
        $ModulusBytes = Set-ByteArrayPadding -Length 256 -InputObject (Invoke-ByteArrayTrim -InputObject $ModulusBytes -DesiredLength 256 -TrimStart)

        # Length of 3
        [System.Byte[]]$ExponentBytes = [System.Convert]::FromBase64String($Exponent)
          
        # This will probably come out as at least 4 bytes
        # Trim from the start of a big endian array, then set the padding on the left
        # in case the array was actually less than the desired length
        $ExponentBytes = Set-ByteArrayPadding -Length 3 -InputObject (Invoke-ByteArrayTrim -InputObject $ExponentBytes -DesiredLength 3 -TrimStart)

        # Create the params here to simplify processing for the
        # private key components
        [System.Security.Cryptography.RSAParameters]$Params = New-Object -TypeName System.Security.Cryptography.RSAParameters
        $Params.Modulus = $ModulusBytes
        $Params.Exponent = $ExponentBytes

        if ($PSCmdlet.ParameterSetName -eq "Private")
        {
            # Length of 256
            [System.Byte[]]$DBytes = [System.Convert]::FromBase64String($D)        
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $DBytes = Set-ByteArrayPadding -Length 256 -InputObject (Invoke-ByteArrayTrim -InputObject $DBytes -DesiredLength 256 -TrimStart)

            # Length of 128
            [System.Byte[]]$PBytes = [System.Convert]::FromBase64String($P)
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $PBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $PBytes -DesiredLength 128 -TrimStart)

            # Length of 128
            [System.Byte[]]$QBytes = [System.Convert]::FromBase64String($Q)
            $QBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $QBytes -DesiredLength 128 -TrimStart)

            # Length of 128
            [System.Byte[]]$DPBytes =[System.Convert]::FromBase64String($DP)
            $DPBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $DPBytes -DesiredLength 128 -TrimStart) 

            # Length of 128
            [System.Byte[]]$DQBytes = [System.Convert]::FromBase64String($DQ)
            $DQBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $DQBytes -DesiredLength 128 -TrimStart)

            # Length of 128
            [System.Byte[]]$IQBytes = [System.Convert]::FromBase64String($IQ)
            $IQBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $IQBytes -DesiredLength 128 -TrimStart)

            # RSA Params need to be in Big Endian format
            $Params.D = $DBytes
            $Params.P = $PBytes
            $Params.Q = $QBytes
            $Params.DP = $DPBytes
            $Params.DQ = $DQBytes
            $Params.InverseQ = $IQBytes
        }
        
        [System.Security.Cryptography.RSACryptoServiceProvider]$RSA = New-Object -TypeName System.Security.Cryptography.RSACryptoServiceProvider

        try {
            $RSA.ImportParameters($Params)
                
            Write-Output -InputObject $RSA
        }
        catch [Exception] {
            Write-Error -Exception $_.Exception -ErrorAction Stop
        }
    }

    End {
    }
}

Function New-DSACryptoServiceProvider {
    <#
        .SYNOPSIS
            Creates a new DSACryptoServiceProvider object from the specified parameters.
         
        .DESCRIPTION
            This cmdlet wraps creating the DSAParameters object and importing those into the DSACryptoServiceProvider object.
            The parameters are supplied as base64 encoded strings that were derived from byte arrays stored in
            big endian order. The parameters are trimmed down to the required lengths before being imported, so if
            the parameters are stored in PEM format with leading padding bytes, they are automatically trimmed and
            can be supplied as is.
 
            This cmdlet is typically called by other cmdlets in the BAMCIS.Crypto module, but can be called directly
            if you are manually parsing or creating DSACryptoServiceProviders from PEM or XML files.
 
        .PARAMETER Counter
            The counter validation parameter.
 
        .PARAMETER G
            The base64 encoded DSA group generator for the DSA algorithm. This should be in big endian order and is expected to
            be 20 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER J
            The base64 encoded J parameter for the DSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
     
        .PARAMETER P
            The base64 encoded prime2 number for the DSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Q
            The base64 encoded prime1 number for the DSA algorithm. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Seed
            The base64 encoded seed value to use. This should be in big endian order and is expected to
            be 20 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER X
            The base64 encoded DSA private exponent. This should be in big endian order and is expected to
            be 20 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .PARAMETER Y
            The base64 encoded DSA public exponent. This should be in big endian order and is expected to
            be 128 bytes long, but will be trimmed or padded if it is shorter or longer.
 
        .EXAMPLE
            [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider -P $P -Q $Q -G $G -Y $Y
 
            This creates an DSA public key.
 
        .EXAMPLE
            [System.Security.Cryptography.RSACryptoServiceProvider]$DSA = New-DSACryptoServiceProvider `
                -P $P `
                -Q $Q `
                -G $G `
                -X $X
 
            Creates a DSA Private key with the RSA Parameters provided. These parameters could be extracted from an
            XML file or decoded from a PEM ASN.1 data structure.
 
        .INPUTS
            None
         
        .OUTPUTS
            System.Security.Cryptography.DSACryptoServiceProvider
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 1/26/2018
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Cryptography.DSACryptoServiceProvider])]
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$P,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Q,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$G,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]$J,

        [Parameter()]
        [ValidateRange(0, [System.Int32]::MaxValue)]
        [System.Int32]$Counter = 0,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]$Seed,

        [Parameter(Mandatory = $true, ParameterSetName = "Private")]
        [ValidateNotNullOrEmpty()]
        [System.String]$X,

        [Parameter(Mandatory = $true, ParameterSetName = "Public")]
        [ValidateNotNullOrEmpty()]
        [System.String]$Y,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [System.String]$Version
    )

    Begin {
    }

    Process {
        if (-not [System.String]::IsNullOrEmpty($Version))
        {
            [System.Byte[]]$VersionBytes = [System.Convert]::FromBase64String($Version)
            
            $VersionBytes = Set-ByteArrayPadding -Length 4 -InputObject (Invoke-ByteArrayTrim -InputObject $VersionBytes -DesiredLength 4 -TrimStart)

            # Reverse here because we want to use ToUInt32 to convert it
            if ([System.BitConverter]::IsLittleEndian)
            {
                [System.Array]::Reverse($VersionBytes)
            }

            $VersionNumber = [System.BitConverter]::ToUInt32($VersionBytes, 0)

            Write-Verbose -Message "Algorithm version: $VersionNumber."
        }

        # All of the DSAParameters are big-endian
        # https://msdn.microsoft.com/en-us/library/ms867080.aspx

        # Length of 128
        [System.Byte[]]$PBytes = [System.Convert]::FromBase64String($P)

        # Trim from the start of a big endian array, then set the padding on the left
        # in case the array was actually less than the desired length
        $PBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $PBytes -DesiredLength 128 -TrimStart)

        # Length of 20
        [System.Byte[]]$QBytes = [System.Convert]::FromBase64String($Q)
          
        # Trim from the start of a big endian array, then set the padding on the left
        # in case the array was actually less than the desired length
        $QBytes = Set-ByteArrayPadding -Length 20 -InputObject (Invoke-ByteArrayTrim -InputObject $QBytes -DesiredLength 20 -TrimStart)

        # Length of 128
        [System.Byte[]]$GBytes = [System.Convert]::FromBase64String($G)
          
        # Trim from the start of a big endian array, then set the padding on the left
        # in case the array was actually less than the desired length
        $GBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $GBytes -DesiredLength 128 -TrimStart)

        # Create the params here to simplify processing for the
        # private key components
        [System.Security.Cryptography.DSAParameters]$Params = New-Object -TypeName System.Security.Cryptography.DSAParameters
        $Params.P = $PBytes
        $Params.Q = $QBytes
        $Params.G = $GBytes
        
        if ($PSBoundParameters.ContainsKey("Counter"))
        {
            $Params.Counter = $Counter
        }

        if (-not [System.String]::IsNullOrEmpty($Seed))
        {
            # Length of 20
            [System.Byte[]]$SeedBytes = [System.Convert]::FromBase64String($Seed)
          
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $SeedBytes = Set-ByteArrayPadding -Length 20 -InputObject (Invoke-ByteArrayTrim -InputObject $SeedBytes -DesiredLength 20 -TrimStart)

            $Params.Seed = $SeedBytes
        }

        if (-not [System.String]::IsNullOrEmpty($J))
        {
            # Length of 20
            [System.Byte[]]$JBytes = [System.Convert]::FromBase64String($J)
          
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $JBytes = Set-ByteArrayPadding -Length 20 -InputObject (Invoke-ByteArrayTrim -InputObject $JBytes -DesiredLength 20 -TrimStart)

            $Params.J = $JBytes
        }

        if ($PSCmdlet.ParameterSetName -eq "Private")
        {
            # Length of 20
            [System.Byte[]]$XBytes = [System.Convert]::FromBase64String($X)        
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $XBytes = Set-ByteArrayPadding -Length 20 -InputObject (Invoke-ByteArrayTrim -InputObject $XBytes -DesiredLength 20 -TrimStart)

            $Params.X = $XBytes
        }
        else
        {
            # Length of 128
            [System.Byte[]]$YBytes = [System.Convert]::FromBase64String($Y)        
            # Trim from the start of a big endian array, then set the padding on the left
            # in case the array was actually less than the desired length
            $YBytes = Set-ByteArrayPadding -Length 128 -InputObject (Invoke-ByteArrayTrim -InputObject $YBytes -DesiredLength 128 -TrimStart)

            $Params.Y = $YBytes
        }
        
        [System.Security.Cryptography.DSACryptoServiceProvider]$DSA = New-Object -TypeName System.Security.Cryptography.DSACryptoServiceProvider

        try
        {
            $DSA.ImportParameters($Params)
                
            Write-Output -InputObject $DSA
        }
        catch [Exception]
        {
            Write-Error -Exception $_.Exception -ErrorAction Stop
        }
    }

    End {
    }
}

#endregion