12 January 2015

I’ve only recently discovered the “multiple assignment” feature in PowerShell via the Windows PowerShell blog. I’m not sure what I was searching for when I stumbled upon it but my first thought when I saw it was “I bet this will make modifying arrays easy!” As some of you may be aware, modifying arrays of object type “string” are a pain in the butt in PowerShell and unless you create the variable as a Collections “ArrayList” object you’re going to be struggling. But, the beforementioned feature makes it so much easier!

For example, I needed to remove 1 line from an INI file on a PC and the potential for doing this to many PC’s prompted me to write a script. I could do the following:

$originalFile = get-content path-to-INI-file

This gets the entire contents of the file. Get-Content is a widely used cmdlet and it stores the content into a string-type array. That’s not such a pain anymore to work with when I want to remove an element from it. First I would search for whatever I want to remove and find it’s index:

$removeIndex = $originalFile.IndexOf($originalFile -match “LogLevel=Finest”)
$newFile = $originalFile[0 .. ($removeIndex – 1)],$originalFile[($removeIndex + 1) .. $originalFile.count]
Set-Content path-to-INI-file $newFile

I could have easily overwritten $originalFile instead of creating $newFile. The gotcha above is that your search term has to be unique. If the match returns multiple results the “indexOf” method will break. You can add the index to the end of the match by adding [] if you know which match it will be ( $orginalFile.IndexOf(($originalFile -match “LogLevel=Finest”)[0]) will return the first match).

This made my life so much easier when I had to write this script to modify multiple configuration files to flip systems to a test mode.


26 October 2014

While planning to develop some scripts that would be meant for others to use I had a need to implement a timeout when asking for input (and to go with a default value).  I didn’t think it had a timeout, but I looked at the documentation on read-host anyway and sure enough, no timeout.  I then took to Google to see if there was anything (I wasn’t feeling in a creative mood and just wanted to do it) that someone else had written to do the job.  I was quite surprised that I couldn’t find anything already written.  I found a few scripts that just waited for a keystroke to be entered before continuing on with the script.  Nothing, though, that could use a promtp like read-host and sit at that prompt until someone started entering something.  So I had to create something myself; it proved surprisingly simple.

I decided that using the detection if a key was available from those other scripts was a great way to detect the moment the end user started typing something.  I knew I’d need a loop, then a break from that loop, and most difficult to capture that first key of input and pass it along to the next section of code, display it for the end user, and also capture the rest of what they typed.  I wanted to do it all seamlessly so it looked exactly like using the built in read-host cmdlet.  I quickly sketched up some psuedo code on my whiteboard and then set out to writing the code.

Like I said, it wasn’t too difficult.  The hardest part was figuring out how to make backspacing appear on the screen as the user typed.  I inadvertently stumbled upon a nice feature when choosing to use a character array instead of creating the string itself on the fly.  Reading each key and adding it to the char array also records the backspaces.  When I use the -join operator to turn the char array into a string, it processes the backspaces and the resultant string does not contain the characters that were backspaced.  Had I been building the string on the fly, I would have had to write extra code to account for that.  I was quite happy when I realized I wouldn’t have to.

The one feature not implemented is the ability to use -assecurestring.  Encryption still makes my brain freeze and I didn’t even try to implement it.  I’m sure I could hack my way through it but I usually prefer to understand something a little better before I implement it.  To contradict myself, though, I did write encryption/decryption scripts for protecting your scripts and even importing the function inside it so it can be used straight away in the shell.  Hopefully I’ll get around to posting those soon.

I think the one thing not to change here is the millisecond sleep when waiting for input.  Unless you go lower… the lower it is the more seamless the typing experience for the end user.  Even at 250 there is a slight delay in writing that first character to the screen.. especially for us fast touch-typists.  It’s pretty seamless though and I’m happy with it.

Anyway, enough gabbing, here’s the code.  It’s fully commented so it should explain everything!

