certificates.psm1

# Helpers
function Install-CertificateViaApi
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]$CertificateFile,
        [Parameter(Mandatory=$false)]
        [SecureString]$Password,
        [Parameter(Mandatory=$true)]
        [string]$Url
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Import-Module -Name "$PSScriptRoot\ps-utilities.psm1" -Scope Local

    $local:CertificateContents = (Get-CertificateFileContents $CertificateFile)
    if (-not $CertificateContents)
    {
        throw "No valid certificate to upload"
    }

    if (-not $Password)
    {
        Write-Host "For no password just press enter..."
        $Password = (Read-host "Password" -AsSecureString)
    }
    $local:PasswordPlainText = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password))

    Write-Host "Uploading Certificate..."
    if ($local:PasswordPlainText)
    {
        $local:NewCertificate = (Invoke-SgDevOpsMethod POST $Url -Body @{
                Base64CertificateData = "$($local:CertificateContents)";
                Passphrase = "$($local:PasswordPlainText)"
            })
    }
    else
    {
        $local:NewCertificate = (Invoke-SgDevOpsMethod POST $Url -Body @{
                Base64CertificateData = "$($local:CertificateContents)"
            })
    }

    $local:NewCertificate
}

<#
.SYNOPSIS
Get TLS server certificate in use by the Secrets Broker service.
 
.DESCRIPTION
Secrets Broker uses TLS to protect its API. This cmdlet shows the certificate
that is currently being used to authenticate the server and protect
communications.
 
.EXAMPLE
Get-SgDevOpsSslCertificate
#>

function Get-SgDevOpsSslCertificate
{
    [CmdletBinding()]
    Param(
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Invoke-SgDevOpsMethod GET "Safeguard/WebServerCertificate"
}

<#
.SYNOPSIS
Install a new TLS server certificate in the Secrets Broker service.
 
.DESCRIPTION
Secrets Broker uses TLS to protect its API. This cmdlet installs a new
certificate that will be used to authenticate the server and protect
communications.
 
This cmdlet can be used to upload a PFX (PKCS#12) certificate file which
includes the private key, or it can be used to upload a PEM encoded
certificate file without a private key as issued based on a CSR obtained
using New-SgDevOpsCsr.
 
Changing the TLS server certificate causes Secrets Broker to restart, and you
will need to reconnect using Connect-SgDevOps.
 
.PARAMETER CertificateFile
The path to a certificate file (either PFX or PEM).
 
.PARAMETER Password
In the case of a PFX file including a private key, this parameter may be used
to specify a password to decrypt the PFX file.
 
.EXAMPLE
Install-SgDevOpsSslCertificate sslcert.pfx
 
.EXAMPLE
Install-SgDevOpsSslCertificate C:\sslcert.pem
 
.EXAMPLE
Install-SgDevOpsSslCertificate .\path\to\sslcert.crt
#>

function Install-SgDevOpsSslCertificate
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$CertificateFile,
        [Parameter(Mandatory=$false, Position=1)]
        [SecureString]$Password
    )

    Install-CertificateViaApi -CertificateFile $CertificateFile -Password $Password -Url "Safeguard/WebServerCertificate"

    Write-Host "The Secrets Broker will restart, you must reconnect using Connect-SgDevOps."
}

<#
.SYNOPSIS
Remove the TLS server certificate in use by the Secrets Broker service and
revert to a self-signed certificate.
 
.DESCRIPTION
Secrets Broker uses TLS to protect its API. This cmdlet removes the current
certificate that is currently being used to authenticate the server and protect
communications. It is replaced with a new one generated by Secrets Broker.
 
Changing the TLS server certificate causes Secrets Broker to restart, and you
will need to reconnect using Connect-SgDevOps.
 
.EXAMPLE
Clear-SgDevOpsSslCertificate
#>

