NTS.Tools.MSADCS.psm1

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
        # configures the local ca with specified values
        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 "$($env:COMPUTERNAME): configuring ca setting"
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\DSConfigDN `"CN=Configuration,$($Domain_DistinguishedName)`"" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\ValidityPeriodUnits $($ValidityPeriodUnits)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\ValidityPeriod $($ValidityPeriod)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\CRLPeriodUnits $($CRLPeriod)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\CRLPeriod $($CRLPeriodUnits)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\CRLDeltaPeriodUnits $($CRLDeltaPeriodUnits)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\CRLDeltaPeriod $($CRLDeltaPeriod)" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-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`"" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-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`"" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\AuditFilter 127" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg CA\forceteletex +0x20" -NoNewWindow -Wait
        Start-Process -FilePath "certutil" -ArgumentList "-setreg policy\EditFlags -EDITF_ADDOLDKEYUSAGE" -NoNewWindow -Wait
        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
        # creates a capolicy.inf for a root ca
        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 "$($env:COMPUTERNAME): creating $($CAPolicy_Path)"
        if ((Test-Path -Path $CAPolicy_Path) -eq $false) {
            New-Item -Path $CAPolicy_Path -ItemType File -Force | Out-Null
        }
        
        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
        # creates a new crl
        New-CACRL
 
        .NOTES
 
    #>


    #region crl
    try {
        Write-Output "$($env:COMPUTERNAME): creating crl"
        $CertutilMessage = certutil -CRL
        if ($CertutilMessage -notmatch "-CRL command completed successfully") {
            throw $CertutilMessage
        }
        
    }
    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
        # this will install the certificate of the local ca
        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 "$($env:COMPUTERNAME): installing ca cert to activate ca"
        Start-Process -FilePath GPUpdate -ArgumentList "/force" -Wait -WindowStyle Hidden
        $AcceptResult = certutil -installCert $CerFilePath
        if ($null -eq $AcceptResult -or $AcceptResult[0] -notlike "*CertUtil: -installCert command completed successfully*") {
            throw "subca could not be installed:`n$($AcceptResult)"
        }
        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
        # published the certificate revocation list to ad
        Publish-CACRLToAD -CRLPath $BaseCRLPath
 
        .NOTES
    #>


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

    try {
        if (Test-Path -Path $CRLPath) {
            $CertutilMessage = certutil -dspublish -f $CRLPath
            if (!(($CertutilMessage -like "*-dspublish command completed successfully*")[0] -like "*-dspublish command completed successfully*")) {
                throw $CertutilMessage
            }
        }
        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
        # creates the script and scheduled task
        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 "$($env:COMPUTERNAME): creating scheduled task to publish crl on a daily basis"
                                        
        try {
            if ((Test-Path -Path $ScriptFolder) -eq $false) {
                New-Item -Path $ScriptFolder -ItemType Directory -Force | Out-Null
            }
                            
            New-Item -Path $Script_FilePath -ItemType File -Force | Out-Null
            Set-Content -Path $Script_FilePath -Value $ScriptContent -Force
        }
        catch {
            throw "$($env:COMPUTERNAME): 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 "$($env:COMPUTERNAME): error creating task: $($PSItem.Exception.Message)"
        }
        Write-Output "$($env:COMPUTERNAME): 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
        # completes the request
        Complete-CertRequest -CertReqFilePath $CertReqFilePath -CAName $CAName
 
        .NOTES
         
    #>


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

        [Parameter(Mandatory = $true)]
        [string]
        $CAName
    )
    
    Write-Output "$($env:COMPUTERNAME): 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 "$($env:COMPUTERNAME): 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 "$($env:COMPUTERNAME): submitting subca request"
        $SubmitResult = certutil -config $CAName -resubmit $ReqID
        if ($SubmitResult[-1] -notlike "*command completed successfully*") {
            throw "$($env:COMPUTERNAME): submit was not successful:`n$($SubmitResult)"
        }
        
        # export cer to file
        Write-Output "$($env:COMPUTERNAME): exporting signed subca cert"
        $ExportResult = certutil -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 "$($env:COMPUTERNAME): file could not be created:`n$($ExportResult)"
        }
    }
    else {
        throw "$($env:COMPUTERNAME): cert req file could not be found at $($CertReqFilePath)"
    }
}