Sunday 22 July 2018

Self-signed certificates in .NET with RsaCryptoServiceProvider

This article is written after experiencing how difficult it is today to create self-signed certificates that works with .NET and specificially WCF. If you use the default method in Windows, using the cmdlet New-SelfSignedCertificate in Powershell, chances are high that you will stumble upon this error: System.Security.Cryptography.CryptographicException: Invalid provider type specified. A few years back, creating certificates was way easier using makecert.exe Then Windows required the certificates to have length of minimum 1024. After some years passed, makecert.exe was deprecated. Microsoft now offers .NET developers to create a self signed certificate the cmdlet New-SelfSignedCertificate to use in development and test environments. Actually it turns out that you still must adjust the private part of certificate to use the RSACryptoServiceProvider by using OpenSSL to achieve this and then import to the certificate, sadly Microsoft gives developers tools for self-signed certificate generation that needs to use third-part libraries such as SSLR to work properly with .NET, at least this is the case with WCF.. Here is the Powershell script I ended up with:
 Write-Host "Generating a self signed certificate for client in MSMQ WCF demo"

$clientCertFile = "C:\temp\MSMQWcfDemoClient.pfx" 

$clientCertFileRsaFormatted = "C:\temp\MSMQWcfDemoClient.RSAConverted.pfx"

$cert = New-SelfSignedCertificate -Subject "CN=MSMQWcfDemoClient" -certstorelocation cert:\localmachine\my `
-NotAfter (Get-Date).AddYears(3) -KeyLength 2048 -KeySpec KeyExchange
$pwd = ConvertTo-SecureString -String ‘bongo’ -Force -AsPlainText 
$path = 'cert:\localMachine\my\' + $cert.thumbprint 
Export-PfxCertificate -cert $path -FilePath $clientCertFile -Password $pwd

$clientCertPasswordSec = ConvertTo-SecureString "bongo" -AsPlainText -Force


Write-Host "Generating a self signed certificate for server in MSMQ WCF demo"

$serverCertFile = "C:\temp\MSMQWcfDemoServer.pfx" 

$serverCertFileRsaFormatted =  "C:\temp\MSMQWcfDemoServer.RSAConverted.pfx"

$certServer = New-SelfSignedCertificate -Subject "CN=MSMQWcfDemoserver" -certstorelocation cert:\localmachine\my `
  -NotAfter (Get-Date).AddYears(3) -KeyExportPolicy Exportable -KeyLength 2048  -KeySpec KeyExchange
$pwdServer = ConvertTo-SecureString -String ‘kongo’ -Force -AsPlainText
$pathServer = 'cert:\localMachine\my\' + $certServer.thumbprint 
Export-PfxCertificate -cert $pathServer -FilePath $serverCertFile -Password $pwdServer

$serverCertPasswordSec = ConvertTo-SecureString "kongo" -AsPlainText -Force

$command = @'
cmd.exe /c c:\temp\rsaconvert.bat
'@

Write-Host "Starting bat file to convert from CNG to RSA format.." 

Invoke-Expression -Command:$command 

Write-Host "Importing RSA formatted certificates.."

Import-PfxCertificate -FilePath $clientCertFileRsaFormatted -CertStoreLocation Cert:\LocalMachine\My -Password $clientCertPasswordSec
Import-PfxCertificate -FilePath $clientCertFileRsaFormatted -CertStoreLocation Cert:\LocalMachine\root -Password $clientCertPasswordSec



Import-PfxCertificate -FilePath $serverCertFileRsaFormatted -CertStoreLocation Cert:\LocalMachine\My -Password $serverCertPasswordSec
Import-PfxCertificate -FilePath $serverCertFileRsaFormatted -CertStoreLocation Cert:\LocalMachine\root -Password $serverCertPasswordSec

The powershell script uses a bat file that calls Openssl to convert the pfx certificate to use the RSACryptoServiceProvider. This is how the bat file looks like:
echo Generating RSA format certificates for server using OpenSSL..

c:\openssl\openssl.exe pkcs12 -in "C:\temp\MSMQWcfDemoserver.pfx" -nokeys -out "C:\temp\MSMQWcfDemoserver.cer" -passin "pass:kongo"
c:\openssl\OpenSSL.exe pkcs12 -in "C:\temp\MSMQWcfDemoserver.pfx" -nocerts -out "C:\temp\MSMQWcfDemoserver.pem" -passin "pass:kongo" -passout "pass:kongo"
c:\openssl\OpenSSL.exe rsa -inform PEM -in "C:\temp\MSMQWcfDemoserver.pem" -out "C:\temp\MSMQWcfDemoserver.rsa" -passin "pass:kongo" -passout "pass:kongo"
c:\openssl\openssl.exe pkcs12 -export -in  "C:\temp\MSMQWcfDemoserver.cer" -inkey "C:\temp\MSMQWcfDemoserver.rsa" -out "C:\temp\MSMQWcfDemoserver.RSAConverted.pfx" -passin "pass:kongo" -passout "pass:kongo"


