Importing a certificate to a computer…you’d think the PowerShell method would be plastered all over the web, but oh no!  I had to figure most of this out all by my lonesome.  Okay, not *all* by my lonesome, but it felt that way.  There was one set of functions I found that worked as long as you use the x86 (aka 32-bit) version of PowerShell, but since I’m a 64-bit kind of guy I prefer my code to work in both the 32-bit and the 64-bit versions of PowerShell.

 

For your reference, the 32-bit only method is here:

 

http://blogs.msdn.com/daiken/archive/2007/01/12/windows-powershell-met-capicom.aspx

 

This method does not work on Windows Server 2008 64-bit because the CAPICOM object does not have a 64-bit COM+ application.  Using this code in PowerShell 64-bit gives you lots and lots of nasty red on black text.  Which meant it was time to go back to the drawing board.

 

In case you didn’t know, PowerShell has a drive for certificates.  Just type in “set-location cert:” (minus the “”) in PowerShell and you are now in your certificate store.  I blogged about this a bit in my MD5 certificate blog post a while back, so I won’t go into that much.  This was a good start, because it let me know what .NET class PowerShell uses for its certificates by looking at the “get-member” properties of a certificate.  Why is this important, you may be wondering?  Because PowerShell is just a scripting language built on top of the .NET framework.  Which means if you know what .NET class to manipulate you can do far more in PowerShell than just reading the cmdlet help files.   In this case I used the following command to get the .NET class.

  1. gci cert:\localmachine\root | gm

…which translates to this using the long way…


  1. get-childitem cert:\LocalMachine\root | get-member

 

I used cert:\LocalMachine\root because I know there are always certificated there, so I will get the right class I need use. And sure enough, at the top of the get-member output you get the .NET class of “System.Security.Cryptography.X509Certificates.X509Certificate2.”With the .NET class in hand I did a quick MSDN search and started my research.

 

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx

 

I’ll spare you readers the boring trial and error stuff and skip straight to the results. There is a way to import certificates, both X.509 (.cer) and .PFX and all other supported types. All you need to do it use one of these two handy functions.


  1. function Import-PfxCertificate {
  2. param([String]$certPath,[String]$certRootStore = “CurrentUser”,[String]$certStore = “My”,$pfxPass = $null)
  3.  $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
  4.  if ($pfxPass -eq $null) {$pfxPass = read-host “Enter the pfx password” -assecurestring}
  5.  $pfx.import($certPath,$pfxPass,“Exportable,PersistKeySet”)
  6.  $store = new-object System.Security.Cryptography.X509Certificates.X509Store($certStore,$certRootStore)
  7.  $store.open(“MaxAllowed”)
  8.  $store.add($pfx)
  9.  $store.close()
  10. }

 

Import-PfxCertificate should be used when importing a certificate that requires a password. I didn’t go all crazy on the password security, but you can add that outside the function in your code pretty easily. If I add some later on I’ll do some updates to the blog post. To use the function you pass one to four parameters.

 

$certPath = the location of the certificate file. Required.

$certRootStore = either “LocalMachine” or “CurrentUser”. Default is CurrentUser, which is your personal user store. LocalMachine is well … the computer’s certificate store. IIS site certificates go here.

$certStore = any number of items. If you go to the cert: drive, then change the directory to either LocalMachine or CurrentUser and run “dir” you will see a list of all the cert stores. My = Personal, root = Trusted Root Certificate Authorities, etc. These “directory names”, or certificate stores, is what you enter for this variable. Again, didn’t go crazy with the error correction in this function, but as long as you pass the right store all is well.

$pfxPass = the password of the pfx, or other passwords protected certificate files. If none is specified you get a prompt, which stores the pass as a securestring. You *can* pass a plain text password, but I wouldn’t recommend it unless you know the script is secure. You can also pass a secure string to the function, using whatever securestring storage method you prefer…or can find.

 

Example:

 

Import-PfxCertificate “C:\temp\testCert.pfx” “LocalMachine” “My”

 

The second function is for certificates that do not require a password.


  1. function Import-509Certificate {
  2.  param([String]$certPath,[String]$certRootStore,[String]$certStore)
  3. $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
  4. $pfx.import($certPath)
  5. $store = new-object System.Security.Cryptography.X509Certificates.X509Store($certStore,$certRootStore)
  6. $store.open(“MaxAllowed”)
  7. $store.add($pfx)
  8. $store.close()
  9. }

 

The variables are the same as before, the function merely lacks password support. Usage is identical to Import-PfxCertificate, just don’t pass it a password string or securestring. I suppose you could, the function will just ignore it.

 

That’s all there is to it. If you’re still with me you may be wondering why I think this is so important. Even working for a hosting company most certificates I handle don’t need this, even though you can use this to generate and complete certificate requests. The reason I looked for this is because of code signing. I can script the importation of the three required certificates for PowerShell code signing to a sever in seconds now. Whether it was worth the effort is in the eye of the beholder, but I think it was.

Try Performance Cloud Servers

Written by James Kehr Employee @ SherWeb