Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import-VcCertificate PKCS #8 support #228

Merged
merged 4 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 2 additions & 26 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,2 @@
This is a major release. Although every attempt has been made to be backwards compatible, **existing scripts will likely require some updates**. Please read the full release notes.

- TPP is now TLS Protect Datacenter (TLSPDC) and VaaS is now TLS Protect Cloud (TLSPC). All functions have been renamed to prefix with `-Vdc` (Venafi Datacenter) or `-Vc` (Venafi Cloud). Combined platform functions, those prefixed with `-Venafi`, have all been updated to dedicated platform functions. The desire to add additional functionality for each platform and reduce parameter set complexity drove this decision. The only exception to this rule are the functions related to the session. Aliases have been added where applicable.
- VenafiPS is now signed. `Test-ModuleHash` has been deprecated.
- VenafiSession is stored for nested operations each time a function is called directly. This has 2 main benefits:
- Performance enhancement bypassing `Test-VenafiSession` in nested functions
- No longer need to pass VenafiSession to each function when sending function output down the pipeline
- Parallel functionality added for many functions, notably export and import certificates. Ensure you are using PowerShell v7!
- Add Certificate, Key, and Chain PEM to `Export-VdcCertificate` and `Export-VcCertificate` Base64 output
- For PSCredential objects which only required a password and not username, add the ability to provide either a password String, SecureString, or PSCredential.
- `Find-VaasObject` has been replaced with dedicated functions `Find-VcCertificateRequest`, `Find-VcLog`, `Find-VcMachine`, and `Find-VcMachineIdentity`. These functions have property filters specific to their types making it super easy to search.
- Environment variable names updated:
- TPP_SERVER -> VDC_SERVER
- TPP_TOKEN -> VDC_TOKEN
- VAAS_KEY -> VC_KEY
- Add keystore/private key import to `Import-VcCertificate`
- Update `Invoke-VenafiParallel` to be version aware. Parallel on PowerShell v7+, synchronous otherwise
- Add option to save .crt/.key with `Export-VdcCertificate` , [#226](https://github.com/Venafi/VenafiPS/issues/226)
- Update TLSPC searching to make -Order case insensitive
- Fix `Get-TppAttribute -Disabled` not working, [#221](https://github.com/Venafi/VenafiPS/issues/221)
- Fix exporting JKS to a file, [#225](https://github.com/Venafi/VenafiPS/issues/225)
- Add option to save exported certificate and key to separate files, [#226](https://github.com/Venafi/VenafiPS/issues/226)
- `Revoke-TppCertificate` deprecated, use `Invoke-VdcCertificateAction -Revoke`
- Dedicated removal functions created for TLSPC
- Add filters `-IsSelfSigned` and `-IsWildcard` to `Find-VdcCertificate`
- CodeSign Protect functions have been deprecated
- Update `Export-VdcCertificate` to return just certificate if private key isn't available for supporting formats
- Add support for PKCS #8 in `Import-VcCertificate -Data`, by file will come in a future release
4 changes: 2 additions & 2 deletions VenafiPS/Private/Split-CertificateData.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ function Split-CertificateData {
if ($pemLines[$i].Contains('-----END')) {
$thisPemLines = $pemLines[$iStart..$i]
if ( $pemLines[$i].Contains('KEY-----')) {
$keyPem = $thisPemLines | Join-String
$keyPem = ($thisPemLines | Join-String -Separator "`n") -replace "`r"
}
else {
$certPem += $thisPemLines | Join-String
$certPem += ($thisPemLines | Join-String -Separator "`n") -replace "`r"
}
continue
}
Expand Down
46 changes: 37 additions & 9 deletions VenafiPS/Public/Export-VdcCertificate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ function Export-VdcCertificate {

$body | Write-VerboseWithSecret

# function not available in parallel jobs, use this workaround to pass it in via InputObject
$splitCertificateDataFunction = 'function Split-CertificateData {{ {0} }}' -f (Get-Command Split-CertificateData | Select-Object -ExpandProperty Definition)
}

Expand All @@ -240,10 +241,26 @@ function Export-VdcCertificate {
$innerResponse = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'certificates/retrieve' -Body $thisBody
}
catch {
return [pscustomobject]@{
'Path' = $thisBody.CertificateDN
'Error' = $_
'CertificateData' = $null
try {
# key not available, get just the cert
if ( $_.ToString() -like '*failed to lookup private key*') {

# we can't have a p12 without a private key
if ( $thisBody.Format -eq 'PKCS #12' ) {
throw $_
}

$thisBody.IncludePrivateKey = $false
$thisBody.Password = $null
$innerResponse = Invoke-VenafiRestMethod -Method 'Post' -UriLeaf 'certificates/retrieve' -Body $thisBody
}
}
catch {
return [pscustomobject]@{
'Path' = $thisBody.CertificateDN
'Error' = $_
'CertificateData' = $null
}
}
}

Expand All @@ -263,32 +280,43 @@ function Export-VdcCertificate {
}

if ( $using:OutPath ) {

$out = $out | Select-Object -Property * -ExcludeProperty CertificateData

# write the file with the filename provided
$outFile = Join-Path -Path (Resolve-Path -Path $using:OutPath) -ChildPath ($innerResponse.FileName.Trim('"'))
$bytes = [Convert]::FromBase64String($innerResponse.CertificateData)
[IO.File]::WriteAllBytes($outFile, $bytes)

Write-Verbose "Saved $outFile"

$out | Add-Member @{'OutPath' = $outFile }
$out | Add-Member @{'OutFile' = @($outFile) }

if ( $thisBody.Format -in 'Base64 (PKCS#8)' -and $thisBody.IncludePrivateKey) {
# outFile will be .pem with cert and key
# write out the individual files as well
try {
$sw = [IO.StreamWriter]::new($outFile.Replace('.pem', '.crt'), $false, [Text.Encoding]::ASCII)
$crtFile = $outFile.Replace('.pem', '.crt')

$sw = [IO.StreamWriter]::new($crtFile, $false, [Text.Encoding]::ASCII)
$sw.WriteLine($splitData.CertPem)
Write-Verbose ('Saved {0}' -f $outFile.Replace('.pem', '.crt'))
Write-Verbose "Saved $crtFile"

$out.OutFile += $crtFile
}
finally {
if ($null -ne $sw) { $sw.Close() }
}

if ( $thisBody.IncludePrivateKey ) {
try {
$sw = [IO.StreamWriter]::new($outFile.Replace('.pem', '.key'), $false, [Text.Encoding]::ASCII)
$keyFile = $outFile.Replace('.pem', '.key')

$sw = [IO.StreamWriter]::new($keyFile, $false, [Text.Encoding]::ASCII)
$sw.WriteLine($splitData.KeyPem)
Write-Verbose ('Saved {0}' -f $outFile.Replace('.pem', '.key'))
Write-Verbose "Saved $keyFile"

$out.OutFile += $keyFile
}
finally {
if ($null -ne $sw) { $sw.Close() }
Expand Down
80 changes: 62 additions & 18 deletions VenafiPS/Public/Import-VcCertificate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@ function Import-VcCertificate {
Import one or more certificates

.DESCRIPTION
Import one or more certificates and their private keys. Currently PKCS12 (.pfx or .p12) is supported.
Import one or more certificates and their private keys. Currently PKCS #8 and PKCS #12 (.pfx or .p12) are supported.

.PARAMETER Path
Path to a certificate file. Provide either this or -Data.

.PARAMETER Data
Contents of a certificate/key to import. Provide either this or -Path.

.PARAMETER Pkcs8
Provided -Data is in PKCS #8 format

.PARAMETER Pkcs12
Provided -Data is in PKCS #12 format

.PARAMETER PrivateKeyPassword
Password the private key was encrypted with

.PARAMETER ThrottleLimit
Limit the number of threads when running in parallel; the default is 100. Applicable to PS v7+ only.
Limit the number of threads when running in parallel; the default is 10. Applicable to PS v7+ only.
100 keystores will be imported at a time so it's less important to have a very high throttle limit.

.PARAMETER VenafiSession
Authentication for the function.
Expand Down Expand Up @@ -68,6 +78,7 @@ function Import-VcCertificate {
[String] $Path,

[Parameter(Mandatory, ParameterSetName = 'Pkcs12', ValueFromPipelineByPropertyName)]
[Parameter(Mandatory, ParameterSetName = 'Pkcs8', ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[Alias('CertificateData')]
Expand All @@ -76,11 +87,14 @@ function Import-VcCertificate {
[Parameter(Mandatory, ParameterSetName = 'Pkcs12')]
[switch] $Pkcs12,

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

[Parameter(Mandatory)]
[psobject] $PrivateKeyPassword,

[Parameter()]
[int32] $ThrottleLimit = 100,
[int32] $ThrottleLimit = 10,

[Parameter()]
[psobject] $VenafiSession
Expand All @@ -94,7 +108,7 @@ function Import-VcCertificate {
Import-Module "$PSScriptRoot/../import/PSSodium/PSSodium.psd1" -Force
}

$vSat = Get-VcSatellite -All | Select-Object -First 1
$vSat = Get-VcSatellite -All | Where-Object { $_.edgeStatus -eq 'ACTIVE' } | Select-Object -First 1

$pkPassString = if ( $PrivateKeyPassword -is [string] ) { $PrivateKeyPassword }
elseif ($PrivateKeyPassword -is [securestring]) { ConvertFrom-SecureString -SecureString $PrivateKeyPassword -AsPlainText }
Expand Down Expand Up @@ -128,12 +142,26 @@ function Import-VcCertificate {
)
}
else {
# check if Data exists since we allow null/empty in case piping from another function and data is not there
if ( $Data ) {
$allCerts.Add(@{
'CertData' = $Data -replace "`r|`n|-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----"
'Format' = $PSCmdlet.ParameterSetName

$addMe = @{
'Format' = $PSCmdlet.ParameterSetName
}

switch ($PSCmdlet.ParameterSetName) {
'Pkcs12' {
$addMe.'CertData' = $Data -replace "`r|`n|-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----"
}
)

'Pkcs8' {
$splitData = Split-CertificateData -CertificateData $Data
$addMe.CertPem = $splitData.CertPem
if ( $splitData.KeyPem ) { $addMe.KeyPem = $splitData.KeyPem }
}
}

$allCerts.Add($addMe)
}
}

Expand All @@ -149,23 +177,32 @@ function Import-VcCertificate {
for ($i = 0; $i -lt $allCerts.Count; $i += 100) {

$params = @{
Method = 'post'
UriRoot = 'outagedetection/v1'
UriLeaf = 'certificates/imports'
Body = @{
Method = 'post'
UriRoot = 'outagedetection/v1'
UriLeaf = 'certificates/imports'
Body = @{
'edgeInstanceId' = $vSat.vsatelliteId
'encryptionKeyId' = $vSat.encryptionKeyId
}
VenafiSession = $VenafiSession
}

switch ($allCerts[$i].Format) {
'Pkcs12' {
$keystores = $allCerts[$i..($i + 99)] | ForEach-Object {
$keystores = foreach ($thisCert in $allCerts[$i..($i + 99)]) {
switch ($allCerts[$i].Format) {
'Pkcs12' {
@{
'pkcs12Keystore' = $_.CertData
'pkcs12Keystore' = $thisCert.CertData
'dekEncryptedPassword' = $dekEncryptedPassword
}
}

'Pkcs8' {
$thisKeystore = @{
'certificate' = $thisCert.CertPem
'dekEncryptedPassword' = $dekEncryptedPassword
}
if ( $thisCert.KeyPem ) { $thisKeystore.passwordEncryptedPrivateKey = $thisCert.KeyPem }
$thisKeystore
}
}
}
Expand All @@ -183,9 +220,16 @@ function Import-VcCertificate {
$jobResponse = invoke-VenafiRestMethod -UriRoot 'outagedetection/v1' -UriLeaf "certificates/imports/$($requestResponse.id)"
Start-Sleep 2
} until (
$jobResponse.status -eq 'COMPLETED'
$jobResponse.status -in 'COMPLETED', 'FAILED'
)
$jobResponse.results

if ( $jobResponse.status -eq 'COMPLETED' ) {
$jobResponse.results
}
else {
# importing only 1 keycert that fails does not give us any results to return to the user :(
throw 'Import failed'
}
}

$invokeParams = @{
Expand Down
Loading