echo Generating RSA format certificates for client using OpenSSL..

c:\openssl\openssl.exe pkcs12 -in "C:\temp\MSMQWcfDemoclient.pfx" -nokeys -out "C:\temp\MSMQWcfDemoclient.cer" -passin "pass:bongo"
c:\openssl\OpenSSL.exe pkcs12 -in "C:\temp\MSMQWcfDemoclient.pfx" -nocerts -out "C:\temp\MSMQWcfDemoclient.pem" -passin "pass:bongo" -passout "pass:bongo"
c:\openssl\OpenSSL.exe rsa -inform PEM -in "C:\temp\MSMQWcfDemoclient.pem" -out "C:\temp\MSMQWcfDemoclient.rsa" -passin "pass:bongo" -passout "pass:bongo"
c:\openssl\openssl.exe pkcs12 -export -in  "C:\temp\MSMQWcfDemoclient.cer" -inkey "C:\temp\MSMQWcfDemoclient.rsa" -out "C:\temp\MSMQWcfDemoclient.RSAConverted.pfx" -passin "pass:bongo" -passout "pass:bongo"


The bat file uses OpenSSL to extract (dismantle) the public part of the certificate into a .cer file (this part could have been done with MMC). The next step is to generate a from our existing pfx file to export to a pem file and then a rsa file with OpenSSL. We then meld the .cer file and .rsa file into a converted .pfx file. This file is with the Powershell script automatically imported into the certificate store Personal (My) of certificate location Local Computer. The Powershell script also Import-PfxCertificate to Trusted Root Certification Authorities. Anyways, my goal was to give you a demo about .NET, WCF and NetMsmqBinding using message security, but I first had to get over this hurdle to be able to have some protection in WCF with certificate and had no idea that Microsoft had given developers so cumbersome tools to generate a self signed certificate to actually work with WCF. Now my MSMQ message queue is filled with encrypted and protection MSMQ messages (containing WCF message to be consumed)! :)
Note: the MSMQ queue was inspected using this Powershell script:

#
# InspectMessageQueueWcfContent.ps1
#


#
# InspectMessageQueue.ps1
#

