Public/Certificates/Export-JIMCertificate.ps1

# Copyright (c) Tetron Limited. All rights reserved.
# Licensed under the Tetron Commercial License. See LICENSE file in the project root.

function Export-JIMCertificate {
    <#
    .SYNOPSIS
        Exports a certificate from JIM's trusted certificate store.
 
    .DESCRIPTION
        Downloads the raw certificate data in DER format. The certificate can
        be converted to PEM format using standard tools like OpenSSL if needed.
        A certificate added by file path is read from that file on the JIM
        server; if the file is missing or no longer holds a certificate, the
        export fails and says so.
 
    .PARAMETER Id
        The unique identifier (GUID) of the certificate to export.
 
    .PARAMETER Path
        The file path to save the certificate to.
 
    .PARAMETER Force
        Overwrites the file if it already exists.
 
    .PARAMETER PassThru
        Returns the certificate's bytes instead of writing them to a file.
 
    .OUTPUTS
        With -PassThru, the certificate's bytes, DER-encoded; otherwise nothing, the certificate being
        written to -Path.
 
    .EXAMPLE
        Export-JIMCertificate -Id $certId -Path "./my-cert.cer"
 
        Exports the certificate to a file.
 
    .EXAMPLE
        Export-JIMCertificate -Id $certId -Path "./my-cert.cer" -Force
 
        Exports the certificate, overwriting if the file exists.
 
    .EXAMPLE
        $bytes = Export-JIMCertificate -Id $certId -PassThru
        [System.IO.File]::WriteAllBytes("./cert.der", $bytes)
 
        Gets the certificate bytes for custom processing.
 
    .LINK
        Get-JIMCertificate
        Add-JIMCertificate
        Test-JIMCertificate
    #>

    [CmdletBinding(DefaultParameterSetName = 'ToFile')]
    [OutputType([byte[]], ParameterSetName = 'PassThru')]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [Guid]$Id,

        [Parameter(Mandatory, ParameterSetName = 'ToFile')]
        [string]$Path,

        [Parameter(ParameterSetName = 'ToFile')]
        [switch]$Force,

        [Parameter(Mandatory, ParameterSetName = 'PassThru')]
        [switch]$PassThru
    )

    process {
        Write-Verbose "Exporting certificate: $Id"

        # Check if connection exists
        if (-not $script:JIMConnection) {
            Write-Error "You are not connected to JIM. Run Connect-JIM -Url <your JIM URL> to authenticate, then try again."
            return
        }

        try {
            $uri = "$($script:JIMConnection.Url)/api/v1/certificates/$Id/download"

            Update-JIMExpiringAccessToken

            $headers = Get-JIMCredentialHeader

            # Use Invoke-WebRequest to get binary data
            # -Debug:$false: the debug record would print the Authorization header verbatim.
            # See the same guard in Invoke-JIMApi.
            $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get -Debug:$false

            if ($PassThru) {
                return $response.Content
            }

            # Save to file
            $fullPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)

            if ((Test-Path $fullPath) -and -not $Force) {
                Write-Error "File already exists: $fullPath. Use -Force to overwrite."
                return
            }

            [System.IO.File]::WriteAllBytes($fullPath, $response.Content)
            Write-Verbose "Certificate exported to: $fullPath"
        }
        catch {
            # The API answers an error with a JSON body; report its message rather than the body itself.
            $errorMessage = "$_"
            if ($_.ErrorDetails.Message) {
                try {
                    $errorObj = $_.ErrorDetails.Message | ConvertFrom-Json
                    $errorMessage = $errorObj.message ?? $errorObj.Message ?? $errorMessage
                }
                catch {
                    # Not JSON; keep the error as it stands
                }
            }
            Write-Error "Failed to export certificate: $errorMessage"
        }
    }
}