function Clear-SgDevOpsSslCertificate
{
    [CmdletBinding()]
    Param(
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Invoke-SgDevOpsMethod DELETE "Safeguard/WebServerCertificate"

    Write-Host "The Secrets Broker will restart, you must reconnect using Connect-SgDevOps."
}

<#
.SYNOPSIS
Get the TLS client certificate that Secrets Broker uses to communicate with
the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. This cmdlet gets the current certificate. Without this
certificate Secrets Broker cannot monitor or synchronize secrets via the
plugins.
 
.EXAMPLE
Get-SgDevOpsClientCertificate
#>

function Get-SgDevOpsClientCertificate
{
    [CmdletBinding()]
    Param(
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Invoke-SgDevOpsMethod GET "Safeguard/ClientCertificate"
}

<#
.SYNOPSIS
Install a new TLS client certificate for Secrets Broker to use to communicate
with the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. This cmdlet installs a new certificate. Secrets Broker uses this
certificate to monitor and synchronize secrets via the plugins.
 
This cmdlet can be used to upload a PFX (PKCS#12) certificate file which
includes the private key, or it can be used to upload a PEM encoded
certificate file without a private key as issued based on a CSR obtained
using New-SgDevOpsCsr.
 
Changing the TLS server certificate causes Secrets Broker to restart, and you
will need to reconnect using Connect-SgDevOps.
 
.PARAMETER CertificateFile
The path to a certificate file (either PFX or PEM).
 
.PARAMETER Password
In the case of a PFX file including a private key, this parameter may be used
to specify a password to decrypt the PFX file.
 
.EXAMPLE
Install-SgDevOpsClientCertificate clientcert.pfx
 
.EXAMPLE
Install-SgDevOpsClientCertificate C:\clientcert.pem
 
.EXAMPLE
Install-SgDevOpsClientCertificate .\path\to\clientcert.crt
#>

function Install-SgDevOpsClientCertificate
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$CertificateFile,
        [Parameter(Mandatory=$false, Position=1)]
        [SecureString]$Password
    )

    Install-CertificateViaApi -CertificateFile $CertificateFile -Password $Password -Url "Safeguard/ClientCertificate"
}

<#
.SYNOPSIS
Remove the TLS client certificate that Secrets Broker uses to communicate with
the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. This cmdlet removes the current certificate. Without this
certificate Secrets Broker cannot monitor or synchronize secrets via the
plugins.
 
.EXAMPLE
Clear-SgDevOpsClientCertificate
#>

function Clear-SgDevOpsClientCertificate
{
    [CmdletBinding()]
    Param(
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Invoke-SgDevOpsMethod DELETE "Safeguard/ClientCertificate"
}

<#
.SYNOPSIS
Request that Secrets Broker create a new certificate signing request.
 
.DESCRIPTION
Secrets Broker uses certificates to secure its API and to connect to Safeguard
A2A API. The most secure way to install certificates is to have Secrets Broker
generate a certificate signing request (CSR). The public key pair is generated
internally and the private key never leaves the box.
 
The CSR returned by this cmdlet can be sent to a CA to be signed. This can be
a corporate CA that is only trusted on your corporate network.
 
.PARAMETER CertificateType
A string containing the type of CSR (ssl or client).
 
.PARAMETER Subject
A string containing the distinguished name of the subject of the CSR.
 
.PARAMETER KeyLength
An integer containing the key length (1024, 2048, 3072, 4096, default: 2048).
 
.PARAMETER IpAddresses
An array of strings containing IP addresses to use in subject alternative names.
 
.PARAMETER DnsNames
An array of strings containing DNS names to use in subject alternative names.
 
.PARAMETER OutFile
A string containing the location to save the CSR.
 
.EXAMPLE
New-SgDevOpsCsr Ssl "CN=ssbdevops.example.com,OU=PAM,O=InfoSec,S=UT,C=US" -KeyLength 3072 -IpAddresses "172.17.17.172" -DnsNames "ssbdevops.example.com","secretsbroker.example.com"
#>

function New-SgDevOpsCsr
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateSet('Ssl', 'Client', IgnoreCase=$true)]
        [string]$CertificateType,
        [Parameter(Mandatory=$true, Position=1)]
        [string]$Subject,
        [Parameter(Mandatory=$false)]
        [ValidateSet(1024, 2048, 3072, 4096)]
        [int]$KeyLength = 2048,
        [Parameter(Mandatory=$false)]
        [string[]]$IpAddresses = $null,
        [Parameter(Mandatory=$false)]
        [string[]]$DnsNames = $null,
        [Parameter(Mandatory=$false,Position=2)]
        [string]$OutFile = "$CertificateType.csr"
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    $local:Parameters = @{
        subjectName = $Subject;
        size = $KeyLength
    }

    switch ($CertificateType)
    {
        "ssl" { $local:Parameters.certType = "WebSsl"; break }
        "client" { $local:Parameters.certType = "A2AClient"; break }
    }

    if ($PSBoundParameters.ContainsKey("IpAddresses"))
    {
        Import-Module -Name "$PSScriptRoot\ps-utilities.psm1" -Scope Local
        $IpAddresses | ForEach-Object {
            if (-not (Test-IpAddress $_))
            {
                throw "$_ is not an IP address"
            }
        }
        $local:Parameters.sanIp = ($IpAddresses -join ",")
    }
    if ($PSBoundParameters.ContainsKey("DnsNames")) { $local:Parameters.sanDns = ($DnsNames -join ",") }

    $local:Csr = (Invoke-SgDevOpsMethod GET "Safeguard/CSR" -Parameters $local:Parameters)
    $local:Csr

    $local:Csr | Out-File -Encoding ASCII -FilePath $OutFile -NoNewline -Force
    Write-Host "`nCSR saved to '$OutFile'"
}