Function read-HostTimeout {
###################################################################
##  Description:  Mimics the built-in "read-host" cmdlet but adds an expiration timer for
##  receiving the input.  Does not support -assecurestring
##
##  This script is provided as is and may be freely used and distributed so long as proper
##  credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Date Modified:  10-24-14
###################################################################

# Set parameters.  Keeping the prompt mandatory
# just like the original
param(
	[Parameter(Mandatory=$true,Position=1)]
	[string]$prompt,
	
	[Parameter(Mandatory=$false,Position=2)]
	[int]$delayInSeconds
)
	
	# Do the math to convert the delay given into milliseconds
	# and divide by the sleep value so that the correct delay
	# timer value can be set
	$sleep = 250
	$delay = ($delayInSeconds*1000)/$sleep
	$count = 0
	$charArray = New-Object System.Collections.ArrayList
	Write-host -nonewline "$($prompt):  "
	
	# While loop waits for the first key to be pressed for input and
	# then exits.  If the timer expires it returns null
	While ( (!$host.ui.rawui.KeyAvailable) -and ($count -lt $delay) ){
		start-sleep -m $sleep
		$count++
		If ($count -eq $delay) { "`n"; return $null}
	}
	
	# Retrieve the key pressed, add it to the char array that is storing
	# all keys pressed and then write it to the same line as the prompt
	$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp").Character
	$charArray.Add($key) | out-null
	Write-host -nonewline $key
	
	# This block is where the script keeps reading for a key.  Every time
	# a key is pressed, it checks if it's a carriage return.  If so, it exits the
	# loop and returns the string.  If not it stores the key pressed and
	# then checks if it's a backspace and does the necessary cursor 
	# moving and blanking out of the backspaced character, then resumes 
	# writing. 
	$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
	While ($key.virtualKeyCode -ne 13) {
		If ($key.virtualKeycode -eq 8) {
			$charArray.Add($key.Character) | out-null
			Write-host -nonewline $key.Character
			$cursor = $host.ui.rawui.get_cursorPosition()
			write-host -nonewline " "
			$host.ui.rawui.set_cursorPosition($cursor)
			$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
		}
		Else {
			$charArray.Add($key.Character) | out-null
			Write-host -nonewline $key.Character
			$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
		}
	}
	""
	$finalString = -join $charArray
	return $finalString
}

27 September 2014

Finally fixed the overlapping text in my comments sections, lol.  I sat down and prepared myself to dig in… but it turned out to be quite simple.  Utilizing the ever-helpfull Firebug plugin I pinned down the offending div and took out it’s fixed height.  I also needed to set a min height on another div since, I think, the floating avatar image was causing the CSS to not obey the margin set by the div it was nested inside.  Weird… but then, CSS has always been a point of confusion for me.  It’s definitely not intuitive!

What I should have done was written the comments code completely from scratch like I did the rest of the site…  Oh well, maybe one day!


25 September 2014

It’s funny how I can write code and then a long time afterwards need to write something for the same goal and come up with it in an easier way.  It makes sense, since I’ve likely learned more and grown in the time that has passed.  Still, to look back on the old cold and see how I fumbled through it or took a very long time to accomplish it and then do it in just a few lines makes me chuckle.

Take, for instance, NetStat.  Now, I know there are plenty of functions and cmdlets out there that other folks have written to get you “objectized” results for NetStat.  I’m sure they work well but I wouldn’t be who I am if I didn’t write my own code to do something.  Lately I’ve been guilty of using the cmdlets from Citrix and VMWare to interact with their items but for the sake of practicality, I have to.  Anyway… I’m troubleshooting slowness reports with one of our Citrix PVS environments but one of the problems is I don’t get told until hours afterwards and/or no one is willing to troubleshoot on the spot.  I can’t do anything if I can’t get data.  So, I needed to write a script that did not require a lot of resources and could run on the servers at the same time the users were using them at the heaviest time of the day – without impacting them.  At the time of this writing it hasn’t run for the first time yet out in the live environment, so after it does (and it’s successful *fingers crossed*) I may just create a post for the whole script.

Part of the script is collecting latency data.  I’m using a standard ping with the default packet size, doing an average of three pings and reporting back.  The piece that I wrote anew, despite having code already, was getting all of the connection data so that I could ping each connection and find latency.  Here’s what I do first:

$netstat = (netstat -ano) | where { ($_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") -and ($_ -match "TCP")  }

I’m pulling net stat and then only selecting the lines that 1) match a 4 octet IPv4 address and 2) are using TCP.  You can always further qualify it by adding a “-notmatch” and putting in any IP address you don’t want like local loopback stuff or all the 0.0.0.0.  I get something like this (I’m using just a sample of the data off a vm):

 TCP    127.0.0.1:49160        127.0.0.1:49161        ESTABLISHED     2232
 TCP    127.0.0.1:49161        127.0.0.1:49160        ESTABLISHED     2232
 TCP    127.0.0.1:49162        127.0.0.1:49163        ESTABLISHED     2232
 TCP    127.0.0.1:49163        127.0.0.1:49162        ESTABLISHED     2232
 TCP    127.0.0.1:49164        127.0.0.1:49165        ESTABLISHED     2232
 TCP    127.0.0.1:49165        127.0.0.1:49164        ESTABLISHED     2232
 TCP    127.0.0.1:49172        127.0.0.1:49173        ESTABLISHED     2232
 TCP    127.0.0.1:49173        127.0.0.1:49172        ESTABLISHED     2232
 TCP    127.0.0.1:49174        127.0.0.1:49175        ESTABLISHED     2232
 TCP    127.0.0.1:49175        127.0.0.1:49174        ESTABLISHED     2232
 TCP    127.0.0.1:49176        127.0.0.1:49177        ESTABLISHED     2232
 TCP    127.0.0.1:49177        127.0.0.1:49176        ESTABLISHED     2232
 TCP    127.0.0.1:49178        127.0.0.1:49179        ESTABLISHED     2232
 TCP    127.0.0.1:49179        127.0.0.1:49178        ESTABLISHED     2232
 TCP    127.0.0.1:49185        127.0.0.1:65001        ESTABLISHED     2060
 TCP    127.0.0.1:55460        127.0.0.1:27015        ESTABLISHED     11792
 TCP    127.0.0.1:55462        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55466        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55467        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55469        127.0.0.1:55470        ESTABLISHED     13388

Then, to pull out the “Foreign” address (the remote machine’s IP) I do some splitting and replacing of each line to widdle it down to just the IP address:

