Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save cdelashmutt-pivotal/03aca91a9d01e6fc72771d3fa01cf5ea to your computer and use it in GitHub Desktop.
Save cdelashmutt-pivotal/03aca91a9d01e6fc72771d3fa01cf5ea to your computer and use it in GitHub Desktop.
Generate OpenSSH compatible public and private key files in Powershell
<#
.SYNOPSIS
Create an OpenSSH compatible Public and Private Key in pure Powershell
.DESCRIPTION
Many scripts rely on the openssh or openssl tools to be available to generate public and private keys compatible with OpenSSH, but this script relies purely on Powershell (and some .NET classes already included) to generate public and private keys.
.EXAMPLE
PS /git/scripts> ./Create-OpenSSHPubAndPrivateKeys.ps1 -PublicKeyPath my-key.pub -PrivateKeyPath my-key
.LINK
mailto:grog@grogscave.net
#>
[CmdletBinding()]
param (
# The path to use to store the public key file
[Parameter(Mandatory=$true)]
[string]
$PublicKeyFile,
# The path to use to store the private key file
[Parameter(Mandatory=$true)]
[string]
$PrivateKeyFile
)
# SSH key generation significantly cribbed from https://stackoverflow.com/posts/3588388/revisions
# and https://www.thedigitalcatonline.com/blog/2018/04/25/rsa-keys/
function Export-SSHPublicKey([System.Security.Cryptography.RSACryptoServiceProvider] $csp) {
$result = @((Get-LengthAndData ([System.Text.Encoding]::ASCII.GetBytes("ssh-rsa"))))
$rsaParams = $rsa.ExportParameters($false)
$result += Get-LengthAndData $rsaParams.Exponent
$result += Get-LengthAndData $(@(0x0)+$rsaParams.Modulus)
return [System.Convert]::ToBase64String($result)
}
function Get-LengthAndData([byte[]] $data) {
$reverseSize = [System.BitConverter]::GetBytes($data.Length)
[System.Array]::Reverse($reverseSize)
return $reverseSize + $data
}
# Generate RSA Key Pair
Write-Host "Creating a 2048-bit RSA Keypair"
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 2048
Write-Host "Saving private and public keys as $PrivateKeyFile and $PublicKeyFile"
#Private Key
"-----BEGIN RSA PRIVATE KEY-----`n" + [System.Convert]::ToBase64String( $rsa.ExportRSAPrivateKey(), [Base64FormattingOptions]::InsertLineBreaks) + "`n-----END RSA PRIVATE KEY-----" | Out-File -Encoding utf8NoBOM $PrivateKeyFile
#Public Key
"ssh-rsa " + (Export-SSHPublicKey $rsa) + " tkg-vms" | Out-File -Encoding utf8NoBOM $PublicKeyFile
@schittli
Copy link

schittli commented Aug 12, 2022

Hello
Thank you very much for sharing your Script!

Do you know why $rsa.ExportRSAPrivateKey() no longer works?
It looks like System.Security.Cryptography.RSACryptoServiceProviderno longer has an ExportRSAPrivateKey() method:

Creating a 2048-bit RSA Keypair
Saving private and public keys as C:\temp\my-key.pub and C:\temp\my-key
Method invocation failed because [System.Security.Cryptography.RSACryptoServiceProvider] does not contain a method named 'ExportRSAPrivateKey'.
At C:\Scripts\PowerShell\Git\Create-OpenSSHPubAndPrivateKeys.ps1:49 char:1
+ "-----BEGIN RSA PRIVATE KEY-----`n" + [System.Convert]::ToBase64String( $rsa.ExportRSAPrivateKey(), 
+                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

@cdelashmutt-pivotal
Copy link
Author

Hello, @schittli, I'm glad you found the script interesting.

As to the method call, I took a look at the docs for that API call at https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsa.exportrsaprivatekey?view=net-6.0&viewFallbackFrom=netframework-4.8#system-security-cryptography-rsa-exportrsaprivatekey. It seems like that method was available in .NET Core 3.0, .NET Core 3.1, and .NET 5 and beyond.

I suspect that you might be on an older version of .NET? There are some instructions at https://docs.microsoft.com/en-us/dotnet/core/install/how-to-detect-installed-versions?pivots=os-windows to check which versions of the .NET Runtime you have installed, but I think those instructions might only work if you are on a later version of your OS. I'll have to do some checking to see if there is an alternative way in older .NET Framework versions to get an RSA type Private Key exported.

@cdelashmutt-pivotal
Copy link
Author

Also, it looks like older versions of Powershell (even up to Powershell 5.1) still use the older .NET Framework 4.8 and lower. You might have better luck on Powershell 6+ if you can use those on whatever machine you are on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment