Search code examples
.netpowershell.net-coreassembliespowershell-core

Difference between PowerShell 5.1 and 7 when working with certificates


Below is a small sample of the script that I am making. I need it to work in PowerShell 5.1 At the moment it only works in PS7. I installed the package "System.IdentityModel.Tokens.Jwt 7.0.3" and copied the dll's I am referencing in the script folder. When I run this script in PS7 I get ouput from "new-object Microsoft.IdentityModel.Tokens.X509SigningCredentials($x509cert)" On PS5.1 I get the error:

System.Management.Automation.MethodInvocationException: Exception calling ".ctor" with "1" argument(s): "The type initializer for 'PerTypeValues`1' threw an exception." ---> System.TypeInitializationException: The type initializer for 'PerTypeValues`1' threw an exception. ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
   at System.SpanHelpers.PerTypeValues`1.MeasureArrayAdjustment()
   at System.SpanHelpers.PerTypeValues`1..cctor()
   --- End of inner exception stack trace ---
   at Microsoft.IdentityModel.Tokens.Base64UrlEncoder.Encode(Byte[] inArray, Int32 offset, Int32 length)
   at Microsoft.IdentityModel.Tokens.X509SecurityKey..ctor(X509Certificate2 certificate)
   at Microsoft.IdentityModel.Tokens.SigningCredentials..ctor(X509Certificate2 certificate)
   at Microsoft.IdentityModel.Tokens.X509SigningCredentials..ctor(X509Certificate2 certificate)
   --- End of inner exception stack trace ---
   at System.Management.Automation.DotNetAdapter.AuxiliaryConstructorInvoke(MethodInformation methodInformation, Object[] arguments, Object[] originalArguments)
   at System.Management.Automation.DotNetAdapter.ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, Object[] arguments)
   at Microsoft.PowerShell.Commands.NewObjectCommand.CallConstructor(Type type, ConstructorInfo[] constructors, Object[] args)

Can someone explain to me what the difference is between 5.1 and 7 other than .NET version? And why this piece of code won't run on 5.1?

$CertPassWord       = "password" # Password used for creating the certificate
$CertificatePath_Pfx = "C:\Temp\Certs\test.pfx" # Path where the certificate is saved 
 

[System.Reflection.Assembly]::LoadFrom("C:\Temp\Certs\Microsoft.IdentityModel.Tokens.dll")


$x509cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2($CertificatePath_Pfx, $CertPassWord)
new-object Microsoft.IdentityModel.Tokens.X509SigningCredentials($x509cert)

Solution

  • I found the problem why it wasn't running in PS 5.1. I updated my question with the detailed exception. The dll "System.Runtime.CompilerServices.Unsafe.dll" wasn't loaded in PowerShell session causing the script to fail. In PS 7 this dll is loaded by default. I only had a newer version then was expected so I needed a binding redirect in PowerShell 5.1. I got the way to do that from this question: Powershell config assembly redirect

    This is my example script that is working in PS 5.1

    $CertPassWord       = "password" # Password used for creating the certificate
    $CertificatePath_Pfx = "C:\Temp\Certs\test.pfx" # Path where the certificate is saved 
     
    
    [System.Reflection.Assembly]::LoadFrom("C:\Temp\Certs\Microsoft.IdentityModel.Tokens.dll")
    [System.Reflection.Assembly]::LoadFrom("C:\Temp\Certs\System.Runtime.CompilerServices.Unsafe.dll")
    
    
    
    $OnAssemblyResolve = [System.ResolveEventHandler] {
        param($sender, $e)
    
        $searchFor = $null
        if ($e.Name -match "(.*?), .*") {
            $searchFor = $matches[1]
        }
        foreach ($a in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
            Write-Host $a
            $foundItem = $null
            if ($a.FullName -match "(.*?), .*") {
                $foundItem = $matches[1]
            }
        
            if ($foundItem -eq $searchFor) {
                return $a
            }
        }
        return $null
    }
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)
    
    try {
    $x509cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2($CertificatePath_Pfx, $CertPassWord)
    new-object Microsoft.IdentityModel.Tokens.X509SigningCredentials($x509cert)
    }
    
    catch {
         Write-Host "An error occurred:"
      Write-Host $_.Exception
    }