4

I manage the Domain Controllers centrally, but the site admins manage their own digital senders locally. I can easily export an X509 certificate (private key not needed) with the whole chain from a Windows Server 2008 R2 Domain Controller to a p7b file through the wizard:

~~~~~~~~~~~~~~~~~

...5. The Certificate Export Wizard opens. Click Next.

  1. In the Export File Format dialog box, do the following:

    a. Select Cryptographic Message Syntax Standard – PKCS #7 Certificates (.P7B).

    b. Check Include all certificates in the certification path if possible.

    c. Click Next.

  2. In the File to Export dialog box, click Browse.

  3. In the Save As dialog box, do the following:

    a. In the File Name box, type ciroots.p7b.

    b. In the Save as type box, select PKCS #7 Certificates (*.p7b).

    c. Click Save.

  4. In the File to Export dialog box, click Next.

  5. On the Completing the Certificate Export Wizard page, click Finish.

~~~~~~~~~~~~~~~~~

It works great. The resulting file imports just fine into a digital sender for authentication. It gives the site admins access to the other certs in the chain if they have not already imported them. It does not need to contain the private key, since it works fine without it.

The trouble is, I would need to do this manually, literally dozens of times, once for each business site, since each has their own Domain Controllers, each with their own certificate. There must be a way I can automate this certificate export (PowerShell w/.NET, certutil.exe, etc.). Maybe something that uses System.Security.Cryptography.X509Certificates X509IncludeOption with WholeChain, but I can't get it to work:

$Cert = (dir Cert:\localmachine\my)[0]

# PKCS7 cert export with .p7b file extension.

$CertCollection = New-Object

System.Security.Cryptography.X509Certificates.X509Certificate2Collection

$Cert | %{[void]$CertCollection.Add($_)}

$Exported_pkcs7 = $CertCollection.Export('Pkcs7')

$out_FileName = $ENV:COMPUTERNAME + ".p7b"

$My_Export_Path = 'd:\CertFiles\' + $out_FileName

Set-Content -path $My_Export_Path -Value $Exported_pkcs7 -encoding Byte

With this code, I only get the certificate, not the rest of the certificates in its chain. I don't need the whole script, just the part that duplicates the export w/chain that I can already do manually through the GUI.

JCSunday
  • 41
  • 2
  • 4

2 Answers2

5

You need to build the certificate chain to get chain certificates and add them to collection:

function Export-Certificate {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
        [Parameter(Mandatory = $true)]
        [IO.FileInfo]$OutputFile,
        [switch]$IncludeAllCerts
    )
    $certs = New-Object Security.Cryptography.X509Certificates.X509Certificate2Collection
    if ($IncludeAllCerts) {
        $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
        $chain.ChainPolicy.RevocationMode = "NoCheck"
        [void]$chain.Build($Certificate)
        $chain.ChainElements | ForEach-Object {[void]$certs.Add($_.Certificate)}
        $chain.Reset()
    } else {
        [void]$certs.Add($Certificate)
    }
    Set-Content -Path $OutputFile.FullName -Value $certs.Export("pkcs7") -Encoding Byte
}
Crypt32
  • 12,850
  • 2
  • 41
  • 70
  • A coworker came up with something very similar that worked, and because of that, the priority on this dropped, but this is on my to-do list to try it exactly your way and see if there are any differences in output from my coworker's method. Thank you CryptoGuy. – JCSunday Nov 12 '15 at 18:23
0

It's quite some time this was posted, but it was of help. I improved the script slightly to allow for pipelining and added help.

function Export-CertificateChain {
<#
.SYNOPSIS
    Create p7b certificate container.
.DESCRIPTION
    Create p7b certificate container.
.EXAMPLE
    PS C:\> ls "C:\PKI Trust Chain\Certificates" -File -Recurse | Get-PfxCertificate | where issuer -match 'Internal|External' | Export-CertificateChain -OutputFile C:\Temp\PKITrustChain.p7b
    Loop thru the folder "C:\PKI Trust Chain\Certificates" (assuming it contains only certificates), load the certficiates and add all certificates where issuer matches into the p7b file. 
.EXAMPLE
    PS C:\> ls "cert:\localMachine\" -Recurse | where issuer -match 'Internal|External' | Export-CertificateChain -OutputFile C:\Temp\PKITrustChain.p7b
    Loop thru the certificate stroe where issuer matches and adds them into the p7b file. 
.INPUTS
    [Security.Cryptography.X509Certificates.X509Certificate2], [IO.FileInfo]
.OUTPUTS
    None.
.NOTES
Author: Patrick Sczepanski  (Vadims Podans)
Original script found: https://stackoverflow.com/questions/33512409/automate-export-x509-certificate-w-chain-from-server-2008-r2-to-a-p7b-file-witho
Version: 20200505
#>
    [CmdletBinding()]
    param(
        # certificate to add to p7b file
        [Parameter(Mandatory = $true, ValueFromPipeline=$true)]
        [Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
        # path an name of the p7b container
        [Parameter(Mandatory = $true)]
        [IO.FileInfo]$OutputFile,
        # automatically add the trust chain for each certificate. Requires the trust chain being available.
        [switch]$IncludeAllCerts
    )
    Begin{
        $certs = New-Object Security.Cryptography.X509Certificates.X509Certificate2Collection
    }
    Process {
        if ($IncludeAllCerts) {
            $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
            $chain.ChainPolicy.RevocationMode = "NoCheck"
            [void]$chain.Build($Certificate)
            $chain.ChainElements | ForEach-Object {[void]$certs.Add($_.Certificate)}
            $chain.Reset()
        } else {
            [void]$certs.Add($Certificate)
        }
    }
    End {
        Set-Content -Path $OutputFile.FullName -Value $certs.Export("pkcs7") -Encoding Byte
    }
}