Managing Windows PFX certificates through PowerShell
Tags: powershell, windows, system, cli
Published: 2020-01-17 15:16:38

The certificate is a container for the public key. It includes the public key, the server name, some extra information about the server, and a signature computed by a certification authority (CA). 

## Table of content

- [Prerequisites](#prerequisites)
- [What is PFX Certificate](#what-is-pfx-certificate)
- [Logical stores](#logical-stores)
- [PKI Module](#pki-module)
  - [Export-PfxCertificate](#exportpfxcertificate)
  - [Get-Certificate](#getcertificate)
  - [Get-PfxData](#getpfxdata)
  - [Import-PfxCertificate](#importpfxcertificate)
  - [New-SelfSignedCertificate](#newselfsignedcertificate)
- [Finding and Selecting a certificate](#finding-and-selecting-a-certificate)
- [Creating a certificate](#creating-a-certificate)
- [Installing a pfx certificate](#installing-a-pfx-certificate)
- [Add a certificate as trusted](#add-a-certificate-as-trusted)
- [Exporting and Importing certificate](#exporting-and-importing-certificate)
  - [Exporting pfx certificate](#exporting-pfx-certificate)
  - [Importing pfx certificate](#importing-pfx-certificate)
- [Export a Certificate from pfx](#export-a-certificate-from-pfx)
- [Deleting a certificate](#deleting-a-certificate)
  - [Deleting with thumbprint](#deleting-with-thumbprint)
  - [Find and Delete a certificate using property match](#find-and-delete-a-certificate-using-property-match)
- [Windows Certificate Manager](#windows-certificate-manager)
- [References](#references)

## Prerequisites

- Windows Vista, Windows Server 2008, or newer operating system. The examples shown use Windows 10 Pro version 1909
- PowerShell 5.X and above
- Familiarity with PowerShell

## What is a PFX Certificate

A `.pfx` file which should not be confused with `.cert` is a [PKCS#12](https://en.wikipedia.org/wiki/PKCS_12) archive; this is a bag that can contain a lot of objects with optional password protection. It usually contains a certificate (possibly with its assorted set of CA certificates) and the corresponding private key. While PFX can contain more than one certificates a `.cert` file contains a single certificate alone with no password and no private key. Within Windows, all certificates exist in logical storage locations referred to as certificate stores. 

The most common usage of a PFX certificate is code signing and creating a Software Publisher Certificate.

## Logical stores 

Logical stores are virtual locations that map to certificate physical paths. Powershell uses the `Cert` [PSDrive](https://docs.microsoft.com/en-us/powershell/scripting/samples/managing-windows-powershell-drives) to map certificates to the physical stores. 

The Certificates Microsoft Management Console (MMC) logical store labeling is different from the `Cert` PSDrive store labeling. The table below shows the comparison between both:

CERT: | CERTIFICATES MMC
-----------|---------------------------
| My | Personal |
| Remote Desktop | Remote Desktop |
| Root | Trusted Root Certification Authorities |
| CA | Intermediate Certification Authorities |
| AuthRoot | Third-Party Root Certification Authorities |
| TrustedPublisher | Trusted Publishers |
| Trust | Enterprise Trust |
| UserDS | Active Directory User Object |

## PKI Module

**MakeCert.exe** utility, which is part of the Microsoft .NET Framework SDK and Microsoft Windows SDK is used to create a self-signed certificate. On a system with no Windows SDK installed, the cmdlets in Powershell PKI module are used to manage the certificates. 

To list all the commands available in the PKI module, run the following command:

```powershell
Get-Command -Module PKI
```

The following commands are available from the PKI module.

```bash
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Add-CertificateEnrollmentPolicyServer              1.0.0.0    PKI
Cmdlet          Export-Certificate                                 1.0.0.0    PKI
Cmdlet          Export-PfxCertificate                              1.0.0.0    PKI
Cmdlet          Get-Certificate                                    1.0.0.0    PKI
Cmdlet          Get-CertificateAutoEnrollmentPolicy                1.0.0.0    PKI
Cmdlet          Get-CertificateEnrollmentPolicyServer              1.0.0.0    PKI
Cmdlet          Get-CertificateNotificationTask                    1.0.0.0    PKI
Cmdlet          Get-PfxData                                        1.0.0.0    PKI
Cmdlet          Import-Certificate                                 1.0.0.0    PKI
Cmdlet          Import-PfxCertificate                              1.0.0.0    PKI
Cmdlet          New-CertificateNotificationTask                    1.0.0.0    PKI
Cmdlet          New-SelfSignedCertificate                          1.0.0.0    PKI
Cmdlet          Remove-CertificateEnrollmentPolicyServer           1.0.0.0    PKI
Cmdlet          Remove-CertificateNotificationTask                 1.0.0.0    PKI
Cmdlet          Set-CertificateAutoEnrollmentPolicy                1.0.0.0    PKI
Cmdlet          Switch-Certificate                                 1.0.0.0    PKI
Cmdlet          Test-Certificate                                   1.0.0.0    PKI
```

The cmdlets used in this article are explained below:

#### Export-PfxCertificate

The Export-PfxCertificate cmdlet exports a certificate or a PFXData object to a Personal Information Exchange (PFX) file. By default, extended properties and the entire chain are exported.

#### Get-Certificate

The Get-Certificate cmdlet can be used to submit a certificate request and install the resulting certificate, install a certificate from a pending certificate request, and enroll for the directory service protocol [LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol). 

#### Get-PfxData

The Get-PfxData cmdlet extracts the contents of a Personal Information Exchange (PFX) file into a structure that contains the end entity certificate, any intermediate and root certificates.

#### Import-PfxCertificate

The Import-PfxCertificate cmdlet imports certificates and private keys from a PFX file to the destination store.

#### New-SelfSignedCertificate

The New-SelfSignedCertificate cmdlet creates a self-signed certificate for testing purposes. Using the CloneCert parameter, a test certificate can be created based on an existing certificate with all settings copied from the original certificate except for the public key.

## Finding and Selecting a certificate

In Powershell, the **Cert:** PSDrive is used to list the certificates in a particular store. To list the two locations under the Cert: PSDrive, run the following command:

```powershell
Get-ChildItem -Path Cert:
```

The certificate locations for the CurrentUser and LocalMachine will be shown. To list the certificates under the LocalMachine Trusted Root Certification Authorities, run the following command:

```powershell
Get-ChildItem -Path Cert:\LocalMachine\Root\
```

The returned object will be the certificates that can be modified, delete or exported. 

The `Get-ChildItem` cmdlet can be combined with `Where-Object` to find specific certificates. The command below will find certificate supposedly issued by Microsoft by checking the Subject property of the certificates:

```powershell
Get-ChildItem -Path Cert:\LocalMachine\Root\ | Where-Object {$_.Subject.Contains("Microsoft")}
```

The certificates listed will have part of it Subject value contains *Microsoft*.

## Creating a certificate

To create a certificate the values of **–DnsName** (name of a DNS server) or the **-Subject** and **-CertStoreLocation** (the certificate store in which the generated certificate will be placed) will have to be specified. 

To create a certificate for the DNS *test.com* and install it in the list of Personal certificate on a system, run the following command:

```powershell
New-SelfSignedCertificate -DnsName test.com -CertStoreLocation cert:\LocalMachine\My
```

The command creates a new certificate and installs it into the personal store location of the computer. On opening `certlm.msc` the certificate will appear in the **Personal** section. The output will show the details of the newly created certificate including its thumbprint:

```bash
PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject
----------                                -------
644F29EA276A639A28F82137F8132D6F49C1664A  CN=test.com
```

A self-signed certificate is generated with the following settings by default:

- Cryptographic algorithm: RSA;
- Key length: 2048 bit;
- Acceptable key usage: Client Authentication and Server Authentication;
- The certificate can be used for Digital Signature, Key Encipherment;
- Validity period: 1 year.

The validity of the generated certificate is limited to one year. To extend the certificate, a proper `Date` object with extended validity period should be specified with the `-notafter` switch. To issue a certificate for 5 years, run the command below:

```powershell
$expiry_year = (Get-Date).AddYears(5)
New-SelfSignedCertificate -DnsName test.com -notafter $expiry_year -CertStoreLocation Cert:\LocalMachine\My
```

The command below generates a certificate with value **Test** using the Subject property only and sets its expiry year to 3 years after the creation date:

```powershell
$expiry_year = (Get-Date).AddYears(3)
New-SelfSignedCertificate -Subject Test -notafter $expiry_year -CertStoreLocation Cert:\LocalMachine\My
```

New certificates can only be created in the personal store locations, `Cert:\LocalMachine\My` or `Cert:\CurrentUser\My`.

Execute the help command below to view all other parameters accepted by the `New-SelfSignedCertificate` cmdlet:

```powershell
help New-SelfSignedCertificate -Full
```

To export the certificate to pfx, read [this section](#exporting-certificate)

## Installing a pfx certificate

The certificate can be installed in a different store location, each store location has its specific purpose but in this article, we will install into four locations `Cert:\CurrentUser\My`, `Cert:\LocalMachine\My`, `Cert:\CurrentUser\Root` and `Cert:\LocalMachine\Root`. **My** location is the personal store for untrusted application and **Root** is the store for trusted certificates.

The command used to install a common certificate is different from the command to install a PFX certificate. The Powershell Cmdlet `Import-PfxCertificate` is used to install a pfx certificate. 

To install a PFX certificate to the current user's personal store, use the command below:

```powershell
Import-PfxCertificate -FilePath ./TestPFXCert.pfx -CertStoreLocation Cert:\CurrentUser\My -Password testpassword 
```

To install into the system personal location change the store location in the command above from `Cert:\CurrentUser\My` to `Cert:\LocalMachine\My`

To install a PFX certificate the current user's trusted store, use the command below:

```powershell
Import-PfxCertificate -FilePath ./TestPFXCert.pfx -CertStoreLocation Cert:\CurrentUser\Root -Password testpassword 
```

Installing into the current user Root location will pop up a dialog to confirm the certificate installation but installing into the system Root location will not pop up any dialog.

To install into the system trusted location change the store location in the command above from `Cert:\CurrentUser\Root` to `Cert:\LocalMachine\Root`

## Add a certificate as trusted

An untrusted certificate is pretty much as dangerous and suspicious than no certificate at all, therefore, it is important to add our certificate to the trusted path.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/dnwitnlgtk0gpfg1fdx1.png)

Certificates can only be generated in the `Cert:\LocalMachine\My` store location. Since any certificate in this location is marked as untrusted by default, to make it secure it has to be moved from `Cert:\LocalMachine\My` to `Cert:\LocalMachine\Root`. 

```powershell
Move-Item -Path $cert.PSPath -Destination "Cert:\LocalMachine\Root"
```

Now the certificate will be marked as trusted. When used to sign a script or application the script will be marked secure for execution.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/tw5v3zkckbtdrym66o4c.png)

## Check for duplicate certificate 

To avoid re-adding an already existing certificate into the certificate store location, the already existing certificate should be checked and removed before adding a new certificate with the same subject. Powershell automatically creates a virtual drive path for the `cert:` label and behaves like a normal directory. The snippet below checks the `cert:\LocalMachine\My` path of the certificate store to find a certificate using its Subject value. 

```powershell
$cert_name = "Test Cert"
ForEach ($cert in (ls cert:\LocalMachine\My)) {
    If ($cert.Subject -eq "CN=$cert_name") {
        //the certificate can be deleted or edited in here
    }
}
```

## Exporting and Importing certificate

The commands to import and export a pfx certificate are different from commands to export and import a regular certificate. The `Import-PfxCertificate` cmdlet is used for importing a passworded pfx certificate and `Export-PfxCertificate` cmdlet for exporting a certificate from its store location to a new file location.

### Exporting pfx certificate

To export a pfx certificate a password is needed for encrypting the private key. 

In the example below, we use the Subject property to find the certificate to be exported by selecting the certificate whose Subject value equals **Test**, 

```powershell
$certificate = Get-ChildItem -Path Cert:\LocalMachine\My\ | Where-Object {$_.Subject -match "Test"}
```

After selecting the certificate it can be exported from its store location to a folder with the command below:

```powershell
$password= "@OurPassword1" | ConvertTo-SecureString -AsPlainText -Force
Export-PfxCertificate -Cert $certificate -FilePath $env:USERPROFILE\Documents\TestCert.pfx -Password $password
```

### Importing pfx certificate

Downloaded or exported pfx certificates can be installed using the `Import-PfxCertificate` cmdlet. The password for the certificate and location are required for importing. 

The command below imports the pfx certificate we exported earlier:

```powershell
$password= "@OurPassword1" | ConvertTo-SecureString -AsPlainText -Force
Import-PfxCertificate -Exportable -Password $password -CertStoreLocation Cert:\LocalMachine\My -FilePath $env:USERPROFILE\Documents\TestCert.pfx
```

The `-Exportable` switch marks the private key as exportable.

## Export a Certificate from pfx

To export certificate from a pfx file, the combined cmdlet `Get-PfxCertificate` and `Export-Certificate` is used. Get-PfxCertificate is used to locate the pfx certificate and Export-Certificate is used to export the specified certificate to a FilePath. 

This process is not seamless as it requires the user to input the password for the pfx certificate.

The following command exports a DER encoded (binary) certificate from a pfx file. 

```powershell
Get-PfxCertificate -FilePath $env:USERPROFILE\Documents\TestCert.pfx | 
Export-Certificate -FilePath $env:USERPROFILE\Documents\TestCert.cer -Type CERT
```

Note the `-Type` switch which is used to indicates the type of certificate to be exported, in this example a **DER encoded (binary) CERT** certificate. There are other types that can be exported. They are listed below.


```bash
-Type <CertType>
    Specifies the type of output file for the certificate export as follows. 
     -- SST: A Microsoft serialized certificate store (.sst) file format which can contain one or more certificates. This is the default value for multiple certificates. 
     -- CERT: A .cer file format which contains a single DER-encoded certificate. This is the default value for one certificate. 
     -- P7B: A PKCS#7 file format which can contain one or more certificates.
```

## Deleting a certificate

To remove a certificate, the `Remove-Item` command in Powershell can be used. Before a certificate can be deleted its thumbprint id must be known or the certificate object itself identified. The certificate path can be iterated through, using the snippets above to find the object or thumbprint. 

### Deleting with thumbprint

The snippet below uses the certificate thumbprint for deletion...

```powershell
Get-ChildItem Cert:\LocalMachine\My\D20159B7772E33A6A33E436C938C6FE764367396 | Remove-Item
```

`Get-ChildItem` is a Powershell command to get file/folder information, one of its aliases is `ls`. It simply uses the thumbprint to get the certificate object and pipe it as input to the `Remove-Item` command.

### Find and Delete a certificate using property match

A certificate can be searched in the store location using the `Where-Object` command that accepts a conditional statement that matches the certificate we are looking for. 

```powershell
Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Subject -match 'Test' } |
Remove-Item
```

Notice we use the **Subject** property of a certificate, the other properties are **Thumbprint** and **SerialNumber**.

## Windows Certificate Manager

The Windows Certificate Manager (certmgr.msc) is a Windows essential GUI application to manage certificates. Search for `certmgr` in the Start menu to open the Windows Certificates MMC. The landing view provides an overview of all the logical stores. 

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ghejpo1bs7p8mh6f8jmg.PNG)

Certificates can be installed, delete, import and export from the Windows Certificate Manager. 

## References

[PKIClient](https://docs.microsoft.com/en-us/powershell/module/pkiclient/?view=win10-ps)
[Digital Certificates](https://docs.microsoft.com/en-us/windows/win32/seccrypto/digital-certificates)
[System Store Locations](https://docs.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations)
[Convert .pfx to .cer](https://stackoverflow.com/questions/403174/convert-pfx-to-cer)
[How to Sign PowerShell Script with a Code Signing Certificate](http://woshub.com/how-to-sign-powershell-script-with-a-code-signing-certificate/)
[Managing Certs with Windows Certificate Manager and PowerShell](https://adamtheautomator.com/windows-certificate-manager/)