NTS.Tools.MSADCS.psm1

function Start-Certutil {
    <#
        .Description
        A Function to start certuil via parameters
 
        .Parameter Parameters
        string for the certutil parameters, these must be separated with " "
 
        .Example
        Start-Certutil -Parameters "-view -out SerialNumber"
 
        .NOTES
        this function is just a wrapper for cerutil
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Parameters
    )

    $SuccessPattern = "command completed successfully"

    try {
        Write-Verbose "executing certutil as 'certutil.exe $($Parameters)'"
        $CertutilMessage = & certutil.exe $Parameters.Split(" ")
    
        if ($null -eq ($CertutilMessage | Select-String -Pattern $SuccessPattern)) {
            throw $CertutilMessage
        }
        else {
            return $CertutilMessage
        }
    }
    catch {
        throw $PSItem.Exception.Message
    }
}

function Set-CAConfig {
    <#
        .Description
        This function kann be used to set some settings on the certification authority by using certutil.
 
        .Parameter CAType
        Type of CA
 
        .Parameter Domain_DistinguishedName
        Distinguished name of domain
 
        .Parameter WebServer_URL
        FQDN of web server that will host the crl
 
        .Parameter ValidityPeriodUnits
        units of validity period for certificate
 
        .Parameter ValidityPeriod
        type of validity period for certificate
 
        .Parameter CRLPeriod
        type of validity period for crl
 
        .Parameter CRLPeriodUnits
        units of validity period for crl
 
        .Parameter CRLDeltaPeriod
        type of validity period for delta crl
 
        .Parameter CRLDeltaPeriodUnits
        units of validity period for delta crl
 
        .Example
        Set-CAConfig -CAType "RootCA" `
            -Domain_DistinguishedName $Domain_DistinguishedName `
            -WebServer_URL $WebServer_URL `
            -ValidityPeriodUnits $RootCA_RenewalValidityPeriodUnits `
            -ValidityPeriod $RootCA_RenewalValidityPeriod `
            -CRLPeriod $RootCA_CRLPeriod `
            -CRLPeriodUnits $RootCA_CRLPeriodUnits `
            -CRLDeltaPeriod $RootCA_CRLDeltaPeriod `
            -CRLDeltaPeriodUnits $RootCA_CRLDeltaPeriodUnits
 
        .NOTES
        this function is just a wrapper for cerutil
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateSet("RootCA", "SubCA")]
        [string]
        $CAType,

        [Parameter(Mandatory = $true)]
        [string]
        $Domain_DistinguishedName,

        [Parameter(Mandatory = $true)]
        [string]
        $WebServer_URL,

        [Parameter(Mandatory = $false)]
        [string]
        $ValidityPeriodUnits,

        [Parameter(Mandatory = $false)]
        [string]
        $ValidityPeriod,

        [Parameter(Mandatory = $false)]
        [string]
        $CRLPeriod,

        [Parameter(Mandatory = $false)]
        [string]
        $CRLPeriodUnits,

        [Parameter(Mandatory = $false)]
        [string]
        $CRLDeltaPeriod,

        [Parameter(Mandatory = $false)]
        [string]
        $CRLDeltaPeriodUnits
    )

    if ($CAType -eq "RootCA") {
        if ($ValidityPeriodUnits -eq "" -or $null -eq $ValidityPeriodUnits) {
            $ValidityPeriodUnits = "10"
        }
        if ($ValidityPeriod -eq "" -or $null -eq $ValidityPeriod) {
            $ValidityPeriod = "years"
        }
        if ($CRLPeriod -eq "" -or $null -eq $CRLPeriod) {
            $CRLPeriod = "weeks"
        }
        if ($CRLPeriodUnits -eq "" -or $null -eq $CRLPeriodUnits) {
            $CRLPeriodUnits = "26"
        }
        if ($CRLDeltaPeriod -eq "" -or $null -eq $CRLDeltaPeriod) {
            $CRLDeltaPeriod = "hours"
        }
        if ($CRLDeltaPeriodUnits -eq "" -or $null -eq $CRLDeltaPeriodUnits) {
            $CRLDeltaPeriodUnits = "0"
        }
    }
    elseif ($CAType -eq "SubCA") {
        if ($ValidityPeriodUnits -eq "" -or $null -eq $ValidityPeriodUnits) {
            $ValidityPeriodUnits = "5"
        }
        if ($ValidityPeriod -eq "" -or $null -eq $ValidityPeriod) {
            $ValidityPeriod = "years"
        }
        if ($CRLPeriod -eq "" -or $null -eq $CRLPeriod) {
            $CRLPeriod = "days"
        }
        if ($CRLPeriodUnits -eq "" -or $null -eq $CRLPeriodUnits) {
            $CRLPeriodUnits = "3"
        }
        if ($CRLDeltaPeriod -eq "" -or $null -eq $CRLDeltaPeriod) {
            $CRLDeltaPeriod = "days"
        }
        if ($CRLDeltaPeriodUnits -eq "" -or $null -eq $CRLDeltaPeriodUnits) {
            $CRLDeltaPeriodUnits = "0"
        }
    }
    
    try {
        Write-Output "configuring ca setting"
        Start-Certutil -Parameters "-setreg CA\DSConfigDN `"CN=Configuration,$($Domain_DistinguishedName)`""
        Start-Certutil -Parameters "-setreg CA\ValidityPeriodUnits $($ValidityPeriodUnits)"
        Start-Certutil -Parameters "-setreg CA\ValidityPeriod $($ValidityPeriod)"
        Start-Certutil -Parameters "-setreg CA\CRLPeriodUnits $($CRLPeriod)"
        Start-Certutil -Parameters "-setreg CA\CRLPeriod $($CRLPeriodUnits)"
        Start-Certutil -Parameters "-setreg CA\CRLDeltaPeriodUnits $($CRLDeltaPeriodUnits)"
        Start-Certutil -Parameters "-setreg CA\CRLDeltaPeriod $($CRLDeltaPeriod)"
        Start-Certutil -Parameters "-setreg CA\CRLPublicationURLs `"1:$($env:windir)\system32\CertSrv\CertEnroll\%3%8%9.crl\n2:http://$($WebServer_URL)/CertEnroll/%3%8%9.crl\n10:LDAP:///CN=%3%8,CN=%3,CN=CDP,CN=Public Key Services,CN=Services,%6%10`""
        Start-Certutil -Parameters "-setreg CA\CACertPublicationURLs `"1:$($env:windir)\system32\CertSrv\CertEnroll\%3.crt\n2:http://$($WebServer_URL)/CertEnroll/%3%4.crt\n2:LDAP:///CN=%3,CN=AIA,CN=Public Key Services,CN=Services,%6%11`""
        Start-Certutil -Parameters "-setreg CA\AuditFilter 127"
        Start-Certutil -Parameters "-setreg CA\forceteletex +0x20"
        Start-Certutil -Parameters "-setreg policy\EditFlags -EDITF_ADDOLDKEYUSAGE"
        Restart-Service -Name certsvc | Out-Null
        Start-Sleep -Seconds 5
    }
    catch {
        throw "error configuring ca settings - $($PSItem.Exception.Message)"
    }
}

function Set-CAPolicyInfFile {
    <#
        .Description
        Creates a CAPolicy.inf file at $($env:SystemDrive)\Windows\ with specified values
 
        .Parameter RootCA
        use this switch to configure a rootca capolicy.inf file
 
        .Parameter SubCA
        use this switch to configure a subca capolicy.inf file
 
        .Parameter Renewalkeylength
        length of key for renewal
 
        .Parameter RenewalValidityPeriodUnits
        unit of key for renewal
 
        .Parameter RenewalValidityPeriod
        type of key for renewal
 
        .Parameter CRLPeriod
        type of validity period for crl
 
        .Parameter CRLPeriodUnits
        units of validity period for crl
 
        .Parameter CRLDeltaPeriod
        type of validity period for delta crl
 
        .Parameter CRLDeltaPeriodUnits
        units of validity period for delta crl
 
        .Parameter DiscreteSignatureAlgorithm
        DiscreteSignatureAlgorithm
 
        .Example
        Set-CAPolicyInfFile -RootCA `
            -Renewalkeylength $RootCA_Key_Length `
            -RenewalValidityPeriod $RootCA_Key_ValidityPeriod `
            -RenewalValidityPeriodUnits $RootCA_Key_ValidityPeriodUnits `
            -CRLPeriod $RootCA_CRL_Period `
            -CRLPeriodUnits $RootCA_CRL_PeriodUnits `
            -CRLDeltaPeriod $RootCA_CRLDelta_Period `
            -CRLDeltaPeriodUnits $RootCA_CRLDelta_PeriodUnits `
            -DiscreteSignatureAlgorithm $RootCA_DiscreteSignatureAlgorithm
 
        .NOTES
        this used some predefined values
    #>


    param (
        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [switch]
        $RootCA,

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

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [Parameter(ParameterSetName = 'SubCA', Mandatory = $false)]
        [string]
        $Renewalkeylength,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [Parameter(ParameterSetName = 'SubCA', Mandatory = $false)]
        [string]
        $RenewalValidityPeriodUnits,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [Parameter(ParameterSetName = 'SubCA', Mandatory = $false)]
        [string]
        $RenewalValidityPeriod,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [string]
        $CRLPeriod,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [string]
        $CRLPeriodUnits,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [string]
        $CRLDeltaPeriod,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [string]
        $CRLDeltaPeriodUnits,

        [Parameter(ParameterSetName = 'RootCA', Mandatory = $false)]
        [string]
        $DiscreteSignatureAlgorithm
    )

    $ErrorActionPreference = 'Stop'
    
    # define default values
    if ($RootCA) {
        if ($Renewalkeylength -eq "") {
            $Renewalkeylength = "4096"
        }
        if ($RenewalValidityPeriod -eq "") {
            $RenewalValidityPeriod = "years"
        }
        if ($RenewalValidityPeriodUnits -eq "") {
            $RenewalValidityPeriodUnits = "10"
        }
        if ($CRLPeriod -eq "") {
            $CRLPeriod = "weeks"
        }
        if ($CRLPeriodUnits -eq "") {
            $CRLPeriodUnits = "26"
        }
        if ($CRLDeltaPeriodUnits -eq "") {
            $CRLDeltaPeriodUnits = "0"
        }
        if ($DiscreteSignatureAlgorithm -eq "") {
            $DiscreteSignatureAlgorithm = "1"
        }
        $CAPolicy_Value = "[Version]
            Signature= `"`$Windows NT$`"
     
            [certsrv_server]
            renewalkeylength = $($renewalkeylength)
            RenewalValidityPeriodUnits = $($RenewalValidityPeriodUnits)
            RenewalValidityPeriod = $($RenewalValidityPeriod)
            CRLPeriod = $($CRLPeriod)
            CRLPeriodUnits = $($CRLPeriodUnits)
            CRLDeltaPeriod = $($CRLDeltaPeriod)
            CRLDeltaPeriodUnits = $($CRLDeltaPeriodUnits)
            DiscreteSignatureAlgorithm = $($DiscreteSignatureAlgorithm)
     
            [CRLDistributionPoint]
            Empty = true
     
            [AuthorityInformationAccess]
            Empty = true
     
            [Extensions]
            2.5.29.15 = AwIBBg==
            Critical = 2.5.29.15
        "

    }
    elseif ($SubCA) {
        if ($Renewalkeylength -eq "") {
            $Renewalkeylength = "4096"
        }
        if ($RenewalValidityPeriod -eq "") {
            $RenewalValidityPeriod = "years"
        }
        if ($RenewalValidityPeriodUnits -eq "") {
            $RenewalValidityPeriodUnits = "5"
        }
        $CAPolicy_Value = "[Version]
            Signature = `"`$Windows NT`$`"
 
            [Extensions]
            2.5.29.15 = AwIBBg==
            Critical = 2.5.29.15
 
            [certsrv_server]
            RenewalKeyLength = $($Renewalkeylength)
            RenewalValidityPeriodUnits = $($RenewalValidityPeriodUnits)
            RenewalValidityPeriod = $($RenewalValidityPeriod)
            LoadDefaultTemplates=0
 
            [PolicyStatementExtension]
            Policies = AllIssuancePolicy
 
            [AllIssuancePolicy]
            OID = 2.5.29.32.0
        "

    }
    else {
        throw "no ca type was selected"
    }

    try {
        $CAPolicy_Path = "$($env:SystemDrive)\Windows\CAPolicy.inf"

        Write-Output "creating $($CAPolicy_Path)"
        New-ItemIfNotExists -Path $CAPolicy_Path -ItemType File
        Set-Content -Path $CAPolicy_Path -Value $CAPolicy_Value
    }
    catch {
        throw "error creating $($CAPolicy_Path) - $($psitem.Exception.Message)"
    }
}

function New-CACRL {
    <#
        .Description
        runs certutil -CRL
 
        .Example
        New-CACRL
 
        .NOTES
 
    #>


    #region crl
    try {
        Write-Output "creating crl"
        Start-Certutil -Parameters "-CRL"
    }
    catch {
        throw "error creating crl - $($PSItem.Exception.Message)"
    }
    #endregion
}

function Install-CACert {
    <#
        .Description
        installs the certificate of a sub ca to activate it
 
        .Parameter CerFilePath
        path to certificate file to install for ca
 
        .Example
        Install-CACert -CerFilePath $SubCA_CerFilePath
 
        .NOTES
        should only be run on an enterprise subca
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $CerFilePath
    )

    $ServiceName = "CertSvc"    
    try {
        Write-Output "installing ca cert to activate ca"
        Start-Process -FilePath GPUpdate -ArgumentList "/force" -Wait -WindowStyle Hidden
        Start-Certutil -Parameters "-installCert $($CerFilePath)"
        Start-Service -Name $ServiceName | Out-Null
        Restart-Service -Name $ServiceName | Out-Null
    }
    catch {
        throw $PSItem.Exception.Message
    }
}

function Publish-CACRLToAD {
    <#
        .Description
        runs certutil -dspublish -f $CRLPath
 
        .Parameter CRLPath
        path to crl file
 
        .Example
        Publish-CACRLToAD -CRLPath $BaseCRLPath
 
        .NOTES
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $CRLPath
    )

    try {
        if (Test-Path -Path $CRLPath) {
            Start-Certutil -Parameters "-dspublish -f $($CRLPath)"
        }
        else {
            throw "cannot find $($CRLPath)"
        }
    }
    catch {
        throw "error publishing crl - $($PSItem.Exception.Message)"
    }
}

