Skip to content

Instantly share code, notes, and snippets.

@realslacker
Last active November 15, 2020 18:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realslacker/b14f686ecd6dbcc229da5cb7a1ca92d9 to your computer and use it in GitHub Desktop.
Save realslacker/b14f686ecd6dbcc229da5cb7a1ca92d9 to your computer and use it in GitHub Desktop.
Work with mRemoteNG passwords... Functionality has been rolled into https://github.com/realslacker/PSmRemoteNG
Add-Type -Path 'C:\Program Files (x86)\mRemoteNG\BouncyCastle.Crypto.dll'
function ConvertFrom-MRNGSecurePassword {
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]
$EncryptedMessage,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[securestring]
$EncryptionKey
)
$Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $EncryptionKey ) )
$EncryptedMessageBytes = [convert]::FromBase64String( $EncryptedMessage )
$Engine = [Org.BouncyCastle.Crypto.Engines.AesEngine]::new()
$Cipher = [Org.BouncyCastle.Crypto.Modes.GcmBlockCipher]::new($Engine)
$Encoding = [System.Text.Encoding]::UTF8
$SecureRandom = [Org.BouncyCastle.Security.SecureRandom]::new()
$NonceBitSize = 128
$MacBitSize = 128
$KeyBitSize = 256
$SaltBitSize = 128
$KeyDerivationIterations = 1000
$MinPasswordLength = 1
$Salt = New-Object byte[] ( $SaltBitSize/8 )
[array]::Copy( $EncryptedMessageBytes, 0, $Salt, 0, $Salt.Length )
Write-Verbose ( "Salt: {0}" -f ($Salt -join ',') ) -Verbose
$PasswordInBytes = [Org.BouncyCastle.Crypto.PbeParametersGenerator]::Pkcs5PasswordToBytes( $Password.ToCharArray() )
$KeyGenerator = [Org.BouncyCastle.Crypto.Generators.Pkcs5S2ParametersGenerator]::new()
$KeyGenerator.Init( $PasswordInBytes, $Salt, $KeyDerivationIterations )
$KeyParameter = $KeyGenerator.GenerateDerivedMacParameters($KeyBitSize)
$KeyBytes = $KeyParameter.GetKey()
$CipherStream = New-Object System.IO.MemoryStream (, $EncryptedMessageBytes )
$CipherReader = New-Object System.IO.BinaryReader ( $CipherStream )
$Payload = $CipherReader.ReadBytes( $Salt.Length )
$Nonce = $CipherReader.ReadBytes( $NonceBitSize / 8 )
Write-Verbose ( 'Nonce: {0}' -f ( $Nonce -join ',' ) ) -Verbose
$Parameters = [Org.BouncyCastle.Crypto.Parameters.AeadParameters]::new( $KeyParameter, $MacBitSize, $Nonce, $Payload )
$Cipher.Init( $false, $Parameters )
$CipherTextBytes = $CipherReader.ReadBytes( $EncryptedMessageBytes.Length - $Nonce.Length )
$PlainText = New-Object byte[] ( $Cipher.GetOutputSize( $CipherTextBytes.Length ) )
$Len = $Cipher.ProcessBytes( $CipherTextBytes, 0, $CipherTextBytes.Length, $PlainText, 0 )
$Cipher.DoFinal( $PlainText, $Len ) > $null
$Encoding.GetString( $PlainText )
}
function ConvertTo-MRNGSecurePassword {
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]
$Message,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[securestring]
$EncryptionKey
)
$Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $EncryptionKey ) )
$MessageBytes = $Encoding.GetBytes($Message)
$Engine = [Org.BouncyCastle.Crypto.Engines.AesEngine]::new()
$Cipher = [Org.BouncyCastle.Crypto.Modes.GcmBlockCipher]::new($Engine)
$Encoding = [System.Text.Encoding]::UTF8
$SecureRandom = [Org.BouncyCastle.Security.SecureRandom]::new()
$NonceBitSize = 128
$MacBitSize = 128
$KeyBitSize = 256
$SaltBitSize = 128
$KeyDerivationIterations = 1000
$MinPasswordLength = 1
$Salt = New-Object byte[] ( $SaltBitSize/8 )
$SecureRandom.NextBytes($Salt)
Write-Verbose ( "Salt: {0}" -f ($Salt -join ',') ) -Verbose
$PasswordInBytes = [Org.BouncyCastle.Crypto.PbeParametersGenerator]::Pkcs5PasswordToBytes( $Password.ToCharArray() )
$KeyGenerator = [Org.BouncyCastle.Crypto.Generators.Pkcs5S2ParametersGenerator]::new()
$KeyGenerator.Init( $PasswordInBytes, $Salt, $KeyDerivationIterations )
$KeyParameter = $KeyGenerator.GenerateDerivedMacParameters($KeyBitSize)
$KeyBytes = $KeyParameter.GetKey()
$Payload = New-Object byte[] ( $Salt.Length )
[array]::Copy( $Salt, 0, $Payload, 0, $Salt.Length )
$Nonce = New-Object byte[] ( $NonceBitSize / 8 )
$SecureRandom.NextBytes( $Nonce, 0, $Nonce.Length )
Write-Verbose ( 'Nonce: {0}' -f ( $Nonce -join ',' ) ) -Verbose
$Parameters = [Org.BouncyCastle.Crypto.Parameters.AeadParameters]::new( $KeyParameter, $MacBitSize, $Nonce, $Payload )
$Cipher.Init( $true, $Parameters )
$CipherText = New-Object byte[] ($Cipher.GetOutputSize( $MessageBytes.Length ))
$Len = $Cipher.ProcessBytes( $MessageBytes, 0, $MessageBytes.Length, $CipherText, 0 )
$Cipher.DoFinal( $CipherText, $Len ) > $null
$CombinedStream = New-Object System.IO.MemoryStream
$BinaryWriter = New-Object System.IO.BinaryWriter ( $CombinedStream )
$BinaryWriter.Write( $Payload )
$BinaryWriter.Write( $Nonce )
$BinaryWriter.Write( $CipherText )
[convert]::ToBase64String( $CombinedStream.ToArray() )
}
function New-MRNGRandomString ( $Length = 20 ) {
$SecureRandom = [Org.BouncyCastle.Security.SecureRandom]::new()
$StringBuilder = [System.Text.StringBuilder]::new()
[char[]]$AvailableChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+|[]{};:',./<>?"
for ( $i = 0; $i -lt $Length; $i ++ ) {
$RandomIndex = $SecureRandom.Next( $AvailableChars.Length - 1 )
$StringBuilder.Append( $AvailableChars[ $RandomIndex ] ) > $null
}
return $StringBuilder.ToString()
}
@reddwarf666
Copy link