<#
.SYNOPSIS
Get the trusted CA certificate that Secrets Broker uses to validate
communication with the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. In order to determine whether Secrets Broker trusts the TLS server
certificate presented by Safeguard A2A API, it checks against this list of
trusted CA certificates. This cmdlet returns the list of CA certificates
currently trusted by Secrets Broker.
 
.PARAMETER Thumbprint
The thumbprint of a single trusted CA certificate to get.
 
.EXAMPLE
Get-SgDevOpsTrustedCertificate
 
.EXAMPLE
Get-SgDevOpsTrustedCertificate DBABE5E597BA4B9256A4CD91B8884058B718D936
#>

function Get-SgDevOpsTrustedCertificate
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false, Position=0)]
        [string]$Thumbprint
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    if ($Thumbprint)
    {
        Invoke-SgDevOpsMethod GET "Safeguard/TrustedCertificates/$Thumbprint"
    }
    else
    {
        Invoke-SgDevOpsMethod GET "Safeguard/TrustedCertificates"
    }
}

<#
.SYNOPSIS
Install a trusted CA certificate that Secrets Broker can use to validate
communication with the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. In order to determine whether Secrets Broker trusts the TLS server
certificate presented by Safeguard A2A API, it checks against this list of
trusted CA certificates. This cmdlet allows you to install a new CA
certificate in the list.
 
This cmdlet can be used to upload a PEM encoded certificate file without a
private key.
 
.PARAMETER CertificateFile
The path to a certificate file (PEM).
 
.EXAMPLE
Install-SgDevOpsTrustedCertificate C:\root.pem
 
.EXAMPLE
Install-SgDevOpsTrustedCertificate .\path\to\issuing-ca.crt
#>

function Install-SgDevOpsTrustedCertificate
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$CertificateFile
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Import-Module -Name "$PSScriptRoot\ps-utilities.psm1" -Scope Local

    $local:CertificateContents = (Get-CertificateFileContents $CertificateFile)
    if (-not $CertificateContents)
    {
        throw "No valid certificate to upload"
    }

    Write-Host "Uploading Certificate..."
    Invoke-SgDevOpsMethod POST "Safeguard/TrustedCertificates" -Parameters @{ importFromSafeguard = $false } -Body @{
        Base64CertificateData = "$($local:CertificateContents)"
    }
}

<#
.SYNOPSIS
Synchronize all of the trusted CA certificates from Safeguard as trusted CA
certificates in Secrets Broker to use to validate communication with the
Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. In order to determine whether Secrets Broker trusts the TLS server
certificate presented by Safeguard A2A API, it checks against this list of
trusted CA certificates. This cmdlet requests that Secrets Broker look up all
of the trusted CA certificates from Safeguard and add them to its own list.
 
.EXAMPLE
Sync-SgDevOpsTrustedCertificate
#>

function Sync-SgDevOpsTrustedCertificate
{
    [CmdletBinding()]
    Param(
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    Write-Host "Synchronizing Trusted Certificates..."
    Invoke-SgDevOpsMethod POST "Safeguard/TrustedCertificates" -Parameters @{ importFromSafeguard = $true } -JsonBody "{}"
}

<#
.SYNOPSIS
Remove a trusted CA certificate that Secrets Broker can use to validate
communication with the Safeguard A2A API.
 
.DESCRIPTION
Secrets Broker uses a TLS client certificate to communicate with the Safeguard
A2A API. In order to determine whether Secrets Broker trusts the TLS server
certificate presented by Safeguard A2A API, it checks against this list of
trusted CA certificates. This cmdlet removes the specified trusted CA
certificate from the list.
 
.PARAMETER Thumbprint
The thumbprint of a single trusted CA certificate to remove.
 
.EXAMPLE
Remove-SgDevOpsClientCertificate DBABE5E597BA4B9256A4CD91B8884058B718D936
#>

function Remove-SgDevOpsTrustedCertificate
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$Thumbprint
    )

    if (-not $PSBoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
    if (-not $PSBoundParameters.ContainsKey("Verbose")) { $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") }

    # no support for remove all in PowerShell

    Invoke-SgDevOpsMethod DELETE "Safeguard/TrustedCertificates/$Thumbprint"
}