ForEach ($rec in $netstat) {
	$foreignAddress = (($rec.split(" ") | where { $_ -match "\d{1,3}\." } )[1].Split(":"))[0]

As I go through each line (which is just a string of text) I first split it by spaces, which is the part of the code up to the first | (pipe) which ends up looking like this:

TCP



127.0.0.1:1120










127.0.0.1:59964













LISTENING






16932

It looks like a mess but it’s made it a lot simpler.  Now each non-blank string of text is on it’s own line, and thus, it has an index.  But, I can further widdle this down by only matching those items that begin with an octet.  That’s where I pipe part one above into the “where” clause and get:

127.0.0.1:1120
127.0.0.1:59964

Awesome.  So I know that the foreign address is always the second one (index “1”) but there’s also that port on there which will break the ping attempt so we need to take that off.  That’s where the rest of the code at the end of the “where” clause comes in.  I tell it I want index 1 ([1]), which is the line “127.0.0.1:59964” and then I split by the “:” which produces two lines, “127.0.0.1” and “59964”  I only want the IP and that is index “0” and so my line ends with a [0].  Note that each step I’ve explained above is in it’s own set of parenthesis.

I know I didn’t do the best job of explaining the breakdown but hopefully it has helped make sense of nesting commands with PowerShell and been an example of how powerful piping and one line of code can be.  My overall code here is 3 lines but it took a mess of a string and brought it down to the exact piece of data that I wanted.  I think that’s pretty darn cool.  For anyone wishing to take this a step further and make their own “objectized” version of NetStat or another text-output tool you should be able to go from here using this.  Maybe a quick google search on how to eliminate blank lines from a multi-line string, too.  I know I have that code somewhere….  If I were smart I’d do the same, that way I can reuse the function no matter which individual piece of data in the results I was looking for and not had to modify anything…


3 August 2014

I hate it when I find a new — and very easy — solution to a problem.  I’ve only recently discovered the parameter -ExecutionPolicy to the powershell.exe application and the value “Bypass.”  It allows you to, you guessed it, bypass whatever execution policy is set even if it’s restricted.  Non-admins can use it too… which at first seems really stupid but the bypass does not elevate anything so if they don’t have rights, they don’t have rights.

Anyway, that’s probably been around forever and I feel really stupid for just learning about it.  I discovered it when I had a script run at startup on a machine that didn’t have an execution policy set to something other than restricted and I saw the command line for the process using that parameter.

Even though I feel really stupid I’m glad I found it 🙂


20 April 2014

Some time ago I posted a script to remotely enable WinRM on a machine.  I have to admit it was fairly kludgy in that it had a lot of moving parts that had to execute just right.  Registry edits to turn on features, create listener config, modify firewall rules, and then cmdlets to remotely stop and start services.  I even wrote one that tried to rely on setting scheduled tasks with batch scripts (which would also call Powershell scripts) to get the job done…   There are a lot of points of failure in both attempts.

The scheduled tasks didn’t always execute correctly (or even get set to be executed) and I just had a lot of trouble with it.  That’s why I set out to write the second one.  The second one, while still a very inelegant way to do it, was solid except for one point of failure that, well, failed.  It failed almost all of the time because it required too perfect a condition.

Stopping and restarting the Windows Firewall Service remotely is just a bad idea.  If the timing between firing off the stop and start is perfect (i.e. lightning fast) then chances are the start command wont ever make it to the remote.  The reason is because once the Firewall Service is stopped the OS goes into a bit of a lockdown and doesn’t allow incoming communication via things like WMI.  It’s a great safety mechanism against remote attacks but a real pain in the butt when you don’t have time to (or just can’t) go to a machine.  The only other way to do it is locally… but I just said sometimes you can’t get to the local machine.  WinRM is our way to execute things remotely as if we were local… and if it’s not enabled then what is the solution?  There is another way to execute remote commands as if they were local!

Enter the Invoke-WMIMethod cmdlet.  This cmdlet allowed me to greatly improve my success rate and make it very elegant!  It simplifies the script  a great deal and only needs one remote mechanism to function.  My original function was 51 lines long, the second one 35, and now this one is only 18!  It can be reduced more but I have detection and logging in there too.  I’ve adapted it to be a standalone script for sharing here .  Without my monstrous heading (which I hope you’ll keep in tact if you use or share this…) it’s still only 30 lines (which includes several comments).  The one caveat to this script is that in order for it to work, Remote WMI rules in the firewall need to be enabled to allow it through!  I dont believe these are turned on by default (in my company we turn them on, on all PC’s so we can mange them…).

OK, I’ve rambled enough, here’s the script:  EnableWinRM

 

Thanks to this script for teaching me that the Invoke-WMIMethod was capable of starting a new process:  http://gallery.technet.microsoft.com/scriptcenter/Get-NetworkStatistics-66057d71

 


20 April 2014

I’ve learned that you need a Host-only network adapter for both VM’s in order for the workstation to connect to the server.  Thanks http://www.duncansutcliffe.com/?p=16


16 April 2014

I can’t believe it’s already been over 2 years since I started this blog and I still don’t post nearly as often as I’d like.  I wish I could have something useful to post every week but, alas, I do not.  I’m not even sure how much my blog gets seen (by real people) let alone is useful.  Not many comments or feedback and I believe most of the visitors are just robots 🙁  Not being popular isn’t a biggie for me.  The point of this blog was to help folks and I hope that I’m accomplishing that.  To those that have posted comments, thank you!  I’m always happy to know my efforts are not wasted or in vain.  I also realized that when I switched from my custom theme to one I downloaded from WordPress it didn’t have my stats code so I dont have stats for the past few months :\  I flipped back to my old, custom theme, and will just have to deal with the poorly designed comments section… sorry folks.

Even though I’ve been busy and when I come home I don’t feel like working more, I’ve managed to get a few “quick drafts” saved of the items I want to post about.  Some of those are:

  • Running encrypted Powershell scripts on the fly.  I do mean encrypted and NOT obfuscated!  I need to test it a little bit more though.
  • Caveats I’ve learned in securing WinRM on machines
  • Multithreading and displaying a progress bar with Powershell
    • I removed the one I had up already because it had a major logic flaw that I’m still embarrassed about even though I caught it months ago and I just have a better way of doing it.
      • If you happen to be using it, STOP!  You’re dropping a lot of data!  Sorry about that…
    • I want to work on my data collision issue when multiple threads are attempting to write to the same file
  • A more detailed “get-process” function (for remote machines) in Powershell
  • I also have a MUCH better version of my Remotely Enabling PSRemoting (WinRM) that has a much greater success rate.
    • Both mine and the commenter’s script on that post have an issue with when the firewall is attempted to be restarted (fails most of the time)… I’ve found a better way to do it which fixes the problem.

Several months ago I bought an Arduino kit but have just gone through the simple projects in the book that came with it.  Electronics have always fascinated me and I really want to get into some real projects.  I have a co-worker that is really into this stuff and even designs and prints his own circuit boards.  He’s given me a ton information, I just need to make the time to sit and do it.  I know I’ll love that discovery process and writing about it.

Just can’t seem to find my motivation for much these days… can’t even remember the last time I was at the gym :\


16 April 2014

Had a need to write a re-usable function at work to compare version numbers so that an action could be taken accordingly.  It’s pretty simple but thought I’d share it anyway.

Function verCompare($ver1, $ver2) {
   # returns whether ver1 is Less, Greater, or Equal to ver2
   $ver1Array = $ver1.Split(".")
   $ver2Array = $ver2.Split(".")

   # Decide which array has the least amount of subversions and we'll only compare the least amount
   If ($ver1Array.count -ge $ver2Array.count) { $count = $ver1Array.count }
   Else { $count = $ver2Array.count }

   # Loop through each sub version and compare them.
   # Once I hit a Greater or Less than I change the count to break
   # Out of the for loop because there is no need to compare further
   For ($i=0; $i -lt $count; $i++) {
      Switch ( $ver1Array[$i].CompareTo($ver2Array[$i]) ) {
         -1 { $compare = "Less"; $i = $count + 1 }
         0 { $compare = "Equal" }
         1 { $compare = "Greater"; $i = $count + 1 }
      }
   }

   return $compare

}

 

What I’m doing here is accepting two versions.  This function will tell you, in the end, if ver1 is greater than, equal to, or less than ver2.  First I split each one using the “.” and store the array.  I do this because, obviously, a number with multiple “.’s” isn’t really a number so you have to break them up and compare them separately.

Next I decide which one is the longer version number, for example 8.0.7601.3572 vs 8.1.7601.  The second one has the least amount of subversions so I would only compare the first 3 version numbers instead of 4.  Why?  Well, to be honest, if you are using this function you’re 99% of the time going to be comparing versions of the same software and the number of subversions will probably be the same.  I know this wont be true 100% of the time but lets go with it.  In the event that the first 3 subversions are equal but one has an addition .### will you really know whether it’s greater, equal, or less than the other version had it been showing a subversion?  Who knows.  It would be simple enough to take this into consideration but I didn’t need to.

Next is just a for loop using a counter that counts up to the lowest number of subversions and uses that counter as the index to the array that holds the subversions for each version.  It compares them using the built-in .CompareTo() method for [ints] and you can see in the switch what value that method returns depending on the result.  I break out of the loop unless they are equal because there is no need to compare further once you already hit a value that is greater or equal.  Using the numbers above this function would compare the first “8’s” and see they are equal, the move onto the next one which is 0 for ver1 and 1 for ver2.  The loop would see ver2 is greater and then not bother to continue comparing the 7601 to 7601 because there is no need.

Once you return that value you can easily use it to act upon, such as we did below:

Function checkRegValue() {
   # Establish a minimum and maximum value that we
   # want the range to fall in, to act upon
   $minValue = "8.0.7601.15526"
   $maxValue = "9.11"

   # Get the current version installed
   $version = (get-itemproperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\").Version

   # Use our compare function to see how the current
   # version compares to the min and max
   $minCompare = verCompare $version $minValue
   $maxCompare = verCompare $version $maxValue

   # To see if it is or falls between the min or max
   # values we want to know if it's NOT less than the
   # min value and NOT greater than the max value
   If ( ($minCompare -ne "Less") -and ($maxCompare -ne "Greater") ) {
      return 1
   }
   Else {
      return 0
   }
}

 

And there you have it…


3 February 2014

As part of my slow-moving efforts within my company to get WinRM enabled across the enterprise (14,000 computers…) I’ve had to do a bit of testing to make sure I can properly secure it.  Much of that testing is around setting the execution level and making sure nothing outside of what I or my team has signed is able to be executed.  To do that, I needed to be able to make my own, self-signed certs to test what it would be like with scripts coming from outside.  Naturally, I chose OpenSSL  I don’t have access to create Microsoft certs with our company tools and I don’t want to bug (or wait on) someone else to make them for me when I need them.  I’m not patient like that.  So, in typical geek fashion I set out to figure out how to do it on my own.

First I’ll start of by saying that 99% of what you find on the internet on how to do it will not work on Windows.  Those instructions will work beautifully on Linux but if the instructions only tell you to make modifications to the config file, then those instructions are not for you.  I went through several different sites giving values to set inside the config but it never worked.  Every cert I ended up with was not valid for code signing!  I ended up spending time going through the official OpenSSL documention and figuring out the method I ended up with.  It’s not hard, it just uses a method most don’t know about or haven’t had a need to use.  You’ll have to make changes to your main config file, create a supplemental one, and I throw in a batch file to automate it.  I am using OpenSSL 1.0.1e that was released on 11 Feb 2013 on a Windows 7 machine.

Make sure the v3_req section in your openssl.cfg matches the one below:

[ v3_req  ] 

# Extensions to add to a certificate request
subjectKeyIdentifier=hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = codeSigning, msCodeInd, msCodeCom
nsCertType = client, email, objsign

You can take away the email under nsCertType but there isn’t a reason to.  You can also add any additional types you may want this cert to do, just don’t take anything away that has Code, Signature or obj references in them.

You then want to create a NEW .cfg file and put the exact same properties as above (without the [ v3_req ] header).  You can call it anything you want, just save it somewhere logical.  I keep mine in the OpenSSL directory with the openssl.cfg.

Below is the batch script I wrote to automate the process of creating a new self-signed CA and a code signing cert.

REM Switch to the directory where openssl.exe is
CD C:\OpenSSL-Win32\bin
REM Create the key for the Certificate Authority.  2048 is the bit encryptiong, you can set it whatever you want
openssl genrsa -out C:\YOURPATH\ca.key 2048
REM Next create the certificate and self-sign it (what the -new and -x509 do).  Note, I'm explicitly telling it the main config path.  You have to.
openssl req -config C:\OpenSSL-Win32\bin\openssl.cfg -new -x509 -days 1826 -key C:\YOURPATH\ca.key -out C:\YOURPATH\ca.crt
REM Now I'm creating the private key that will be for the actual code signing cert
openssl genrsa -out C:\YOURPATH\codesign.key 2048
REM Creating the request for a certificate here.  Note the -reqexts you need to tell it to pull that section from the main config or it wont do it.
openssl req -config C:\OpenSSL-Win32\bin\openssl.cfg -new -key C:\YOURPATH\codesign.key -reqexts v3_req -out C:\YOURPATH\codesign.csr
REM Signing the code signing cert with the certificate authority I created.  Note the -extfile this is where you point to the new .cfg you made.
openssl x509 -req -days 1826 -in C:\YOURPATH\codesign.csr -CA C:\YOURPATH\ca.crt -CAkey C:\YOURPATH\ca.key -extfile C:\OpenSSL-Win32\bin\v3.cfg -set_serial 01 -out C:\YOURPATH\codesign.crt
REM Now I"m expoorting my key and crt into a PKCS12 (.pfx file) so that I can import it onto the machine that I'm going to use it to sign on.
openssl pkcs12 -export -out C:\YOURPATH\codesign.pfx -inkey C:\YOURPATH\codesign.key -in C:\YOURPATH\codesign.crt

I know some of it looks redundant, and it is, but I found it all to be necessary to get it to work.  I suspect it’s a bug with the Windows version of SSL since it should read it all from the openssl.cfg file.  It does on Linux.  I may have missed a specific scenario of testing config information in places, and maybe you can leave it out in one place, but this isn’t a long or complicated process so I see no reason to go back and fully verify.

I don’t go into detail on all of the parameters that are used.  I don’t understand enough to do that (although I’m pretty sure I have it figured out) and I am by no means an expert on encryption or SSL.  I hope this all works for you.  If anyone has any website they recommend that gives a great, clear, and thorough explanation of how encryption (or specifically SSL) works I would really appreciate you linking it in the comments!

A few more notes:

  • Don’t forget to import this to use it!
  • If you want full trust, import the CA you created too.
  • This works for signing powershell scripts, it’s the reason why I figured it out
  • Secure your cert with a password.  It’s listed as an optional parameter when generating the crt but do it.  When you import it into your certificate store, mark it as non-exportable and keep your key, crt, and pfx files in a secure location!