PowerShell Pipeline
Adding a Certificate to a Store Using PowerShell
Of course you could accomplish this outside of PowerShell, but why would you want to?
Usually the method for adding a certificate to a certificate store in Windows means that you perform one of a couple of actions, such as right-clicking on the certificate file and importing the certificate to a store or using the certificates MMC snap-in to import the certificate. Both ways get the job done, but we aren't the types of people to go about it that way, are we?
That's right, we are going with the PowerShell approach to make this happen! As with my previous article on exporting a certificate, I am going to show you two ways to import a certificate:
- Using the Import-Certificate cmdlet from the PKI module (or Import-PfxCertificate if using cert with private keys). Note: this is only available with PowerShell V4 and at least Windows 8.1/Windows Server 2012 R2
- Using .Net classes to import the certificate
Using Import-Certificate
The easiest way to accomplish this is by using the Import-Certificate cmdlet available in the PKI module. There are only a couple of parameters that mean anything to me:
- FilePath: Where the certificate file is located
- CertStoreLocation: The store where the certificate will be imported into. E.g. Cert:\LocalMachine\My
In this case, I am going to import one of my certificates to the LocalMachine\My store.
Import-Certificate -FilePath "C:\Users\proxb\Desktop\BackupCert.Cer" -CertStoreLocation 'Cert:\LocalMachine\My' -Verbose
That was simple enough! The same approach could be used if you wanted to install a new Root CA certificate or any certificate for that matter as long as you know where it needs to go. If you have PowerShell remoting enabled in your environment (and each system is running PowerShell V4 along with being on Windows 8.1/Windows Server 2012 R2), then you could simply use copy the file to each system and use Invoke-Command to import the certificates. Of course, if you do not meet the criteria for using this command, then you are in luck as I will show you how to do this using the .Net classes with PowerShell!
.Net Approach
As with exporting a certificate, there is a little more work involved with importing a certificate (using a cmdlet makes it seems so easy, doesn't it?) and I will cover that in each step.
Let's start out by making the connection to a remote certificate store like, its 'Machine\My' store.
$Computername = "boe-pc"
$CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "\\$($Computername)\My", "LocalMachine"
$CertStore.Open('ReadWrite')
I make sure to specify 'ReadWrite' with the open flag so I can actually write to the certificate store by adding the new certificate. I am also making sure to connect to the LocalMachine\My store, which will be where my certificate will be imported into.
$Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
I create an empty certificate object that can then be used along with the Import() method to load the data into the object and actually create a useful certificate. Before I use this method, let's take a look at the available overloads for this method.
$certificate.Import
OverloadDefinitions
-------------------
void Import(byte[] rawData)
void Import(byte[] rawData, string password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags)
void Import(byte[] rawData, securestring password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags)
void Import(string fileName)
void Import(string fileName, string password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags)
void Import(string fileName, securestring password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlag
There are quite a few choices here that I can use with Import(). For this instance, I am going to use the overload that accepts a filepath to a certificate file. With that, I am going to import the .cer backup file that I have available.
$certificate.Import('"C:\Users\proxb\Desktop\BackupCert.Cer"')
Now we can look at the object and see what is in it now.
Now that we have our certificate object ready to go, we can now add this certificate back into the store. With the Add() method on $CertStore, we only have one possible option to use.
$CertStore.Add
OverloadDefinitions
-------------------
void Add(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
With that, we can now add the certificate:
$CertStore.Add($certificate)
We can verify that the certificate is available in store by looking at the certifiates property on the certificate store object:
$CertStore.Certificates
With that, we have now added a certificate to a remote store. We could change this up if we wanted to just like when I showed the cmdlet approach to this by adding certificates to other stores such as the Trusted Root Certification Authorities store. In fact, I wrote a small function that would make pushing out a new certificate to multiple systems much easier.
Function Import-Certificate {
<#
.SYNOPSIS
Import a certificate from a local or remote system.
.DESCRIPTION
Import a certificate from a local or remote system.
.PARAMETER Computername
A single or list of computernames to perform search against
.PARAMETER StoreName
The name of the certificate store name that you want to search
.PARAMETER StoreLocation
The location of the certificate store.
.NOTES
Name: Import-Certificate
Author: Boe Prox
Version History:
1.0 - Initial Version
.EXAMPLE
$File = "C:\temp\SomeRootCA.cer"
$Computername = 'Server1','Server2','Client1','Client2'
Import-Certificate -Certificate $File -StoreName Root -StoreLocation LocalMachine -ComputerName $Computername
Description
-----------
Adds the SomeRootCA certificate to the Trusted Root Certificate Authority store on the remote systems.
#>
[cmdletbinding(
SupportsShouldProcess=$True
)]
Param (
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('PSComputername','__Server','IPAddress')]
[string[]]$Computername = $env:COMPUTERNAME,
[parameter(Mandatory=$True)]
[string]$Certificate,
[System.Security.Cryptography.X509Certificates.StoreName]$StoreName = 'My',
[System.Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation = 'LocalMachine'
)
Begin {
$CertificateObject = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$CertificateObject.Import($Certificate)
}
Process {
ForEach ($Computer in $Computername) {
Try {
Write-Verbose ("Connecting to {0}\{1}" -f "\\$($Computername)\$($StoreName)",$StoreLocation)
$CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "\\$($Computername)\$($StoreName)", $StoreLocation
$CertStore.Open('ReadWrite')
If ($PSCmdlet.ShouldProcess("$($StoreName)\$($StoreLocation)","Add $Certificate")) {
$CertStore.Add($CertificateObject)
}
} Catch {
Write-Warning "$($Computer): $_"
}
}
}
}
Using the example below, I can easily push out a certificate to a few remote systems.
$File = "C:\temp\SomeRootCA.cer"
$Computername = 'Server1','Server2','Client1','Client2'
Import-Certificate -Certificate $File -StoreName Root -StoreLocation LocalMachine -ComputerName $Computername
As I mentioned previously, this is a simple function that doesn't do anything more than adding a regular certificate to a store. It doesn't handle passwords or certificates with a private key. With that, we can wrap up this article on adding certificates to a certificate store.