Import, Export, Validate & EKU
Expose API endpoints to import a certificate and export it in different formats, with simple validation and EKU reporting.
Prerequisites: see Introduction.
Full source
<#
Run a small API for importing and exporting certificates.
- POST /certs/import { filePath, format, password? }
- POST /certs/export { filePath, format, outPath, includePrivateKey?, password? }
FileName: 6.3-Cert-Import-Export.ps1
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
<#
.SYNOPSIS
Converts a plain text password to a SecureString or returns $null if the input is null or empty.
.DESCRIPTION
This function takes a plain text password as input and converts it to a SecureString.
If the input is null or empty, it returns $null.
.PARAMETER Password
The plain text password to convert.
.EXAMPLE
$securePassword = Convert-ToSecureStringOrNull -Password "myP@ssword"
Converts the plain text password "myP@ssword" to a SecureString.
#>
function Convert-ToSecureStringOrNull {
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
param([string]$Password)
if ([string]::IsNullOrWhiteSpace($Password)) { return $null }
return (ConvertTo-SecureString $Password -AsPlainText -Force)
}
Initialize-KrRoot -Path $PSScriptRoot
New-KrLogger |
Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -Name 'myLogger' -SetAsDefault
New-KrServer -Name "Cert Ops API"
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
Enable-KrConfiguration
# POST /certs/import
# Body JSON example: { "filePath": "./devcert.pfx", "password": "p@ss" }
Add-KrMapRoute -Verbs Post -Pattern "/certs/import" -ScriptBlock {
$body = Get-KrRequestBody
try {
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Importing certificate from {filePath}" -Values $body.filePath
$password = Convert-ToSecureStringOrNull -Password ([string]$body.password)
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Password provided: {hasPassword}" -Values ($null -ne $password)
$cert = Import-KrCertificate -FilePath ([string]$body.filePath) -Password $password
if ( $null -eq $cert) {
throw "Certificate import failed."
} else {
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Certificate imported successfully"
}
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Certificate imported successfully: {subject}" -Values $cert.Subject
$purposes = Get-KrCertificatePurpose -Certificate $cert
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Certificate purposes: {purposes}" -Values $purposes
$valid = Test-KrCertificate -Certificate $cert -DenySelfSigned:$false -AllowWeakAlgorithms:$false
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Certificate validity: {valid}" -Values $valid
Write-KrJsonResponse -StatusCode 200 -InputObject @{ subject = $cert.Subject; notAfter = $cert.NotAfter; valid = $valid; purposes = $purposes }
} catch {
Write-KrLog -Level Error -LoggerName 'myLogger' -Message "Certificate import failed" -ErrorRecord $_
Write-KrJsonResponse -StatusCode 400 -InputObject @{ error = $_.Exception.Message }
}
}
# POST /certs/export
# Body JSON example: { "filePath": "./devcert.pfx", "format": "Pfx", "outPath": "./devcert", "includePrivateKey": true, "password": "p@ss" }
Add-KrMapRoute -Verbs Post -Pattern "/certs/export" -ScriptBlock {
$body = Get-KrRequestBody
try {
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Exporting certificate from {filePath} with format {format} to {outPath}" -Values $body.filePath, $body.format, $body.outPath
if (-not $body.outPath) {
throw "Output path (outPath) is required for export."
}
$password = Convert-ToSecureStringOrNull -Password ([string]$body.password)
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Password provided: {hasPassword}" -Values ($null -ne $password)
$cert = New-KrSelfSignedCertificate -DnsNames "temp.example.com" -Exportable -ValidDays 1
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Temporary self-signed certificate created: {subject}" -Values $cert.Subject
$params = @{ Certificate = $cert; FilePath = ([string]$body.outPath); Format = ([string]$body.outFormat) }
if ($body.includePrivateKey) { $params.IncludePrivateKey = [bool]$body.includePrivateKey }
if ($password) { $params.Password = $password }
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Exporting certificate to {outPath} with format {format}" -Values $body.outPath, $body.outFormat
Export-KrCertificate @params | Out-Null
Write-KrLog -Level Debug -LoggerName 'myLogger' -Message "Certificate exported successfully to {outPath} with format {format}" -Values $body.outPath, $body.outFormat
Write-KrJsonResponse -StatusCode 200 -InputObject @{ exported = $true; path = $body.outPath; format = $body.outFormat }
} catch {
Write-KrLog -Level Error -LoggerName 'myLogger' -Message "Certificate export failed" -ErrorRecord $_
Write-KrJsonResponse -StatusCode 400 -InputObject @{ error = $_.Exception.Message }
}
}
Start-KrServer -CloseLogsOnExit
# Clean up and close all the loggers when the server stops
Step-by-step
- Initialize:
Initialize-KrRoot -Path $PSScriptRoot. - Logging: a console logger named
myLoggeris registered at Debug to trace operations. - Server and listener: -
New-KrServer -Name 'Cert Ops API'-Add-KrEndpoint -Port 5000 -IPAddress Loopback-Enable-KrConfiguration - Map routes: - POST
/certs/importreads JSON{ filePath, password? }and returnssubject,notAfter,valid(fromTest-KrCertificate), andpurposes(fromGet-KrCertificatePurpose). Format is auto-detected byImport-KrCertificate. - POST/certs/exportreads JSON{ outPath, outFormat, includePrivateKey?, password? }, creates a temporary self‑signed certificate, and exports it to the requested format/path; returns{ exported, path, format }. - Supported formats: - Import: auto‑detects
Pfx,Pem, orDerbased on file content/extension. - Export:outFormatacceptsPfxorPem(PEM may split key/cert; PFX can bundle with password). - Errors: - Import: returns HTTP 200 with
{ error: '...' }on failure (demo behavior in sample). - Export: returns HTTP 400 with{ error: '...' }on failure. - Start server:
Start-KrServer.
Try it
Save the sample locally so it’s easy to run. Copy the contents of pwsh/tutorial/examples/6.3-Cert-Import-Export.ps1 into a new file in an empty working folder (for example, cert-ops.ps1), then run:
# From your working folder
pwsh .\cert-ops.ps1
# Export a temporary self-signed cert to PFX (include private key)
curl -X POST http://127.0.0.1:5000/certs/export -H "Content-Type: application/json" `
-d '{"outPath":"./exported","outFormat":"Pfx","includePrivateKey":true,"password":"p@ss"}'
# Import a PFX
curl -X POST http://127.0.0.1:5000/certs/import -H "Content-Type: application/json" `
-d '{"filePath":"./exported.pfx","password":"p@ss"}'
PowerShell alternatives:
# Export a temporary self-signed cert to PFX (include private key)
Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:5000/certs/export' -ContentType 'application/json' -Body (@{
outPath = './exported'
outFormat = 'Pfx'
includePrivateKey = $true
password = 'p@ss'
} | ConvertTo-Json)
# Import a PFX
Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:5000/certs/import' -ContentType 'application/json' -Body (@{
filePath = './exported.pfx'
password = 'p@ss'
} | ConvertTo-Json)
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Import returns error | Wrong path/password or unsupported file | Verify file exists; pass PFX password; use supported formats |
| Export file not created | Permission issues or bad path | Choose a writable path; avoid restricted directories |
| Purposes empty | Certificate lacks EKU | Generate/choose a cert with desired EKUs (e.g., ServerAuth) |
| 200 with error payload | Sample returns 200 on import failure | Treat presence of error as failure in your client logic |
References
- Import-KrCertificate
- Export-KrCertificate
- Test-KrCertificate
- Get-KrCertificatePurpose
- New-KrSelfSignedCertificate
Previous / Next
Go back to Generate a CSR or return to Certificates.