c-hive.psm1
|
function Test-IsNumeric { <# .SYNOPSIS Returns if a specific value is numeric or not .EXAMPLE Test-IsNumeric -Value "1" # Returns $true Test-IsNumeric -Value "a" # Returns $false #> param( [Parameter(Mandatory = $true)] [string]$Value ) Write-Verbose "Executing: Test-IsNumeric" return $Value.Trim() -match '^-?\d+$' } function Get-IsValidCsr{ param($base64Csr) $pemHeader = "-----BEGIN CERTIFICATE REQUEST-----`n" $pemFooter = "`n-----END CERTIFICATE REQUEST-----" # Insert line breaks every 64 chars (optional but recommended) $wrappedCsr = ($base64Csr -split '(.{64})' | Where-Object {$_ -ne ''}) -join "`n" $pem = $pemHeader + $wrappedCsr + $pemFooter $stringReader = New-Object Org.BouncyCastle.Utilities.IO.Pem.PemReader([System.IO.StringReader]::new($pem)) try { $csr = $stringReader.ReadPemObject() if ($csr.Type -eq "CERTIFICATE REQUEST") { return $true } else { return $false } } catch { return $false } } function Convert-CSR2Object{ param($CSR) #add-type -path ".\BouncyCastle.Cryptography.dll" $base64 = ($CSR -replace "-----.*-----", "") -replace "\s", "" $bytes = [Convert]::FromBase64String($base64) return [Org.BouncyCastle.Pkcs.Pkcs10CertificationRequest]::new($bytes) } function Get-Subject{ param($csr_object) $subject = $csr_object.GetCertificationRequestInfo().Subject $oids = $subject.GetOidList() $values = $subject.GetValueList() # Initialize empty hashtable $subjectHash = @{} for ($i = 0; $i -lt $oids.Count; $i++) { $oid = $oids[$i].Id $value = $values[$i].ToString() switch ($oid) { "2.5.4.3" { $subjectHash["CommonName"] = $value } "2.5.4.10" { $subjectHash["Organization"] = $value } "2.5.4.11" { $subjectHash["OrganizationalUnit"] = $value } "2.5.4.6" { $subjectHash["Country"] = $value } "2.5.4.7" { $subjectHash["Locality"] = $value } "2.5.4.8" { $subjectHash["StateOrProvince"] = $value } "2.5.4.9" { $subjectHash["StreetAddress"] = $value } "1.2.840.113549.1.9.1" { $subjectHash["EmailAddress"] = $value } "0.9.2342.19200300.100.1.25" { $subjectHash["DomainComponent"] = $value } "0.9.2342.19200300.100.1.1" { $subjectHash["UserID"] = $value } "2.5.4.12" { $subjectHash["Title"] = $value } "2.5.4.15" { $subjectHash["BusinessCategory"] = $value } "2.5.4.17" { $subjectHash["PostalCode"] = $value } "2.5.4.5" { $subjectHash["SerialNumber"] = $value } "2.5.4.44" { $subjectHash["GenerationQualifier"] = $value } "2.5.4.42" { $subjectHash["GivenName"] = $value } "2.5.4.4" { $subjectHash["Surname"] = $value } "2.5.4.43" { $subjectHash["Initials"] = $value } "2.5.4.65" { $subjectHash["Pseudonym"] = $value } "1.2.840.113549.1.9.2" { $subjectHash["UnstructuredName"] = $value } default { $subjectHash["OID_$oid"] = $value } } } # Create a clean output object $subjectObject = [pscustomobject]$subjectHash return $subjectObject } function Get-SAN{ param($csr_object) $extRequestOidString = "1.2.840.113549.1.9.14" $csr_object_attributes = $csr_object.GetCertificationRequestInfo().Attributes $sanEntries = @() foreach ($attr in $csr_object_attributes) { $oid = $attr[0] # DerObjectIdentifier if ($oid.Id -eq $extRequestOidString) { $values = $attr[1] # DerSet $extAsn1 = $values[0] # Extensions ASN.1 object $extensions = [Org.BouncyCastle.Asn1.X509.X509Extensions]::GetInstance($extAsn1) $sanOid = [Org.BouncyCastle.Asn1.X509.X509Extensions]::SubjectAlternativeName $sanExtension = $extensions.GetExtension($sanOid) if ($sanExtension -ne $null) { $generalNames = [Org.BouncyCastle.Asn1.X509.GeneralNames]::GetInstance($sanExtension.GetParsedValue()) foreach ($generalName in $generalNames.GetNames()) { $entry = [PSCustomObject]@{ TagNo = $generalName.TagNo Type = "" Value = $null } switch ($generalName.TagNo) { 1 { # Email (RFC822Name) $entry.Type = "Email" $entry.Value = [string]$generalName.Name } 2 { # DNS Name $entry.Type = "DNS" $entry.Value = [string]$generalName.Name } 3 { # X400Address $entry.Type = "X400Address" $entry.Value = [string]$generalName.Name } 4 { # DirectoryName $entry.Type = "DirectoryName" $entry.Value = [string]$generalName.Name } 5 { # EdiPartyName $entry.Type = "EdiPartyName" $entry.Value = [string]$generalName.Name } 6 { # UniformResourceIdentifier $entry.Type = "URI" $entry.Value = [string]$generalName.Name } 7 { # IP Address $entry.Type = "IPAddress" $ipBytes = $generalName.Name.GetOctets() $entry.Value = [System.Net.IPAddress]::new($ipBytes).ToString() } 8 { # RegisteredID $entry.Type = "RegisteredID" $entry.Value = [string]$generalName.Name } default { $entry.Type = "Other" $entry.Value = [string]$generalName.Name } } $sanEntries += $entry } } } } return ,@($sanEntries) } function Get-Key{ param($csr_object) if ($Null -ne $csr_object.GetPublicKey().modulus) { #RSA $key = [PSCustomObject]@{ Type = "RSA" Len = $csr_object.GetPublicKey().modulus.bitlength } } else { #EC $key = [PSCustomObject]@{ Type = "EC" Len = $csr_object.GetPublicKey().parameters.curve.fieldSize } } return $key } function Get-KeyUsage{ param($csr_object) $extRequestOidString = "1.2.840.113549.1.9.14" $csrAttributes = $csr_object.GetCertificationRequestInfo().Attributes $keyUsageResult = @() foreach ($attr in $csrAttributes) { $oid = $attr[0] # DerObjectIdentifier if ($oid.Id -eq $extRequestOidString) { $values = $attr[1] # DerSet $extAsn1 = $values[0] # ASN.1 encoded extensions $extensions = [Org.BouncyCastle.Asn1.X509.X509Extensions]::GetInstance($extAsn1) $keyUsageOid = [Org.BouncyCastle.Asn1.X509.X509Extensions]::KeyUsage $keyUsageExt = $extensions.GetExtension($keyUsageOid) if ($keyUsageExt -ne $null) { $keyUsage = [Org.BouncyCastle.Asn1.X509.KeyUsage]::GetInstance($keyUsageExt.GetParsedValue()) $flags = $keyUsage.IntValue if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::DigitalSignature) { $keyUsageResult += "DigitalSignature" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::NonRepudiation) { $keyUsageResult += "NonRepudiation" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::KeyEncipherment) { $keyUsageResult += "KeyEncipherment" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::DataEncipherment) { $keyUsageResult += "DataEncipherment" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::KeyAgreement) { $keyUsageResult += "KeyAgreement" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::KeyCertSign) { $keyUsageResult += "KeyCertSign" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::CrlSign) { $keyUsageResult += "CrlSign" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::EncipherOnly) { $keyUsageResult += "EncipherOnly" } if ($flags -band [Org.BouncyCastle.Asn1.X509.KeyUsage]::DecipherOnly) { $keyUsageResult += "DecipherOnly" } } } } return $keyUsageResult } function Get-EnhancedKeyUsage{ param ($csr_object) $ekuList = @() $extRequestOidString = "1.2.840.113549.1.9.14" $csrAttributes = $csr_object.GetCertificationRequestInfo().Attributes foreach ($attr in $csrAttributes) { $oid = $attr[0] if ($oid.Id -eq $extRequestOidString) { $values = $attr[1] $extAsn1 = $values[0] $extensions = [Org.BouncyCastle.Asn1.X509.X509Extensions]::GetInstance($extAsn1) $ekuOid = [Org.BouncyCastle.Asn1.X509.X509Extensions]::ExtendedKeyUsage $ekuExtension = $extensions.GetExtension($ekuOid) if ($ekuExtension -ne $null) { $eku = [Org.BouncyCastle.Asn1.X509.ExtendedKeyUsage]::GetInstance($ekuExtension.GetParsedValue()) foreach ($keyPurposeId in $eku.GetAllUsages()) { $oidString = $keyPurposeId.Id $friendlyName = switch ($oidString) { "1.3.6.1.5.5.7.3.1" { "Server Authentication" } "1.3.6.1.5.5.7.3.2" { "Client Authentication" } "1.3.6.1.5.5.7.3.3" { "Code Signing" } "1.3.6.1.5.5.7.3.4" { "Email Protection" } "1.3.6.1.5.5.7.3.8" { "Time Stamping" } "1.3.6.1.5.5.7.3.9" { "OCSP Signing" } "1.3.6.1.4.1.311.10.3.4" { "Encrypting File System" } default { "Unknown EKU: $oidString" } } $ekuList += $friendlyName } } } } return $ekuList } function Get-CryptographicServiceProvider{ param($csr_object) $cspList = @() $attributes = $csr_object.GetCertificationRequestInfo().Attributes $msCspOid = "1.3.6.1.4.1.311.13.2.2" foreach ($attr in $attributes) { $oid = $attr.AttrType if ($oid.Id -eq $msCspOid) { $attrValues = $attr.AttrValues foreach ($val in $attrValues) { $asn1Str = [Org.BouncyCastle.Asn1.DerUtf8String]::GetInstance($val) if ($asn1Str.GetString() -match "Provider\s*=") { $provider = $asn1Str.GetString().Split("=")[1].Trim() $cspList += $provider } } } } return $csrList } function Get-CertExtensions{ param ($csr_object) $extensionsList = @() $attrs = $csr_object.GetCertificationRequestInfo().Attributes $extRequestOid = "1.2.840.113549.1.9.14" # PKCS #9 extensionRequest OID foreach ($attr in $attrs) { if ($attr.AttrType.Id -eq $extRequestOid) { $attrValues = $attr.AttrValues foreach ($val in $attrValues) { # Get X509Extensions object from ASN1 $exts = [Org.BouncyCastle.Asn1.X509.X509Extensions]::GetInstance($val) # Extract all extensions from X509Extensions $oids = $exts.GetExtensionOids() foreach ($oid in $oids) { $extension = $exts.GetExtension($oid) $oidStr = $oid.Id try { $extName = [Org.BouncyCastle.Asn1.X509.X509ExtensionUtilities]::GetExtensionName($oid) } catch { $extName = "Unknown" } $obj = [PSCustomObject]@{ ExtensionOID = $oidStr ExtensionName = $extName ExtensionValue = $extension } $extensionsList += $obj } } } } return $extensionsList } function Test-CSR { param ( $csr ) $result = [PSCustomObject]@{ isValid = $false subject = @() san = @() key = @() keyusage = @() EnhancedKeyUsage = @() CryptographicServiceProvider = @() CertExtensions = @() csr = $csr } $result.isValid = Get-IsValidCsr -base64Csr $csr if ($result.isValid) { $csr_object = Convert-CSR2Object -CSR $csr $result.subject = get-subject $csr_object $result.san = get-san $csr_object $result.key = get-key -csr_object $csr_object $result.keyusage = get-keyusage -csr_object $csr_object $result.EnhancedKeyUsage = Get-EnhancedKeyUsage -csr_object $csr_object $result.CryptographicServiceProvider = Get-CryptographicServiceProvider -csr_object $csr_object $result.CertExtensions = Get-CertExtensions -csr_object $csr_object } return $result } function Test-CSRPolicies() { param($Test_CSRResult, $csr_policies, $allowed_domains, $template) $csr_basics = $Test_CSRResult | ConvertFrom-Json -Depth 10 $current_allowed_domains = $allowed_domains | ConvertFrom-Json -Depth 10 $current_template = $template | ConvertFrom-Json -Depth 10 $current_policies = $csr_policies | ConvertFrom-Json -Depth 10 | Where-Object { ($_.vx_assignedtemplate.Id -eq $current_template.ID) -or ($_.vx_assignedtemplate.Id -eq $null) } | Where-Object { $_.vx_enabled -eq $true } | Select-Object id, vx_allowoverwrite, vx_helpurl, vx_condition,vx_property, vx_value $key = $csr_basics.key | convertfrom-json $subject = $csr_basics.subject | ConvertFrom-Json $policy_violations = @() #region hard coded policies #region minKeyLen check $current_kl = [int]$key.Len $min_kl = [int]$current_template.vx_minkeylen if ($current_kl -lt $min_kl.vx_minkeylen) { $my_category = "Key Length" $policy_violations += [PSCustomObject]@{ CheckValue = $min_kl Category = $my_category; Issue = [String]$current_kl; Condition = "less than"; HelpURL ="https://www.ssl.com/blogs/new-minimum-rsa-key-size-for-code-signing-certificates/"; AllowOverwrite=$false } } #endregion #region countryCode check try{[Void][System.Globalization.RegionInfo]$subject.Country;} catch{ $my_category = "Country" $policy_violations += [PSCustomObject]@{ CheckValue = "Valid CountryCode"; Category = $my_category; Issue = [String]$subject.Country; Condition = "not ="; HelpURL ="https://www.digicert.com/kb/ssl-certificate-country-codes.htm"; AllowOverwrite=$false } } #endregion #region checks based on: https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties if ($subject.CommonName.length -ge 64) { $my_category = "Common Name Length" $policy_violations += [PSCustomObject]@{ CheckValue = "64"; Category = $my_category; Issue = [String]$subject.CommonName.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.Organization.length -ge 64) { $my_category = "Organization Length" $policy_violations += [PSCustomObject]@{ CheckValue = "64"; Category = $my_category; Issue = [String]$subject.Organization.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.OrganizationalUnit.length -ge 64) { $my_category = "Organizational Unit Length" $policy_violations += [PSCustomObject]@{ CheckValue = "64"; Category = $my_category; Issue = [String]$subject.OrganizationalUnit.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.Locality.length -ge 64) { $my_category = "Locality Length" $policy_violations += [PSCustomObject]@{ CheckValue = "64"; Category = $my_category; Issue = [String]$subject.Locality.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.StateOrProvince.length -ge 128) { $my_category = "State or Province Length" $policy_violations += [PSCustomObject]@{ CheckValue = "128"; Category = $my_category; Issue = [String]$subject.StateOrProvince.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.StreetAddress.length -ge 30) { $my_category = "Street Address Length" $policy_violations += [PSCustomObject]@{ CheckValue = "30"; Category = $my_category; Issue = [String]$subject.StreetAddress.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.EmailAddress.length -ge 128) { $my_category = " Email Address Length" $policy_violations += [PSCustomObject]@{ CheckValue = "128"; Category = $my_category; Issue = [String]$subject.EmailAddress.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.DomainComponent.length -ge 128) { $my_category = "Domain Component Length" $policy_violations += [PSCustomObject]@{ CheckValue = "128"; Category = $my_category; Issue = [String]$subject.DomainComponent.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.GivenName.length -ge 16) { $my_category = "Given Name Length" $policy_violations += [PSCustomObject]@{ CheckValue = "16"; Category = $my_category; Issue = [String]$subject.GivenName.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.Initials.length -ge 5) { $my_category = "Initials Length" $policy_violations += [PSCustomObject]@{ CheckValue = "5"; Category = $my_category; Issue = [String]$subject.Initials.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.Title.length -ge 64) { $my_category = "Title Length" $policy_violations += [PSCustomObject]@{ CheckValue = "64"; Category = $my_category; Issue = [String]$subject.Title.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } if ($subject.SerialNumber.length -ge 40) { $my_category = "Serial Number Length" $policy_violations += [PSCustomObject]@{ CheckValue = "40"; Category = $my_category; Issue = [String]$subject.SerialNumber.Length; Condition = "greater or equal than"; HelpURL ="https://docs.microsoft.com/en-us/windows/win32/seccrypto/name-properties"; AllowOverwrite=$false } } #endregion #endregion #region SAN checks $DNSNames = @() $DNSNames+=$subject.CommonName $DNSNames+=(($csr_basics.san | ConvertFrom-Json) | Where-Object{$_.Type -eq "DNS"}).value $DNSNames = $DNSNames | Select-Object -Unique #region max number of SANs check if ($DNSNames.count -ge 64) { $violation = [PSCustomObject]@{ Category = 'Maxium number of SANs exceeded' Issue = [String]($DNSNames.count) Condition = "more than 64 SANs" CheckValue = "DNSNames"; HelpURL = "https://youralloweddomains.contoso.com"; AllowOverwrite = $true } $policy_violations += $violation } #endregion #region allowed domain name check foreach($DNSName in $DNSNames) { $found = ($current_allowed_domains | where-object {$DNSName -like "*$($_.vx_domain)*"}).count if ($found -eq 0) { $violation = [PSCustomObject]@{ Category = 'Allowed Domains' Issue = [String]$DNSName Condition = "unexpected Domain" CheckValue = "DNSNames"; HelpURL = "https://youralloweddomains.contoso.com"; AllowOverwrite = $true } $policy_violations += $violation } } #endregion #endregion #region custom policies foreach ($policy in $current_policies) { $current_violation = $false $value = $policy.vx_value $violation = [PSCustomObject]@{ Category = '' Issue = '' Condition = $policy.vx_condition CheckValue = $policy.vx_value HelpURL = $policy.vx_allowoverwrite AllowOverwrite = $policy.vx_allowoverwrite } switch ($policy.vx_property){ "CN" {$my_check_value = $subject.CommonName ;$violation.category = "Common Name"} "OU" {$my_check_value = $subject.OrganizationalUnit ;$violation.category = "Organizational Unit"} "O" {$my_check_value = $subject.Organization ;$violation.category = "Organization"} "L" {$my_check_value = $subject.Locality ;$violation.category = "Locality"} "ST" {$my_check_value = $subject.StateOrProvince ;$violation.category = "State or Province"} "STREET" {$my_check_value = $subject.StreetAddress ;$violation.category = "Street Address"} "C" {$my_check_value = $subject.Country ;$violation.category = "Country"} "E" {$my_check_value = $subject.EmailAddress ;$violation.category = "Email Address"} "SAN (DNS)" {$my_check_value = $csr_basics.san.Value -join ";" ;$violation.category = "Subject Alternative Name"} "KeyAlgo" {$my_check_value = $csr_basics.key.Type ;$violation.category = "Key Type"} "KeyLength" {$my_check_value = $csr_basics.key.Len ;$violation.category = "Key Length "} "enhancedKeyUsage" {$my_check_value = $csr_basics.EnhancedKeyUsage ;$violation.category = "Enhanced Key Usage"} "keyUsage" {$my_check_value = ($csr_basics.keyusage) -join ";" ;$violation.category = "Key Usage"} } switch ($policy.vx_condition) { "equals" { if ($my_check_value -ne $value) { $current_violation = $true } } "not equals" { if ($my_check_value -eq $value) { $current_violation = $true } } "greater than" { if ($my_check_value -lt [int]$value) { $current_violation = $true } } "less than" { if ($my_check_value -gt [int]$value) { $current_violation = $true } } "contains" { if (!($my_check_value -like "*$value*")) { $current_violation = $true } } "not contains" { if ($my_check_value -like "*$value*") { $current_violation = $true } } "greater or equal than" { if ($my_check_value -le [int]$value) { $current_violation = $true } } "less or equal than" { if ($my_check_value -ge [int]$value) { $current_violation = $true } } } if ($current_violation) { $violation.Issue = $my_check_value; $policy_violations += $violation } } #endregion return $policy_violations } Export-ModuleMember * # SIG # Begin signature block # MIIFfwYJKoZIhvcNAQcCoIIFcDCCBWwCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAxNO4YCoZFJEmx # d8RtrtatF4evxKUAfBZfz6xAF8VnP6CCAvwwggL4MIIB4KADAgECAhARXeApLbab # i0U384S48TtIMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZpdmFYaXRlMCAX # DTI1MDUyNjA5NTEyN1oYDzIxMjQwNTI2MTAwMTI4WjATMREwDwYDVQQDDAh2aXZh # WGl0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL65W+H9E82gU7ZH # /XSJbAxPvFtzHVRoUHA045konf474KBkaDc4ztYlgRvkZldzFykjg8jeuSa0ANlm # VX1HoObUVYagSax/fWPSOkqDN4aTolX8GaI98ZOey9a9OYUI3F5mznkeksWUqbT7 # cgCX2Y/Qtnh557nSuplxmmtTGEzrDzj5evvnAyGGH1mcJxD3DeJGv3olsK9J982q # 0s+t9bCSSWPx2vapiR9VmCTKZJMi4YlNXrM+UFEd7R1KQR5M/KPY+rg5UJ0kU8Hz # q0z9z6L3mbk+b28uI9EwGbvuXPtqUCi5P9YrdpKjOR16kaBJ9Od1+mphUaxNfc75 # eb8Uzd0CAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF # BwMDMB0GA1UdDgQWBBQjLXXsxxmYhIvcsrZywbsrsjn9ajANBgkqhkiG9w0BAQsF # AAOCAQEAtVY7rYwe8G/F+95Lc47UpZD1ICAg/sS/yRZjKhoos/iufKV478RsZyDX # dAj5unm7XqGZSAyW4hHOdWYQdF//emNpXyOmNSkPuwpebhKtAwddvUwlGzFbsh4g # 5ULVgmvnf2J3eqKQpOulecCH8MJqCrRAi8pgBjl5/cmr6ZMULLAswmEkCTNgvx7g # qVFvJ+Zcbxu2gOiA9EzTzMWM8thrkOGWYYu/i0b63BxKXAenkBKJtjugFb2iefGb # mhfcelCGo/Gx7XD7AdYM3ov5YTYpnTDmCCTch9dR8vz7FPgwV0HPYfIZ1b1INC4y # C6I3Hf8ZXMzquofQpd9zlu7qqnKF/DGCAdkwggHVAgEBMCcwEzERMA8GA1UEAwwI # dml2YVhpdGUCEBFd4CkttpuLRTfzhLjxO0gwDQYJYIZIAWUDBAIBBQCggYQwGAYK # KwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB # BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg # Yxaie2WQ+Y4GP2zlXDwiRYXf5OE7R3tAEma6PxTHGgIwDQYJKoZIhvcNAQEBBQAE # ggEAkBzIVd0LHk0zafJZbyLpYiUZtBJkxUAI3n58CqVs/79jXAfbnGT0sPCM5cMe # h234D7h9umtuniejojAEVJVRATSuivNT4RA0cn/sLih8+B94kM+3F7KKHYEps5zD # U/z+6FfZsBkDq/Uqv3NQfM+DtzfXZ2mNrIkiUWnHJeStgtdxy2z5ecBUTTee0c29 # uN2L0HCqffXrLTOHiQnwF745cHgkBkzdkI5kOjSjg2DPPblKPgRQobpB+NuILFBA # RidCMwf+QMjZch8i8FRMxkzt+H3Xvjf/I5ZDwBWRbFGPWFvuxTHhR1U1CjSOqvMf # P0nmIklbt8j8p8eFZBlvYjx78Q== # SIG # End signature block |