function Enable-ScheduledTaskForCRLCreation {
    <#
        .Description
        Creates a script and a scheduled task to automate crl creation
 
        .Parameter SMBSharePath
        smb share path
 
        .Parameter CACommonName
        CA Name
 
        .Parameter CAServerName
        CA Server Name
 
        .Parameter ScriptName
        Name of the script
 
        .Parameter ScriptFolder
        path to the script, it will be created there
 
        .Example
        Enable-ScheduledTaskForCRLCreation -SMBSharePath "\\$($WEB_Obj.Name).$($env:USERDNSDOMAIN)\CertEnroll" `
            -CACommonName $SubCA_Initial_CACommonName `
            -CAServerName $SubCA_Obj.Name
 
        .NOTES
        Scheduled Task Properties
        $Trigger = New-JobTrigger -Daily -At "4:00 AM" -DaysInterval 1
        $User = "NT AUTHORITY\SYSTEM"
        $Action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument "-NoProfile -NoLogo -NonInteractive -ExecutionPolicy Bypass -File $($Script_FilePath)"
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $SMBSharePath, 

        [Parameter(Mandatory = $true)]
        [string]
        $CACommonName, 

        [Parameter(Mandatory = $true)]
        [string]
        $CAServerName,
        
        [Parameter(Mandatory = $false)]
        [string]
        $ScriptName = "Publish-DailyCRL",

        [Parameter(Mandatory = $false)]
        [string]
        $ScriptFolder = "$($env:ProgramData)\NTS\CertSrv"
    )


    try {
        $Script_FilePath = "$($ScriptFolder)\$($ScriptName).ps1"                
        $ScriptContent = "Start-Process -FilePath certutil -ArgumentList `"-CRL`" -Wait -WindowStyle Hidden
        Copy-Item -Path `"$($env:windir)\system32\certsrv\certenroll\*`" -Destination `"$($SMBSharePath)`" -Force
        `$Path = `"$($SMBSharePath)\$($CAServerName).$($env:USERDNSDOMAIN)_$($CACommonName).crt`"
        if(Test-Path -Path `$Path) {
            if(Test-Path -Path `"$($SMBSharePath)\$($CACommonName).crt`") {
                Remove-Item -Path `"$($SMBSharePath)\$($CACommonName).crt`" -Force
            }
            Rename-Item -Path `$Path -NewName `"$($CACommonName).crt`"
        }
        "

        
        Write-Output "creating scheduled task to publish crl on a daily basis"
                                        
        try {
            New-ItemIfNotExists -Path $ScriptFolder -ItemType Directory
            New-Item -Path $Script_FilePath -ItemType File -Force | Out-Null
            Set-Content -Path $Script_FilePath -Value $ScriptContent -Force
        }
        catch {
            throw "error creating files: $($PSItem.Exception.Message)"
        }

        try {
            $Trigger = New-JobTrigger -Daily -At "4:00 AM" -DaysInterval 1
            $User = "NT AUTHORITY\SYSTEM"
            $Action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument "-NoProfile -NoLogo -NonInteractive -ExecutionPolicy Bypass -File $($Script_FilePath)"
            Register-ScheduledTask -TaskName $ScriptName -Trigger $Trigger -User $User -Action $Action -RunLevel Highest -Force | Out-Null
        }
        catch {
            throw "error creating task: $($PSItem.Exception.Message)"
        }
        Write-Output "starting task"
        Start-ScheduledTask -TaskName $ScriptName
    }
    catch {
        throw "error creating task - $($PSItem.Exception.Message)"
    }
    #endregion
}

