Functions/Public/Start-GenerateRootCert.ps1

Function Start-GenerateRootCert {
    [CmdletBinding(DefaultParameterSetName = 'gui')]
    param (
        # Cert Key Password
        [Parameter(HelpMessage = 'The root certificate key password', ParameterSetName = 'cli')]
        [string]
        $certKeyPassword,
        # Parameter to "New" or "Replace" the root certificate validateSet ('1', '2', '3', 'E')
        [Parameter(HelpMessage = 'Select an option to generate or replace the root certificate', ParameterSetName = 'cli')]
        [ValidateSet('New', 'Replace', 'Renew')]
        [string] $generateType = 'New',
        # Force invoke commands after generation
        [Parameter(HelpMessage = 'When specified, this parameter will replace certificates if they already exist on the current filesystem', ParameterSetName = 'cli')]
        [switch]
        $force


    )

    if ($Global:JCRSettings.sessionImport -eq $false) {
        Get-JCRGlobalVars
        $Global:JCRSettings.sessionImport = $true
    }


    # this script will generate a Self Signed CA (root cert) to be imported on the
    # Radius CBA-BYO Authentication UI

    ################################################################################
    # Do Not Edit Below:
    ################################################################################
    # Set-Location $JCRScriptRoot

    # REM Generate Root Server Private Key and server certificate (self signed as CA)
    Write-Host "Generating Self Signed Root CA Certificate"
    if (Test-Path -Path "$($global:JCRConfig.radiusDirectory.value)/Cert") {
        Write-Host "Cert Path Exists"
    } else {
        Write-Host "Creating Cert Path"
        New-Item -ItemType Directory -Path "$($global:JCRConfig.radiusDirectory.value)/Cert"
    }

    # If cert backup folder does not exist, create it
    if (Test-Path -Path "$($global:JCRConfig.radiusDirectory.value)/Cert/Backups") {
        Write-Host "Backup Path Exists"
    } else {
        Write-Host "Creating Backup Path"
        New-Item -ItemType Directory -Path "$($global:JCRConfig.radiusDirectory.value)/Cert/Backups"
    }

    $CertPath = Resolve-Path "$($global:JCRConfig.radiusDirectory.value)/Cert"
    $outKey = "$CertPath/radius_ca_key.pem"
    $outCA = "$CertPath/radius_ca_cert.pem"
    $timestamp = Get-Date -Format "yyyyMMddHHmmss"

    # If parameterSetName is CLI
    if ($PSCmdlet.ParameterSetName -eq 'cli') {
        switch ($GenerateType) {
            'New' {
                $selection = 1
            }
            'Replace' {
                # Same functionality as 'New'
                $selection = 1
            }
            'Renew' {
                $selection = 2
            }
        }
    } else {
        Show-RootCAGenerationMenu
        $selection = Read-Host "Please make a selection"
    }



    switch ($selection) {
        # Generate new root certificate
        '1' {
            if (Test-Path -Path "$($global:JCRConfig.radiusDirectory.value)/Cert/radius_ca_cert.pem") {
                Write-Host "Root Cert already exists"
                # If the force switch is set, force the generation of a new root certificate
                if (!$force) {
                    if ($PSCmdlet.ParameterSetName -eq 'cli') {
                        $overwritePrompt = Get-ResponsePrompt -message "Do you want to overwrite the existing CA Cert? This will generate a new root CA with a new serial number and user certs generated with the previous CA will no longer authenticate." -cli $true
                    } else {
                        $overwritePrompt = Get-ResponsePrompt -message "Do you want to overwrite the existing CA Cert? This will generate a new root CA with a new serial number and user certs generated with the previous CA will no longer authenticate."
                    }
                } else {
                    $overwritePrompt = $true
                }

                switch ($overwritePrompt) {
                    $true {
                        Write-Host "Overwriting Root Cert..." -ForegroundColor Yellow

                        switch ($PSCmdlet.ParameterSetName) {
                            'gui' {
                                $env:certKeyPassword = ""
                                # Loop until the passwords match
                                do {
                                    # Prompt for password
                                    Write-Host "NOTE: Please save your root certificate password in a password manager" -foregroundcolor Yellow
                                    $secureCertKeyPass = Read-Host -Prompt "Enter a password for the certificate key" -AsSecureString

                                    # Reprompt for password
                                    $secureCertKeyPass2ReEntry = Read-Host -Prompt "Re-enter the password for the certificate key" -AsSecureString

                                    # Convert SecureString to plain text to validate
                                    $plainCertKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                                    $plainCertKeyPassReEntry = ConvertFrom-SecureString $secureCertKeyPass2ReEntry -AsPlainText

                                    # Validate that the passwords match
                                    if ($plainCertKeyPass -ne $plainCertKeyPassReEntry) {
                                        Write-Host "Passwords do not match. Please try again." -foregroundcolor Red
                                    } else {
                                        Write-Host "Password set successfully" -foregroundcolor Green
                                        $certKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                                    }
                                } while ($plainCertKeyPass -ne $plainCertKeyPassReEntry)
                            }
                            'cli' {
                                $certKeyPass = $certKeyPassword
                            }
                        }

                        # Copy the current root cert to the backups folder and zip it
                        try {
                            Copy-Item -Path "$CertPath/radius_ca_cert.pem" -Destination "$CertPath/Backups/radius_ca_cert_$timestamp.pem"
                            Copy-Item -Path "$CertPath/radius_ca_key.pem" -Destination "$CertPath/Backups/radius_ca_key_$timestamp.pem"

                            # Zip the root cert and key files
                            $zipPath = "$CertPath/Backups/newOverwrite_radius_ca_cert_backup_$timestamp.zip"
                            Compress-Archive -Path "$CertPath/Backups/radius_ca_cert_$timestamp.pem", "$CertPath/Backups/radius_ca_key_$timestamp.pem" -DestinationPath $zipPath

                            Remove-Item -Path "$CertPath/Backups/radius_ca_cert_$timestamp.pem"
                            Remove-Item -Path "$CertPath/Backups/radius_ca_key_$timestamp.pem"

                        } catch {
                            Write-Error "Error backing up the current root cert and key. $($_.Exception.Message)"
                            exit
                        }
                        # Save the pass phrase in the env:
                        $env:certKeyPassword = $certKeyPass
                        Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) req -x509 -newkey rsa:2048 -days $($global:JCRConfig.caCertValidityDays.value) -keyout `"$outKey`" -out `"$outCA`" -passout pass:$($env:certKeyPassword) -subj /C=$($($global:JCRConfig.certSubjectHeader.Value.CountryCode))/ST=$($($global:JCRConfig.certSubjectHeader.Value.StateCode))/L=$($($global:JCRConfig.certSubjectHeader.Value.Locality))/O=$($($global:JCRConfig.certSubjectHeader.Value.Organization))/OU=$($($global:JCRConfig.certSubjectHeader.Value.OrganizationUnit))/CN=$($($global:JCRConfig.certSubjectHeader.Value.CommonName))"
                        # REM PEM pass phrase: myorgpass
                        Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -in `"$outCA`" -noout -text"
                        # openssl x509 -in ca-cert.pem -noout -text
                        # Update Extensions Distinguished Names:
                        if (-not (Test-JCRExtensionFile)) {
                            Write-Host "Extensions file is not valid, updating it now..."
                            Set-JCRExtensionFile
                        } else {
                            Write-Host "Extensions file is valid, no need to update"
                        }
                    }
                    $false {
                        return
                    }
                    'exit' {
                        return
                    }
                }

            } else {
                # Generate new root certificate
                Write-Host "Generating new CA Cert..."

                switch ($PSCmdlet.ParameterSetName) {
                    'gui' {
                        $env:certKeyPassword = ""
                        # Loop until the passwords match
                        do {
                            # Prompt for password
                            Write-Host "NOTE: Please save your root certificate password in a password manager" -foregroundcolor Yellow
                            $secureCertKeyPass = Read-Host -Prompt "Enter a password for the certificate key" -AsSecureString

                            # Reprompt for password
                            $secureCertKeyPass2ReEntry = Read-Host -Prompt "Re-enter the password for the certificate key" -AsSecureString

                            # Convert SecureString to plain text to validate
                            $plainCertKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                            $plainCertKeyPassReEntry = ConvertFrom-SecureString $secureCertKeyPass2ReEntry -AsPlainText

                            # Validate that the passwords match
                            if ($plainCertKeyPass -ne $plainCertKeyPassReEntry) {
                                Write-Host "Passwords do not match. Please try again." -foregroundcolor Red
                            } else {
                                Write-Host "Password set successfully" -foregroundcolor Green
                                $certKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                            }
                        } while ($plainCertKeyPass -ne $plainCertKeyPassReEntry)
                    }
                    'cli' {
                        $certKeyPass = $certKeyPassword
                    }
                }
                # Save the pass phrase in the env:
                $env:certKeyPassword = $certKeyPass
                Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) req -x509 -newkey rsa:2048 -days $($global:JCRConfig.caCertValidityDays.value) -keyout `"$outKey`" -out `"$outCA`" -passout pass:$($env:certKeyPassword) -subj /C=$($($global:JCRConfig.certSubjectHeader.Value.CountryCode))/ST=$($($global:JCRConfig.certSubjectHeader.Value.StateCode))/L=$($($global:JCRConfig.certSubjectHeader.Value.Locality))/O=$($($global:JCRConfig.certSubjectHeader.Value.Organization))/OU=$($($global:JCRConfig.certSubjectHeader.Value.OrganizationUnit))/CN=$($($global:JCRConfig.certSubjectHeader.Value.CommonName))"
                # REM PEM pass phrase: myorgpass
                Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -in `"$outCA`" -noout -text"
                # openssl x509 -in ca-cert.pem -noout -text
                # Update Extensions Distinguished Names:
                if (-not (Test-JCRExtensionFile)) {
                    Write-Host "Extensions file is not valid, updating it now..."
                    Set-JCRExtensionFile
                } else {
                    Write-Host "Extensions file is valid, no need to update"
                }

                return
            }
        }
        # Renew current root certificate
        '2' {
            $env:certKeyPassword = $certKeyPass
            # Check if there is a current CA cert
            if (Test-Path -Path "$($global:JCRConfig.radiusDirectory.value)/Cert/radius_ca_cert.pem") {

                if (!$force) {
                    if ($PSCmdlet.ParameterSetName -eq 'cli') {
                        $replacePrompt = Get-ResponsePrompt -message "Do you want to renew the existing CA Cert? renewing the root CA will contain the same serial number and CA subject headers. User certs generated with the previous CA will continue to authenticate." -cli $true
                    } else {
                        $replacePrompt = Get-ResponsePrompt -message "Do you want to renew the existing CA Cert? renewing the root CA will contain the same serial number and CA subject headers. User certs generated with the previous CA will continue to authenticate."
                    }
                } else {
                    $replacePrompt = $true
                }

                switch ($replacePrompt) {
                    $true {
                        Write-Host "Renewing Root Cert..." -ForegroundColor Yellow
                        switch ($PSCmdlet.ParameterSetName) {
                            'gui' {
                                $env:certKeyPassword = ""
                                # Loop until the passwords match
                                $secureCertKeyPass = Read-Host -Prompt "Enter the current password for the certificate key" -AsSecureString
                                $certKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                                do {
                                    $result = Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) rsa -in `"$outKey`" -passin pass:$($certKeyPass) -check"
                                    if ($result -match "RSA key ok") {
                                        Write-Host "Password validated! Proceeding..."
                                        $env:certKeyPassword = $certKeyPass
                                        $passwordValid = $true
                                    } else {
                                        Write-Host "Incorrect private key password!" -ForegroundColor Red
                                        $passwordValid = $false  # Continue loop if password is incorrect
                                        $secureCertKeyPass = Read-Host "Please enter a valid password for the current private key" -AsSecureString
                                        $certKeyPass = ConvertFrom-SecureString $secureCertKeyPass -AsPlainText
                                    }
                                } while (-not $passwordValid)  # Continue looping until the password is correct
                            }
                            'cli' {
                                $certKeyPass = $certKeyPassword
                                # Validate that the password works with the old CA cert
                                # Attempt to read the private key with the password
                                do {
                                    $result = Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) rsa -in `"$outKey`" -passin pass:$($certKeyPass) -check"

                                    if ($result -match "RSA key ok") {
                                        Write-Host "Password validated! Proceeding..."
                                        $env:certKeyPassword = $certKeyPass
                                        $passwordValid = $true  # Exit condition for the loop
                                    } else {
                                        Write-Host "Incorrect password" -ForegroundColor Red
                                        $passwordValid = $false  # Continue loop if password is incorrect
                                        $certKeyPass = Read-Host "Please enter a valid password for the current private key" -AsSecureString
                                        $certKeyPass = ConvertFrom-SecureString $certKeyPass -AsPlainText
                                    }
                                } while (-not $passwordValid)  # Continue looping until the password is correct
                            }
                        }

                        # Copy the current root cert to the backups folder and zip it
                        try {
                            Copy-Item -Path "$CertPath/radius_ca_cert.pem" -Destination "$CertPath/Backups/radius_ca_cert_$timestamp.pem"
                            Copy-Item -Path "$CertPath/radius_ca_key.pem" -Destination "$CertPath/Backups/radius_ca_key_$timestamp.pem"

                            # Zip the root cert and key files
                            $zipPath = "$CertPath/Backups/renew_radius_ca_cert_backup_$timestamp.zip"
                            Compress-Archive -Path "$CertPath/Backups/radius_ca_cert_$timestamp.pem", "$CertPath/Backups/radius_ca_key_$timestamp.pem" -DestinationPath $zipPath

                            Remove-Item -Path "$CertPath/Backups/radius_ca_cert_$timestamp.pem"
                            Remove-Item -Path "$CertPath/Backups/radius_ca_key_$timestamp.pem"

                        } catch {
                            Write-Error "Error backing up the current root cert and key. $($_.Exception.Message)"
                            exit
                        }
                        $certConfPath = "$CertPath/radius_ca_cert2.conf"
                        $csrOutPath = "$CertPath/radius_ca_cert.csr"
                        $select = Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -in `"$($outCA)`" -serial -noout"
                        $serial = $select -replace "serial=", ""

                        # Validate that the password works with the old CA cert
                        # Attempt to read the private key with the password

                        try {
                            Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -x509toreq -in $($outCA) -signkey $($outKey) -out $csrOutPath -passin pass:$($env:certKeyPassword)"

                        } catch {
                            # Exit
                            Write-Error "Error creating CSR file $($_.Exception.Message)"
                            exit
                        }

                        $string = @"
[ v3_ca ]
basicConstraints= CA:TRUE
subjectKeyIdentifier= hash
authorityKeyIdentifier= keyid:always,issuer:always
"@
 | Out-File "$certConfPath"

                        try {
                            Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) req -x509 -newkey rsa:2048 -days $($global:JCRConfig.caCertValidityDays.value) -keyout `"$outKey`" -out `"$outCA`" -passout pass:$($env:certKeyPassword) -subj /C=$($($global:JCRConfig.certSubjectHeader.Value.CountryCode))/ST=$($($global:JCRConfig.certSubjectHeader.Value.StateCode))/L=$($($global:JCRConfig.certSubjectHeader.Value.Locality))/O=$($($global:JCRConfig.certSubjectHeader.Value.Organization))/OU=$($($global:JCRConfig.certSubjectHeader.Value.OrganizationUnit))/CN=$($($global:JCRConfig.certSubjectHeader.Value.CommonName))"

                            Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -req -days $($global:JCRConfig.caCertValidityDays.value) -in $csrOutPath -set_serial `"0x$serial`" -signkey `"$outKey`" -out `"$outCA`" -extfile $certConfPath -extensions v3_ca -passin pass:$($env:certKeyPassword)"

                            # Message of success
                            Write-Host "Root Certificate Renewed Successfully!" -ForegroundColor Yellow
                            # Cleanup
                            Remove-Item -Path $certConfPath
                            Remove-Item -Path $csrOutPath
                        } catch {
                            # Error replacing the certificate
                            Write-Error "Error replacing the certificate. $($_.Exception.Message)"
                        }
                        Invoke-Expression "$($global:JCRConfig.openSSLBinary.value) x509 -in `"$outCA`" -enddate -serial -noout "
                    }
                    $false {
                        return
                    }
                    'exit' {
                        return
                    }
                }
            } else {
                Write-Host "No Root Cert detected. Please generate a new CA Cert. Returning to main menu..." -ForegroundColor Yellow
                return
            }
        }
        # Return to main menu
        'E' {
            return
        }
    }
}