CertificateValidation/Microsoft.AzureStack.PublicCertificateRequest.psm1
function Write-AzsCertificateRequestFileInternal { <# .SYNOPSIS Internal function to call CSR using the standard Azure Stack request template. .DESCRIPTION Imports the standard Azure Stack certificate request template replaces deployment specific data such as subject and SAN. Detects if commas are used in the subject name and thus requiring a different X500NameFlag .EXAMPLE Write-AzsCertificateRequestFileInternal -subjectAltNames $completeSANs -subject $subject -KeyLength $KeyLength -HashAlgorithm $HashAlgorithm -OutputRequestPath $OutputRequestPath .INPUTS subjectAltNames - string - SubjectAlternativeNames subject - ordereddictionary - hashtable of deployment subject OutputRequestPath - string - path (parent must be valid) to where the CSRs should land. KeyLength - int - Defines the length of the public and private key HashAlgorithm - string - Hash Algorithm to be used for this request. .OUTPUTS Encoded CSR file - This is the file or content that should be presented to a CA to request certificates Clear Text INF file - this is a reference file to help debugging. Certreq log file - the output of the certreq.exe command, later streamed into overall AzsCertificateRequest.log .NOTES #> [cmdletbinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.OrderedDictionary]$subject, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$subjectAltNames, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript( {Test-Path $_ -PathType Container})] $OutputRequestPath, [Parameter(Mandatory = $false)] [ValidateSet(2048, 4096, 8192)] [int]$KeyLength = 2048, [Parameter(Mandatory = $false)] [ValidateSet('SHA256', 'SHA384', 'SHA512')] [string]$HashAlgorithm = 'SHA256' ) $thisFunction = $MyInvocation.MyCommand.Name try { #parse subject for comma and set appropropriate name flag to avoid error, and build subjectname $subjectStringArray = $subject.GetEnumerator() | ForEach-Object { if ($_.Value) {"$($_.Name)=$($_.Value)"} } if ($subjectStringArray -notcontains ',') { Write-AzsReadinessLog -Message ("Subject {0} doesnt contain comma" -f $subjectStringArray) -Type Info -Function $thisFunction $stringString = $subjectStringArray -join ',' } else { Write-AzsReadinessLog -Message ("Subject contains comma" -f $subjectStringArray) -Type Info -Function $thisFunction $stringString = $subjectStringArray -join ';' $X500NameFlags = "X500NameFlags = 0x40000000`n`r" } #Get INF Template and replace placeholders with user data $data = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.PublicCertificateRequestData.psd1 $infTemplate = $data.requestINF $requestINF = $infTemplate.Replace('[[SubjectString]]', $stringString) $requestINF = $requestINF.Replace('[[X500]]', $X500NameFlags) $requestINF = $requestINF.Replace('[[SANStrings]]', $subjectAltNames) $requestINF = $requestINF.Replace('[[KeyLength]]', $KeyLength) $requestINF = $requestINF.Replace('[[HashAlgorithm]]', $HashAlgorithm) #Create filepaths for inf, req and log as [host|wildcard].region.external.fqdn-CertRequest- $infFilePath = "{0}\{1}_CertRequest_{2}_ClearTextDoNotUse.inf" -f $OutputRequestPath, $subjectAltNames.split('&')[0].Replace('dns=', '').Replace('.', '_').Replace('*', 'wildcard'), (Get-Date -f yyyyMMddHHmmss) Write-AzsReadinessLog -Message ("Setting infFilePath to {0}" -f $infFilePath) -Type Info -Function $thisFunction $csrFilePath = $infFilePath.Replace('_ClearTextDoNotUse', '').Replace('inf', 'req') $certReqLogPath = $csrFilePath.Replace('.req', '.log') #Create and print Inf template $requestINF | Out-File $infFilePath -Force #Create encoded cert request Write-AzsReadinessLog -Message ("Setting csrFilePath to {0}" -f $csrFilePath) -Type Info -Function $thisFunction $cmd = "-new $infFilePath $csrFilePath" $null = Start-Process -FilePath certreq.exe ` -ArgumentList $cmd ` -WindowStyle Hidden ` -PassThru ` -Wait ` -RedirectStandardOutput $certReqLogPath Write-AzsReadinessLog -Message ("CSR generating for following SAN(s): {0}" -f $subjectAltNames) -Type Info -Function $thisFunction -toScreen Write-AzsReadinessLog -Message ("Present this CSR to your Certificate Authority for Certificate Generation: {0}" -f $csrFilePath) -Type Info -Function $thisFunction -toScreen } catch { Write-AzsReadinessLog -Message ("CSR generation failed with: {0}" -f $_.exception) -Type Error -Function $thisFunction } finally { # Move inf file to child directory called inf $infDest = ("{0}\{1}" -f (Split-Path $infFilePath -parent), 'Inf') if (-not (Test-Path $infDest)) {$null = New-Item -path $infDest -ItemType Directory -Force} Move-Item -Path $infFilePath -Destination $infDest -Force -ErrorAction SilentlyContinue # Move log content to parent log and clean up log file. $certReqLogContent = Get-Content $certReqLogPath -ErrorAction SilentlyContinue | ForEach-Object {if ($_) {$_}} if (-not $certReqLogContent) {$certReqLogContent = '[missing]'} Write-AzsReadinessLog -Message ("Certreq.exe output: {0}" -f ($certReqLogContent -join '. ')) -Type Info -Function $thisFunction -toScreen Remove-item $certReqLogPath -Force } } function New-AzsCertificateSigningRequest { <# .SYNOPSIS Wrapper function to build SANs and call internal certreq.exe function for multiSAN CSR or SingleSAN CSR .DESCRIPTION Imports the standard Azure Stack SANs and builds the SAN list including PaaS if neccessary Calls internal function for once for multiSAN or multiple time for singleSAN (depending on the user input) .EXAMPLE $certificateRequestParams = @{ 'regionName' = azurestack 'externalFQDN' = contoso.com 'subject' = [ordered]@{"OU"="AzureStack";"O"="Microsoft";"L"="Redmond";"ST"="Washington";"C"="US"} 'KeyLength' = 2048 'HashAlgorithm' = SHA256 'requestType' = MultipleCSR 'includePaaS' = $false 'OutputRequestPath' = '$ENV:USERPROFILE\Documents\AzsCertRequests' } Write-AzsCertificateRequestFile @certificateRequestParams Generates multiple CSRs for deployment of Azure Stack e.g. portal.azurestack.contoso.com, management.azurestack.contoso.com etc.. .OUTPUTS None - This is a wrapper function that calls an internal function to generate the encoded CSR .PARAMETER RegionName Specifies the Azure Stack deployment's region name when deploymentdata.json is not used, must be alphanumeric. .PARAMETER externalFQDN Specifies the Azure Stack deployment's External FQDN, also aliased as ExternalFQDN and FQDN, must be valid DNSHostName .PARAMETER Subject Specifies an ordered dictionary of the subject for the certificate request generation. .PARAMETER KeyLength Defines the length of the public and private key for the certificate request generation. Default is 2048. Valid values 2048, 4096, 8192 .PARAMETER HashAlgorithm Hash Algorithm to be used for the certificate request generation. Default is SHA256. Valid values SHA256, SHA384, SHA512 .PARAMETER RequestType Specifies the SAN type of the certificate request. Valid values: MultipleCSR, SingleCSR. SingleCSR generates one certificate request for all services (not recommended for production). User will be prompted to confirm use. MultipleCSR generates multiple certificate requests, one for each service (strongly recommended in production environments). .PARAMETER IncludePaaS Specifies if PaaS services/hostnames should be added to the certificate request(s) .PARAMETER OutputRequestPath Specifies the destination path for certificate request files, directory must already exist. .PARAMETER IdentitySystem Specifies the Azure Stack deployment's Identity System valid values, AAD or ADFS, for Azure Active Directory and Active Directory Federated Services respectively .PARAMETER OutputPath Specifies custom path to save Readiness JSON report and Verbose log file. .LINK Generate Azure Stack Certificate Requests - https://aka.ms/AzsCSR Azure Stack Readiness Checker Tool - https://aka.ms/AzsReadinessChecker .NOTES #> [cmdletbinding()] [Alias("New-AzsCSR", "New-AzsCertificateRequest", "Write-AzsCertificateRequestFile")] param ( [Parameter(Mandatory = $true, HelpMessage = "Enter Azure Stack Region Name")] [string]$regionName, [Parameter(Mandatory = $true, HelpMessage = "Enter Azure Stack External FQDN (without region name)")] [Alias("FQDN", "ExternalDomainName")] [ValidateScript( {[System.Uri]::CheckHostName($_) -eq 'dns' <#FQDN must be valid DNSHostName#>})] [string]$externalFQDN, [Parameter(Mandatory = $true, HelpMessage = 'Provide subject name as an ordered hashtable e.g. $subject = [ordered]@{"OU"="AzureStack";"O"="Microsoft";"L"="Redmond";"ST"="Washington";"C"="US"}')] [System.Collections.Specialized.OrderedDictionary]$Subject, [Parameter(Mandatory = $false, HelpMessage = 'Provide Key Length: 2048, 4096 or 8192')] [ValidateSet(2048, 4096, 8192)] [int]$KeyLength = 2048, [Parameter(Mandatory = $false, HelpMessage = 'Provide Hash Algorithm: SHA256, SHA384 or SHA512')] [ValidateSet('SHA256', 'SHA384', 'SHA512')] [string]$HashAlgorithm = 'SHA256', [Parameter(Mandatory = $false, HelpMessage = "Enter Certificate Request generation type ('Single' = Request individual wildcard certificates, 'Multiple' = Request single multi domain wildcard certificates")] [ValidateSet('MultipleCSR', 'SingleCSR')] [string]$requestType = 'MultipleCSR', [Parameter(Mandatory = $false, HelpMessage = "Use if you want to add PaaS domain names to the certificate request")] [switch]$includePaaS, [Parameter(Mandatory = $true, HelpMessage = "Enter Azure Stack Identity System (AAD or ADFS)")] [ValidateSet('AAD', 'ADFS')] [string]$IdentitySystem, [Parameter(Mandatory = $true, HelpMessage = "Destination Path for Certificate Request(s)")] [ValidateScript( {Test-Path -Path $_ -PathType Container <# should be a valid directory path #>})] [string]$OutputRequestPath, [Parameter(Mandatory = $false, HelpMessage = "Directory path for log and report output")] [string]$OutputPath = "$ENV:TEMP\AzsReadinessChecker" ) $thisFunction = $MyInvocation.MyCommand.Name $GLOBAL:OutputPath = $OutputPath Import-Module $PSScriptRoot\..\Microsoft.AzureStack.ReadinessChecker.Reporting.psm1 -Force Write-Header -invocation $MyInvocation -params $PSBoundParameters # Get template Data for SANs $data = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.PublicCertificateRequestData.psd1 # Generate environment specific Deployment SANs, and force region and fqdn to lowercase $deploySANs = $data.deploySANs -f $regionName.ToLower(), $externalFQDN.ToLower() if ($IdentitySystem -eq 'ADFS') { $deploySANs += $data.adfsSANs -f $regionName.ToLower(), $externalFQDN.ToLower() } Write-AzsReadinessLog -Message ("Set Deployment SANs to: {0}" -f $deploySANs) -Type Info -Function $thisFunction # If applicable, Generate environment specific Deployment SANs, and force region and fqdn to lowercase if ($includePaaS) { $paasSANs = $data.paasSANs -f $regionName.ToLower(), $externalFQDN.ToLower() Write-AzsReadinessLog -Message ("Set PaaS SANs to: {0}" -f $paasSANs) -Type Info -Function $thisFunction $completeSANs = $deploySANs + $paasSANs } else { $completeSANs = $deploySANs } Write-AzsReadinessLog -Message ("Complete SAN list is: {0}" -f $completeSANs) -Type Info -Function $thisFunction # If the request type is for multiple SANs generate one request file with all SANs, else generate individual single SAN requests Write-AzsReadinessLog -Message ("CSR RequestType is: {0}" -f $requestType) -Type Info -Function $thisFunction if ($requestType -eq 'SingleCSR') { try { #suppress warning if this is pester. if ($OutputRequestPath -eq "$ENV:TEMP\PesterCSR") { $warnaction = 'SilentlyContinue' } else { $warnaction = 'Inquire' } Write-Warning "It is not recommended to use a single certificate across all public endpoints, the scope of a potential compromise is unneccessarily large." -WarningAction $warnaction $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $completeSANs -SubjectToChange $subject Write-AzsCertificateRequestFileInternal -subjectAltNames $completeSANs -subject $subject -KeyLength $KeyLength -HashAlgorithm $HashAlgorithm -OutputRequestPath $OutputRequestPath } catch { $_.exception } } elseif ($requestType -eq 'MultipleCSR') { foreach ($individualSAN in $completeSANs.split('&')) { if ($individualSAN -match '\*.appservice.|\*.scm.appservice.|\*.sso.appservice.') { Write-AzsReadinessLog -Message ("Skipping {0} as part of multi SAN request later" -f $individualSAN) -Type Warning -Function $thisFunction } else { $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $individualSAN -SubjectToChange $subject Write-AzsCertificateRequestFileInternal -subjectAltNames $individualSAN -subject $subject -KeyLength $KeyLength -HashAlgorithm $HashAlgorithm -OutputRequestPath $OutputRequestPath } } # special case for app service web default ssl is a multi SAN request if ($includePaaS) { $appSvcWebDefaultSSLSANs = $data.AppSvcWebTrafficDefault -f $regionName.ToLower(), $externalFQDN.ToLower() $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $appSvcWebDefaultSSLSANs -SubjectToChange $subject Write-AzsCertificateRequestFileInternal -subjectAltNames $appSvcWebDefaultSSLSANs -subject $subject -KeyLength $KeyLength -HashAlgorithm $HashAlgorithm -OutputRequestPath $OutputRequestPath } } else { Write-AzsReadinessLog -Message ("Unexpected RequestType: {0}" -f $requestType) -Type Error -Function $thisFunction throw ("Unexpected RequestType: $requestType" -f $thisFunction) } Write-Footer -invocation $MyInvocation } function Set-AzsCertificateCommonName { <# .SYNOPSIS Set's the CN of the subject to the first DNS name in the SAN .DESCRIPTION To avoid potential client problems the common name will be made consistent with the first DNS Name in the SubjectAlternativeNames .EXAMPLE Set-AzsCertificateCommonName -subjectAlternativeNames $SANs -SubjectToChange $subject This will set the common regardless of it's previous state or value. .INPUTS SubjectAlternativeNames - string - SANs to parse common from. SubjectToChange - OrderedDictionary - may or may not contain a common name. .OUTPUTS SubjectChanged - OrderedDictionary - with the new or replaced common name .NOTES General notes #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [OutputType([System.Collections.Specialized.OrderedDictionary])] param ([string]$SubjectAlternativeNames, [System.Collections.Specialized.OrderedDictionary]$SubjectToChange ) $thisFunction = $MyInvocation.MyCommand.Name # Write CN as first SAN, overwrite as needed. $commonName = $SubjectAlternativeNames.split('&')[0].Replace('dns=', '') if ($SubjectToChange.CN) { Write-AzsReadinessLog -Message ("Found CN = {0} and will remove it" -f $SubjectToChange.CN) -Type Info -Function $thisFunction $SubjectToChange.Remove('CN') } $SubjectToChange.Insert(0, 'CN', $commonName) Write-AzsReadinessLog -Message ("Inserted CN = {0}" -f $commonName) -Type Info -Function $thisFunction $SubjectChanged = $SubjectToChange $SubjectChanged } # SIG # Begin signature block # MIIkiAYJKoZIhvcNAQcCoIIkeTCCJHUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBNgIbIfx/8htg4 # SOHoDSwsfgjgnSaM+7+ylcwebJzMtqCCDYEwggX/MIID56ADAgECAhMzAAABA14l # HJkfox64AAAAAAEDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTgwNzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDRlHY25oarNv5p+UZ8i4hQy5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU5 # 3Rt+M2wR/Dsm85iqXVNrqsPsE7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94S # nYW/fHaGfXKxdpth5opkTEbOttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjR # hIjiATwi/K+WCMxdmcDt66VamJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8A # DwH+S15UD8rEZT8K46NG2/YsAzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0 # NIZ5rCrVXzCRO+QUauuxygQjAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8w # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJ # Uw0/DSbsokTYDdGfY5YGSz8eXMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHn # wya0rPUn00iThoj+EjWRZCLRay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjY # DBu2KWAndjQkm925l3XLATutghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs # 1JPyZ6vic8g9o838Mh5gHOmwGzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCy # RhxdXnYqWJ/U7vL0+khMtWGLsIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuy # bR/5WcF9ttE5yXnggxxgCto9sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpD # MYLa2ISuubAfdfX2HX1RETcn6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy # 2TUxuNty+rR1yIibar+YRcdmstf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v4 # 6Mp79mwV+QMZDxGFqk+av6pX3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOla # P470I3gwsvKmOMs1jaqYWSRAuDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6Iwh # SkHC3uuOW+4Dwx1ubuZUNWZncnwa6lL2IsRyP64wggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIWXTCCFlkCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAQNeJRyZH6MeuAAAAAABAzAN # BglghkgBZQMEAgEFAKCB3jAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgC9NDFMS7 # 0kfgj5XyUPzngfYUTRtnvKoHS+25s7ZZWUQwcgYKKwYBBAGCNwIBDDFkMGKgSIBG # AE0AaQBjAHIAbwBzAG8AZgB0ACAAQQB6AHUAcgBlAFMAdABhAGMAawAgAFAAYQBy # AHQAbgBlAHIAVABvAG8AbABrAGkAdKEWgBRodHRwOi8vQ29kZVNpZ25JbmZvIDAN # BgkqhkiG9w0BAQEFAASCAQCbMn3kzq8uoCCDEePiGGpDjzlU2S7M8ZYdNDb9jO/v # tJADXcrwB7mKIsS1CULc3Uz1H7RuV2VnqwH0K31TKSWh1ykyRduzYoNHbYI5pP7O # qPB/DeopeScuMiohtOwoS3m3wFY2/QrJ5meQ95kGE4F/1fZSwIHlQtYTxNIII796 # Vcr5DEc40TIBULu+ydaq5jpVdwpFGuQiP1BzOWxtjodQhDjvCapc3AAaaZwpiUBi # 84ytAjeI/chimuwmxWIA40CHLyRGVr8PzPgkSAEwcwMFmeXDUeueNPt6Gn6xaYFT # v2PRhpE9JyZo8QugEdiYGkxUlaIIcLz+QA8+jQO7d7HwoYITtzCCE7MGCisGAQQB # gjcDAwExghOjMIITnwYJKoZIhvcNAQcCoIITkDCCE4wCAQMxDzANBglghkgBZQME # AgEFADCCAVgGCyqGSIb3DQEJEAEEoIIBRwSCAUMwggE/AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIK1ZHNRn4FvU05aUrIU0AYL1YtQtNFJPqcpylbKs # ve7cAgZb2dKzteAYEzIwMTgxMTAxMjE1MzUzLjA0MVowBwIBAYACAfSggdSkgdEw # gc4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsT # IE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFs # ZXMgVFNTIEVTTjpDMEY0LTMwODYtREVGODElMCMGA1UEAxMcTWljcm9zb2Z0IFRp # bWUtU3RhbXAgU2VydmljZaCCDx8wggT1MIID3aADAgECAhMzAAAA0BxqYGHC5+Gt # AAAAAADQMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy # MDEwMB4XDTE4MDgyMzIwMjYyOFoXDTE5MTEyMzIwMjYyOFowgc4xCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpD # MEY0LTMwODYtREVGODElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy # dmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJs+hjbXWs77xn3M # KjwgBhWamzkgPLrX6kM0xhOnSbyG5WitejlIoyYoBicTQLWyDqyuZflDSzhrrgdh # GKcO/aXLYczZxTfqG9rcTLVle02RqzqNK51fUh2p7c24gIaQ5Ma3J2EF5ItZDnu1 # bHhzT94U/JFvnoH3c5V7fmAyKuc01TEbXf5nQ9XnZJGMZNn7NQ9nLtysrVQ/xE1s # p8dZ3Y9hkrpZJG/ftV3uhyoLv0ds/XElvylkvFNUKpaV8+iQTWhl8rGYdO0EOU/J # PWsaSoKBX3sf8pZoB5ezL9ZF5dpa5o9XElUKelwSxGuC18WadKI2gTO75+DsUggE # 9c6SRisCAwEAAaOCARswggEXMB0GA1UdDgQWBBQDgyiXqGsl8sRTMh+ZrTq8dxjF # cTAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEug # SaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N # aWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsG # AQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Rp # bVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQClvCSWZAnfWvS/lFN/NhmRbjwg # 9eBgXXyyl0YJWgwYPuorUZHkhc3R5PNNY0lNGk6RHjvbp0n22u6UR/BMN72jC2eB # WdRNNlyjiPUFrWEudzStxPXBl9tBz1tdaaEQjcouhsBOMrtK1/Pj1VX1C9GCveV2 # eoYZtjmaIeDkIo4gY6v1cMW1Pe972NOolmXyrElWgp8dxSJ1h0bB8AqPzNwwiu3T # 5oT1XsbTTBM1X124MgDm9rNkBSQmHq+z4fYeayXRKt7O1ycs65IYvkfBImt6cY+U # FPiYR5mpGF0la6dB01Z8QaY3Wi9BmzsprJG72Xk6JryszvCRcNUX1wT5wIEsMIIG # cTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0 # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1 # WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC # ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9p # lGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEw # WbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeG # MoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJ # UGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw # 2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0C # AwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ # 80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E # BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2U # kFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5j # b20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmww # WgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYD # VR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYI # KwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0 # AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9 # naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtR # gkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzy # mXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCf # Mkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3D # nKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs # 9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110 # mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL # 2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffI # rE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxE # PJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc # 1bN+NR4Iuto229Nfj950iEkSoYIDrTCCApUCAQEwgf6hgdSkgdEwgc4xCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29m # dCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVT # TjpDMEY0LTMwODYtREVGODElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg # U2VydmljZaIlCgEBMAkGBSsOAwIaBQADFQApKR+LcXEyVzc2v72d4dOjMT6OlaCB # 3jCB26SB2DCB1TELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEp # MCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJzAlBgNV # BAsTHm5DaXBoZXIgTlRTIEVTTjo1N0Y2LUMxRTAtNTU0QzErMCkGA1UEAxMiTWlj # cm9zb2Z0IFRpbWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIF # AN+FY3kwIhgPMjAxODExMDExOTM1MjFaGA8yMDE4MTEwMjE5MzUyMVowdDA6Bgor # BgEEAYRZCgQBMSwwKjAKAgUA34VjeQIBADAHAgEAAgIciTAHAgEAAgIbsTAKAgUA # 34a0+QIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIBAAID # FuNgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQB1PgRQMBfYZpbjQ+xP # UIqi/zxcL/H8yVU71XY6xejb85doJuyW8eCNdTEUQVXKXE1T1/Gcp9H+XFZLuVey # EUDjO8vsCndgcW3ZRQUH32JAP2l2t+ibgv2wx3fIc48wJ4iMKT++MzBhe82Dl04x # 8sjEv6HQ3iDBleI0RS210nDnPYakNl0jujielh5oJqJTeWu56/LkO1BkIhuQemdc # qZF81SgSnKU6Ao0FaBR42AR2mg6N7EYsqxjChe996Gdc8Fe5vS8vYPT87wEiKgDT # oSSroOY2UVKRLLWccNlJlBKGv4vg0ZIOQT8TubcNoL9GrJyXw8mLQFIzI6aO++P1 # ZEKgMYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC # EzMAAADQHGpgYcLn4a0AAAAAANAwDQYJYIZIAWUDBAIBBQCgggEyMBoGCSqGSIb3 # DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgfZ8wGMOJ7sMneDLT # 3do47Mkbu0bzXl56/thIUfPvjtowgeIGCyqGSIb3DQEJEAIMMYHSMIHPMIHMMIGx # BBQpKR+LcXEyVzc2v72d4dOjMT6OlTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwAhMzAAAA0BxqYGHC5+GtAAAAAADQMBYEFG8/trCD4uFRLd0e # OmOcBD/p1lRdMA0GCSqGSIb3DQEBCwUABIIBADZohByOTaEqnyyJPV7fmYyM/uuY # X6yYF7v7srfRP9jxjBgU+0uO5jKRrzjc7x/lLPHzpDsh5+QrL95WTZ6570LbHw/+ # zcFapVasrtZSQvphmd3hB8KoqhRfwymEhKHVRtVAjr9wNF20McciBo/186xuAxIZ # t/SNYAZg53YfszperPS/jupH1YEwYHZieS3vSPvzthkoRh+EpHAziSYvrUawUmHR # By8hISErsssw81hQfHdv1ZGgex8SabKJuSAt+XNu+VDY3WJiSu8dWPwMKoWsDbkI # eFnNQHLkTUVwHTj6M01piZTReq5JyBNKAtrzxGu7HVpx7j8kRNLJZeThNBU= # SIG # End signature block |