function Complete-CertRequest {
    <#
        .Description
        this function can be used to sign a certificate request using the local ca
 
        .Parameter CertReqFilePath
        path to the certificate request
 
        .Parameter CAName
        name of the local ca
 
        .Example
        Complete-CertRequest -CertReqFilePath $CertReqFilePath -CAName $CAName
 
        .NOTES
         
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $CertReqFilePath,

        [Parameter(Mandatory = $true)]
        [string]
        $CAName
    )
    
    Write-Output "trying to sign the subca cert req"
    if (Test-Path -Path $CertReqFilePath) {
        $SubCA_CerFilePath = $CertReqFilePath.Replace(".req", ".cer")

        # load req into cert authority
        try {
            Write-Output "loading subca request into ca"
            $ImportResult = certreq -submit -q -config - "$($CertReqFilePath)" "$($SubCA_CerFilePath)"
            if ($ImportResult -like "*ERROR_FILE_EXISTS*" ) {
                Remove-Item -Path "$($CertReqFilePath.Replace(".req",".rsp"))" -Force
            }
            $ReqID = ($ImportResult -like "*RequestId: *")[0].Replace("RequestId: ", "")
        }
        catch {
            throw "error importing ca req file - $($PSItem.Exception.Message)"
        }

        # sign req
        Write-Output "submitting subca request"
        $SubmitResult = Start-Certutil -Parameters "-config $($CAName) -resubmit $($ReqID)"
        if ($SubmitResult[-1] -notlike "*command completed successfully*") {
            throw "submit was not successful:`n$($SubmitResult)"
        }
        
        # export cer to file
        Write-Output "exporting signed subca cert"
        $ExportResult = Start-Certutil -Parameters "-config $($CAName) -view -restrict requestid=$($ReqID) -out rawcertificate"
        if ($ExportResult[-1] -like "*command completed successfully*") {
            $FirstFilterString = "-----BEGIN CERTIFICATE-----"
            $SecondFilterString = "-----END CERTIFICATE-----"
            $RegexPattern = "$($FirstFilterString)(.*?)$($SecondFilterString)"
            
            #Perform the opperation
            $CertFileValue = $firstString + "`n" + ([regex]::Match($ExportResult, $RegexPattern).Groups[1].Value) + "`n" + $secondString
            
            New-Item -Path $SubCA_CerFilePath -ItemType File -Force | Out-Null
            Set-Content -Path $SubCA_CerFilePath -Value $CertFileValue | Out-Null
        }
        else {
            throw "file could not be created:`n$($ExportResult)"
        }
    }
    else {
        throw "cert req file could not be found at $($CertReqFilePath)"
    }
}