[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.IO") | Out-Null


$queuePath = ".\private$\demoqueue3"

Write-Host "Powershell MSMQ queue WCF inspector v0.1. Inspecting queue contents of the queue: $queuePath"
Write-Host ""


Run-MainDemoIterateMsmq $queuePath



Function Get-XmlFromWcfMessage([System.Messaging.Message] $msg) {
   
    $doc = New-Object System.Xml.XmlDocument;
    $messageLength = [int] $msg.BodyStream.Length


    $buffer = New-Object byte[] $messageLength

    
    $msg.BodyStream.Read($buffer, 0, $messageLength)

    $envelopeStart = Find-SoapEnvelopeStart($buffer)

    $envelopeStart = $envelopeStart - 0

    $envelopeLength = $($buffer.Length - $envelopeStart)
    #Write-Host $envelopeStart


    $stream = New-Object System.IO.MemoryStream($buffer, $envelopeStart, $envelopeLength)

    $elm = New-Object System.ServiceModel.Channels.BinaryMessageEncodingBindingElement
    $elm.ReaderQuotas.MaxStringContentLength = 10000000
    $elm.ReaderQuotas.MaxBytesPerRead = 10000000


    $msg1 = $elm.CreateMessageEncoderFactory().Encoder.ReadMessage($stream, 10000000);

    $doc.Load($msg1.GetReaderAtBodyContents());

    $msg.BodyStream.Position = 0;



    return $doc;
}

Function Find-SoapEnvelopeStart([byte[]] $stream)
{
    $i = 0;
    $j = 0;
    $prevByte = $stream[$i];
    $curByte = [byte]$j;
    for ($i = 0; $i -lt $stream.Length; $i++)
    {
        $curByte = $stream[$i];
        if ($curByte -eq [byte] 0x02 -and $prevByte -eq [byte] 0x56) {
            break;
        }
        $prevByte = $curByte;
    }
    return $i - 1;
}


Function Run-MainDemoIterateMsmq([string] $queuePath) {

$queue = New-Object System.Messaging.MessageQueue $queuePath

foreach ($message in $queue.GetAllMessages()){
  $xmlDoc = Get-XmlFromWcfMessage $message 
  Write-Host $xmlDoc.OuterXml
 } 


}





Saturday 21 July 2018

Reading WCF messages in MSMQ queues in Powershell

The last article looked at displaying MSMQ contents for a MSMQ queue used with NetMsmqBinding using Powershell. The message queues contained characters that were unreadable. That is because the message queue items contains actually ready to consume WCF messages. This article will present a new Powershell script where the MSMQ body contents is finally readable using Powershell and .NET WCF classes in System.ServiceModel. Here is how the contents looks using regular methods to extract the MSMQ Message queue items with garbled contents. This is as noted due to the MSMQ queue items each containing a corresponding WCF message.
Let's fix this up by using System.ServiceModel classes!
[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.IO") | Out-Null


$queuePath = ".\private$\demoqueue4"

Write-Host "Powershell MSMQ queue WCF inspector v0.1. Inspecting queue contents of the queue: $queuePath"
Write-Host ""


Run-MainDemoIterateMsmq $queuePath



Function Get-XmlFromWcfMessage([System.Messaging.Message] $msg) {
   
    $doc = New-Object System.Xml.XmlDocument;
    $messageLength = [int] $msg.BodyStream.Length


    $buffer = New-Object byte[] $messageLength

    
    $msg.BodyStream.Read($buffer, 0, $messageLength)

    $envelopeStart = Find-SoapEnvelopeStart($buffer);

    $envelopeLength = $($buffer.Length - $envelopeStart)
    #Write-Host $envelopeStart


    $stream = New-Object System.IO.MemoryStream($buffer, $envelopeStart, $envelopeLength)

    $elm = New-Object System.ServiceModel.Channels.BinaryMessageEncodingBindingElement
    $elm.ReaderQuotas.MaxStringContentLength = 10000000
    $elm.ReaderQuotas.MaxBytesPerRead = 10000000


    $msg1 = $elm.CreateMessageEncoderFactory().Encoder.ReadMessage($stream, 10000000);

    $doc.Load($msg1.GetReaderAtBodyContents());

    $msg.BodyStream.Position = 0;



    return $doc;
}

Function Find-SoapEnvelopeStart([byte[]] $stream)
{
    $i = 0;
    $j = 0;
    $prevByte = $stream[$i];
    $curByte = [byte]$j;
    for ($i = 0; $i -lt $stream.Length; $i++)
    {
        $curByte = $stream[$i];
        if ($curByte -eq [byte] 0x02 -and $prevByte -eq [byte] 0x56) {
            break;
        }
        $prevByte = $curByte;
    }
    return $i - 1;
}


Function Run-MainDemoIterateMsmq([string] $queuePath) {

$queue = New-Object System.Messaging.MessageQueue $queuePath

foreach ($message in $queue.GetAllMessages()){
  $xmlDoc = Get-XmlFromWcfMessage $message 
  Write-Host $xmlDoc.OuterXml
 } 


}
The soap envelope is found looking after the escape sequence "V" followed with the special ANSI character 0x02 (STX = Start of Text). From this point on, the rest of the WCF Message is our SOAP body! We then get readable output!
Finally, the WCF messages inside the MSMQ queue that is filled up using NetMsmqBinding is readable! In my next article I will present a demo solution of how to use NetMsmqBinding in WCF! Until then, you can clone the solution already from here: git clone git@bitbucket.org:toreaurstad/demonetmsmqwcfgit.git

Reading MSMQ contents with Powershell

This article will present two ways to read a MSMQ (Microsoft Message Queue) using Powershell. First, the queue will be read using the GetString method of System.Text.UTF8Encoding:

[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
 
$queuePath = ".\private$\myqueue"

$queue = New-Object System.Messaging.MessageQueue $queuePath

foreach ($message in $queue.GetAllMessages()){

 Write-Host (New-Object System.Text.UTF8Encoding).GetString($message.BodyStream.ToArray())

 Write-Host $msg 
}

As an alternative, it is also possible to use the StreamReader (more ceremony really):

[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
 
$queuePath = ".\private$\myqueue"

$queue = New-Object System.Messaging.MessageQueue $queuePath

foreach ($message in $queue.GetAllMessages()){

 Write-Host ([Environment]::NewLine)

 $sr = New-Object System.IO.StreamReader($message.BodyStream)

 $message.Formatter = New-Object System.Messaging.XmlMessageFormatter(@(""));
 
 $msg = "";

 while ($sr.Peek() -ge 0){
  $msg += $sr.ReadLine()
 }

 Write-Host $msg 
}

Actually, I was needing a simple way to look at some messages sent with the NetMsmqBinding in WCF, so making a Powershell script seemed the quickest way! WCF does some strange formatting on the message that is sent through the wire, you can though see that our two alternatives gives also a bit different formatting, the first alternative being the most clean and with shortest syntax.
As the reader can see, the contents of the MSMQ queue that the NetMsmqBinding uses shows unreadable characters. That is because the MSMQ message item are containing actually WCF Messages. QueueExplorer showed me this fact, so next article will present a more lengthy version where the content can be properly decoded using Powershell to the rescue!