Add INF import and per-INF issuance
This commit is contained in:
162
certy.ps1
162
certy.ps1
@@ -234,6 +234,62 @@ function Get-DefaultValue {
|
||||
return $prop.Value
|
||||
}
|
||||
|
||||
function Get-HostsFromInfLines {
|
||||
param([string[]]$Lines)
|
||||
$set = New-Object System.Collections.Generic.HashSet[string] ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
if (-not $Lines) { return @() }
|
||||
|
||||
foreach ($line in $Lines) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
||||
if ($line.TrimStart() -match '^[;#]') { continue }
|
||||
$matches = [regex]::Matches($line, '(?i)\bdns\s*=\s*([^&",\s]+)')
|
||||
foreach ($match in $matches) {
|
||||
$value = $match.Groups[1].Value.Trim()
|
||||
if ($value) { [void]$set.Add($value) }
|
||||
}
|
||||
}
|
||||
|
||||
if ($set.Count -gt 0) { return $set | Sort-Object }
|
||||
|
||||
$subjectLine = $Lines | Where-Object { $_ -match '(?i)^\s*subject\s*=' } | Select-Object -First 1
|
||||
if ($subjectLine -and ($subjectLine -match '(?i)\bCN\s*=\s*([^,"]+)')) {
|
||||
$cn = $Matches[1].Trim()
|
||||
if ($cn) { [void]$set.Add($cn) }
|
||||
}
|
||||
|
||||
return $set | Sort-Object
|
||||
}
|
||||
|
||||
function Remove-InfSubjectLines {
|
||||
param([string[]]$Lines)
|
||||
$removed = $false
|
||||
$filtered = foreach ($line in $Lines) {
|
||||
if ($line -match '(?i)^\s*subject\s*=') {
|
||||
$removed = $true
|
||||
continue
|
||||
}
|
||||
$line
|
||||
}
|
||||
return [pscustomobject]@{
|
||||
Lines = $filtered
|
||||
Removed = $removed
|
||||
}
|
||||
}
|
||||
|
||||
function Save-SanitizedInf {
|
||||
param(
|
||||
[string]$FileName,
|
||||
[string[]]$Lines
|
||||
)
|
||||
$dir = Join-Path $env:ProgramData "Certy\\inf-sanitized"
|
||||
if (-not (Test-Path -Path $dir -PathType Container)) {
|
||||
New-Item -Path $dir -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
$outPath = Join-Path $dir $FileName
|
||||
Set-Content -Path $outPath -Value $Lines -Encoding ascii
|
||||
return $outPath
|
||||
}
|
||||
|
||||
function Resolve-HostEntry {
|
||||
param(
|
||||
[string]$Name,
|
||||
@@ -712,6 +768,22 @@ $panel.Controls.Add($csrImportBtn)
|
||||
Style-ButtonSecondary $csrImportBtn
|
||||
$y += $rowHeight + $gap
|
||||
|
||||
$infLabel = Add-Label "INF folder (optional)" $xLabel $y $labelWidth $rowHeight
|
||||
$infFolderBox = Add-TextBox $xInput $y ($inputWidth - ((2 * $buttonWidth) + $buttonGap)) $rowHeight $false
|
||||
$infBrowseBtn = New-Object System.Windows.Forms.Button
|
||||
$infBrowseBtn.Text = "Browse"
|
||||
$infBrowseBtn.Location = [System.Drawing.Point]::new(($xInput + $inputWidth - (2 * $buttonWidth + $buttonGap)), ($y - 1))
|
||||
$infBrowseBtn.Size = [System.Drawing.Size]::new($buttonWidth, 26)
|
||||
$panel.Controls.Add($infBrowseBtn)
|
||||
Style-ButtonSecondary $infBrowseBtn
|
||||
$infImportBtn = New-Object System.Windows.Forms.Button
|
||||
$infImportBtn.Text = "Import INF"
|
||||
$infImportBtn.Location = [System.Drawing.Point]::new(($xInput + $inputWidth - $buttonWidth), ($y - 1))
|
||||
$infImportBtn.Size = [System.Drawing.Size]::new($buttonWidth, 26)
|
||||
$panel.Controls.Add($infImportBtn)
|
||||
Style-ButtonSecondary $infImportBtn
|
||||
$y += $rowHeight + $gap
|
||||
|
||||
$useFqdnBox = Add-CheckBox "Input contains FQDNs (otherwise default zone is appended)" $xInput $y $inputWidth $rowHeight
|
||||
$y += $rowHeight + $gap
|
||||
|
||||
@@ -909,6 +981,8 @@ $sectionAnchors = @{
|
||||
Logs = $sectionActivity
|
||||
}
|
||||
|
||||
$script:infRequests = @()
|
||||
|
||||
foreach ($key in $sectionAnchors.Keys) {
|
||||
$label = $navLabels[$key]
|
||||
if (-not $label) { continue }
|
||||
@@ -996,7 +1070,7 @@ function Show-HelpDialog {
|
||||
|
||||
$stepY = 28
|
||||
$steps = @(
|
||||
"Add hostnames (one per line, CSV, or CSR folder) then click 1) Preview.",
|
||||
"Add hostnames (one per line, CSV, CSR, or INF folder) then click 1) Preview.",
|
||||
"Click 2) Scan and select the DNS server (Primary is 10.106.60.1). Replication runs from there.",
|
||||
"Set the replication wait time. Typical value: 15 minutes.",
|
||||
"Choose output type (PEM or PFX). If PFX, supply a password.",
|
||||
@@ -1192,6 +1266,9 @@ function Apply-Layout {
|
||||
$csrFolderBox.Width = $inputWidthCalc - ((2 * $buttonWidth) + $buttonGap)
|
||||
$csrBrowseBtn.Left = $xInput + $inputWidthCalc - ((2 * $buttonWidth) + $buttonGap)
|
||||
$csrImportBtn.Left = $xInput + $inputWidthCalc - $buttonWidth
|
||||
$infFolderBox.Width = $inputWidthCalc - ((2 * $buttonWidth) + $buttonGap)
|
||||
$infBrowseBtn.Left = $xInput + $inputWidthCalc - ((2 * $buttonWidth) + $buttonGap)
|
||||
$infImportBtn.Left = $xInput + $inputWidthCalc - $buttonWidth
|
||||
$zoneBox.Width = $inputWidthCalc
|
||||
$ipBox.Width = $inputWidthCalc - ($buttonWidth + $buttonGap)
|
||||
$ipRefreshBtn.Left = $xInput + $inputWidthCalc - $buttonWidth
|
||||
@@ -1276,6 +1353,13 @@ $csrBrowseBtn.Add_Click({
|
||||
}
|
||||
})
|
||||
|
||||
$infBrowseBtn.Add_Click({
|
||||
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
||||
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
$infFolderBox.Text = $dialog.SelectedPath
|
||||
}
|
||||
})
|
||||
|
||||
$csrImportBtn.Add_Click({
|
||||
try {
|
||||
$folder = $csrFolderBox.Text.Trim()
|
||||
@@ -1298,6 +1382,58 @@ $csrImportBtn.Add_Click({
|
||||
}
|
||||
})
|
||||
|
||||
$infImportBtn.Add_Click({
|
||||
try {
|
||||
$folder = $infFolderBox.Text.Trim()
|
||||
if (-not $folder) { throw "INF folder is empty." }
|
||||
if (-not (Test-Path -Path $folder -PathType Container)) { throw "INF folder not found: $folder" }
|
||||
|
||||
$infFiles = Get-ChildItem -Path $folder -Filter *.inf -File -Recurse
|
||||
if (-not $infFiles) {
|
||||
& $logAction "No INF files found in $folder"
|
||||
return
|
||||
}
|
||||
|
||||
$script:infRequests = @()
|
||||
$infHosts = @()
|
||||
$subjectRemovedCount = 0
|
||||
|
||||
foreach ($infFile in $infFiles) {
|
||||
$lines = Get-Content -Path $infFile.FullName
|
||||
$hosts = @(Get-HostsFromInfLines -Lines $lines)
|
||||
$sanitize = Remove-InfSubjectLines -Lines $lines
|
||||
$sanitizedPath = Save-SanitizedInf -FileName $infFile.Name -Lines $sanitize.Lines
|
||||
if ($sanitize.Removed) { $subjectRemovedCount++ }
|
||||
|
||||
if ($hosts.Count -eq 0) {
|
||||
& $logAction "INF $($infFile.Name): no hostnames detected."
|
||||
continue
|
||||
}
|
||||
|
||||
$script:infRequests += [pscustomobject]@{
|
||||
File = $infFile.FullName
|
||||
Hosts = $hosts
|
||||
Sanitized = $sanitizedPath
|
||||
}
|
||||
$infHosts += $hosts
|
||||
}
|
||||
|
||||
$infHosts = @($infHosts | Where-Object { $_ } | Sort-Object -Unique)
|
||||
if ($infHosts.Count -gt 0) {
|
||||
$currentHosts = Split-List $hostsBox.Text
|
||||
$merged = Merge-Hostnames -Existing $currentHosts -NewItems $infHosts
|
||||
$hostsBox.Text = ($merged -join [Environment]::NewLine)
|
||||
}
|
||||
|
||||
& $logAction "Imported $($infFiles.Count) INF file(s), added $($infHosts.Count) hostname(s)."
|
||||
if ($subjectRemovedCount -gt 0) {
|
||||
& $logAction "Removed Subject line from $subjectRemovedCount INF file(s) (sanitized copies saved)."
|
||||
}
|
||||
} catch {
|
||||
& $logAction "Error: $($_.Exception.Message)"
|
||||
}
|
||||
})
|
||||
|
||||
$dnsScanBtn.Add_Click({
|
||||
try {
|
||||
$dnsListBox.Items.Clear()
|
||||
@@ -1506,7 +1642,29 @@ $runBtn.Add_Click({
|
||||
if (-not (Test-Path -Path $wacsPath -PathType Leaf)) {
|
||||
throw "WACS not found at: $wacsPath"
|
||||
}
|
||||
if ($perHostBox.Checked) {
|
||||
if ($script:infRequests -and $script:infRequests.Count -gt 0) {
|
||||
& $logAction "INF requests detected; issuing one certificate per INF file."
|
||||
foreach ($req in $script:infRequests) {
|
||||
$reqEntries = @($req.Hosts | ForEach-Object { Resolve-HostEntry -Name $_ -Zone $zone -UseProvidedFqdn $useFqdnBox.Checked } | Where-Object { $_ })
|
||||
$reqFqdns = @($reqEntries | ForEach-Object { $_.Fqdn } | Where-Object { $_ })
|
||||
if ($reqFqdns.Count -eq 0) {
|
||||
& $logAction "INF $([System.IO.Path]::GetFileName($req.File)) skipped (no hosts)."
|
||||
continue
|
||||
}
|
||||
& $logAction "Requesting certificate for INF $([System.IO.Path]::GetFileName($req.File)) with $($reqFqdns.Count) hostname(s)."
|
||||
Invoke-Wacs `
|
||||
-WacsPath $wacsPath `
|
||||
-HostFqdns $reqFqdns `
|
||||
-OutputType $outputType `
|
||||
-OutputPath $outputPath `
|
||||
-PfxPassword ($(if ($usePfxPassword) { $pfxPasswordBox.Text } else { "" })) `
|
||||
-BaseUri $baseUriBox.Text.Trim() `
|
||||
-Validation $validationBox.Text.Trim() `
|
||||
-ValidationPort $validationPortBox.Text.Trim() `
|
||||
-Verbose $verboseBox.Checked `
|
||||
-Log $logAction
|
||||
}
|
||||
} elseif ($perHostBox.Checked) {
|
||||
foreach ($entry in $hostEntries) {
|
||||
& $logAction "Requesting certificate for $($entry.Fqdn)."
|
||||
Invoke-Wacs `
|
||||
|
||||
Reference in New Issue
Block a user