Hi
I just found this module and wanted to thank you so, so much!

I have been using mRemoteNG for years now and it is one of the Windows apps I keep using because our team works 99% with Windows and MremoteNG. Nothing wrong with mRemoteNG, nothing at all and it is to date one of the best RDP managers out there.

But I have a Macbook and need to run a virtual system to run mRemoteNG. But I do not always want to start Windows just for that 1 tool. I know there are other RDP managers for MacOS but the thing holding me back was the fact that whatever tool I looked at would either not support importing mRemoteNG files or if they can they will discard the password.

So for years I had the idea/dream of having an RDP manager on MacOS that I would create a connection file for via PowerShell and use the mRemoteNG confCons file as source. The idea always broke at the password hurdle and I stopped before investing too much effort.

Our passwords change too frequently there are just too many servers to have to manually enter these passwords, I wanted to have an automated solution where 1 script does the transfer and everything just works, passwords included.

Thanks to your wonderful work I now have that dream! Finally, after years of hoping to solve the complete puzzle I can complete the story :-)

It consists of PowerShell Core, Royal TSX, a PS Core module of Royal to manipulate their connections file(s) and your password decoder. I ran a first test script tonight, pulling connections from confCons, decoding passwords, creating the same connections in a Royal TSX document and it all worked perfectly fine. Yay!

Again, I cannot thank you enough for solving the final piece of the puzzle and make this happen!

Regards,
Steven

@realslacker
Copy link
Author

@reddwarf666 So glad it's useful to someone else!

@reddwarf666
Copy link

Hi @realslacker

I recently upgraded to PowerShell Core 7 and when I ran my script which has this function "ConvertFrom-MRNGSecurePassword" it crashes on "$Cipher.DoFinal( $PlainText, $Len ) > $null"

Example:
$EncyptionKey = ConvertTo-SecureString -String "mR3m" -AsPlainText -Force
$DecodedPassword = ConvertFrom-MRNGSecurePassword -EncryptedMessage "8k8a2vU8B26YnxvtYaLX9EOqpAr4xnYziSa4thiapTt/J9y1VxcjYfkxu8wPldHXkk5Sg7qeh2Ce3ofSpS3Pc5obDsk7cE2UZ4Fij6SA" -EncryptionKey $EncyptionKey
MethodInvocationException: /Users/stevenspierenburg/Documents/mremote_testing/PSmRemoteNG-master/mRemoteNG-Password-Util.psm1:84
Line |
84 | $Cipher.DoFinal( $PlainText, $Len ) > $null
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "DoFinal" with "2" argument(s): "mac check in GCM failed"

When I used the module as you have on "https://github.com/realslacker/PSmRemoteNG" I face the same issue:
ConvertFrom-MRNGSecureString -EncryptedMessage "8k8a2vU8B26YnxvtYaLX9EOqpAr4xnYziSa4thiapTt/J9y1VxcjYfkxu8wPldHXkk5Sg7qeh2Ce3ofSpS3Pc5obDsk7cE2UZ4Fij6SA"
MethodInvocationException:
Line |
104 | $Cipher.DoFinal( $PlainTextByteArray, $Len ) > $null
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "DoFinal" with "2" argument(s): "mac check in GCM failed"
�~�t���p} �Ts����)C�ˋ@���

I downloaded the latest "BouncyCastle.Crypto.dll" from the site "http://www.bouncycastle.org/csharp/#RELEASENOTES186" and it did not resolve this problem I am seeing.

Do you have any idea what might be wrong?

System Information:
MacOS Catalina 10.15
PowerShell Core 7

DotNet --info output:
Version: 2.1.4
Commit SHA-1 hash: 5e8add2190
Runtime Environment:
OS Name: Mac OS X
OS Version: 10.15
OS Platform: Darwin
RID: osx.10.12-x64
Base Path: /usr/local/share/dotnet/sdk/2.1.4/
Microsoft .NET Core Shared Framework Host
Version : 2.0.5
Build : 17373eb129b3b05aa18ece963f8795d65ef8ea54

@reddwarf666
Copy link

reddwarf666 commented May 21, 2020

@realslacker Any idea what this could be?
I tried Powershell 7.0.1 but that did not solve the issue
I opened a issue on github for Powershell Core to see if they have any ideas:
PowerShell/PowerShell#12741

@reddwarf666
Copy link

@realslacker It's been a while now and I thought to check in and see if maybe you found a solution to the issue?

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