# encoding: ascii
# api: powershell
# title: CenturionPortal
# description: Generates a self-signed certificate authority and a code-signing certificate using OpenSSL
# version: 1.2
# type: script
# author: Joel Bennett
# license: CC0
# function: Get-UserEmail
# x-poshcode-id: 2102
# x-archived: 2013-03-05T02:09:37
# x-published: 2013-08-25T07:00:00
#
# Shows how to import certificates to the current user and local machine stores using System.Security.Cryptography.X509Certificates.X509Store
# Please READ the intro comments, and consider deleting your Certificate Authority Private key if you can’t be 100% sure you can secure it.
#
## New-CodeSigningCert.ps1
########################################################################################################################
## Does the setup needed to self-sign PowerShell scripts ...
## Generates a "test" self-signed root Certificate Authority 
## And then generates a code-signing certificate (and signs it with the CA certificate)
## OPTIONALLY (specify -import or -importall) imports the certificates to the store(s)
########################################################################################################################
## NOTE: Uses OpenSSL (because it's xcopy redistributable -- wake up Microsoft)
##       In order for this to work you should KEEP the script in the folder with OpenSsl.exe
## Also, it is VERY important that you properly provide passwords and the locale data...
## You can obviously reorder the parameters however you like, and hard-code some of the values in the parameters, but 
## you need to make sure that if you use this to generate multiple certificates, that you preserve all of the certs
## and keep track of all your passwords so you don't lock yourself out of any of them.
########################################################################################################################
## Usage:
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Joel Bennett" Jaykul@HuddledMasses.org HuddledMasses.org Mystery Rochester "New York" US -importall -OpenSSLLocation C:\Users\Joel\Documents\WindowsPowershell\PoshCerts\bin -CAPassword MyCleverRootPassword -CodeSignPassword EvenMoreCleverPasswords
##
## If I hard-coded the company/dept/etc ... I could use this to generate certs for all my devs:
## 
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Mark Andreyovich" FakeEmail@Xerox.net -CAPassword MyCleverRootPassword -CodeSignPassword MarksPassword
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Jesse Voller" FakeEmail2@Xerox.net -CAPassword MyCleverRootPassword -CodeSignPassword JessesPassword
##
## For the signed scripts to work, I just  have to -import on the devices where the scripts need to run:
##
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Jesse Voller" -import
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Mark Andreyovich" -import
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Joel Bennett" -import
##
## On the developers' workstations, I need to use Get-PfxCertificate to sign, or else run -importall 
## That will load the codesigning cert in their "my" store, and will only require the password for the initial import
##
## \\Server\PoshCerts\New-CodeSigningCert.ps1 $pwd\Certs "Joel Bennett" -importall -CodeSignPassword MyCodeSignPassword
########################################################################################################################
## History
##  1.0 - Initial public release
##  1.1 - Bug fix release to make it easier to use...
##  1.2 - Bug fix to get the ORG and COMMON NAME set correctly -- Major whoops!
##
Param(
$CertStorageLocation = (join-path (split-path $Profile) "Certs"),
$UserName       = (Read-Host "User name")

, $email        
, $company      
, $department   
, $city         
, $state        
, $country      

, $RootCAName   = "Self-Signed-Root-CA"
, $CodeSignName = "$UserName Code-Signing"
, $alias        = "PoshCert",


[string]$keyBits = 4096,
[string]$days = 365,
[string]$daysCA = (365 * 5),

[switch]$forceNew = $false,
[switch]$importall = $false,
[switch]$import = ($false -or $importall),

## we ask you to specify the CA password and your codesign password
## You can leave these null when importing on end-user desktops
$CAPassword = $null,
$CodeSignPassword = $null,

## You really shouldn't pass these unless you know what you're doing
$OpenSSLLocation = $null,
$RootCAPassword = $Null, 
$CodeSignCertPassword = $null
)


function Get-UserEmail {
   if(!$script:email) {
      $script:email = (Read-Host "Email address")
   }
   return $script:email
}

function Get-RootCAPassword {
   if(!$script:RootCAPassword) { 
      if(!$script:CAPassword) {
         $script:CAPassword = ((new-object System.Management.Automation.PSCredential "hi",(Read-Host -AsSecureString "Root CA Password")).GetNetworkCredential().Password)
      }

      ## Then down here we calculate large passwords to actually use:
      ## This works as long as you keep the same company name and root ca name 
      $script:RootCAPassword = [Convert]::ToBase64String( (new-Object Security.Cryptography.PasswordDeriveBytes ([Text.Encoding]::UTF8.GetBytes($CaPassword)), ([Text.Encoding]::UTF8.GetBytes("$company$RootCAName")), "SHA1", 5).GetBytes(64) )
   }
   return $script:RootCAPassword
}

function Get-CodeSignPassword {
   if(!$script:CodeSignCertPassword) { 
    
      if(!$script:CodeSignPassword) {
         $script:CodeSignPassword = ((new-object System.Management.Automation.PSCredential "hi",(Read-Host -AsSecureString "Code Signing Password")).GetNetworkCredential().Password)
      }
      ## This works as long as you keep the same PFX password and email address
      $script:CodeSignCertPassword = ([Convert]::ToBase64String( (new-Object Security.Cryptography.PasswordDeriveBytes ([Text.Encoding]::UTF8.GetBytes($CodeSignPassword)), ([Text.Encoding]::UTF8.GetBytes((Get-UserEmail))), "SHA1", 5).GetBytes(64) ))
   }
   return $script:CodeSignCertPassword
}

function Get-SslConfig {
Param ( 
   $keyBits, 
   $Country    = (Read-Host "Country (2-Letter code)"), 
   $State      = (Read-Host "State (Full Name, no intials)"), 
   $city       = (Read-Host "City"), 
   $company    = (Read-Host "Company Name (or Web URL)"), 
   $orgUnit    = (Read-Host "Department (team, group, family)"), 
   $CommonName, 
   $email = (Read-Host "Email Address")
)
@"
# OpenSSL example configuration file for BATCH certificate generation
# This definition stops the following lines choking if HOME isn't  defined.
HOME			   = .
RANDFILE		   = $($ENV::HOME)/.rnd

# To use this configuration with the "-extfile" option of the "openssl x509" utility
# name here the section containing the X.509v3 extensions to use:
#extensions		= code_sign

####################################################################
[ req ]
default_bits		   = {0}
default_keyfile 	   = privkey.pem
distinguished_name	= req_distinguished_name
#attributes		      = req_attributes
x509_extensions	   = v3_ca  # The extentions to add to the self signed cert
# req_extensions     = v3_ca  # Other extensions to add to a certificate request?

## Passwords for private keys could be specified here, instead of on the commandline
# input_password = secret
# output_password = secret

## Set the permitted string types...
## Some software crashes on BMPStrings or UTF8Strings, so we'll stick with 
string_mask = nombstr

[ req_distinguished_name ]
countryName			      = Country Name (2 letter code)
countryName_default		= {1}
countryName_min			= 2
countryName_max			= 2

stateOrProvinceName		= State or Province Name (full name)
stateOrProvinceName_default	= {2}

localityName			= Locality Name (eg, city)
localityName_default = {3}

0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= {4}

# we can do this but it is not usually needed
#1.organizationName		= Second Organization Name (eg, company)
#1.organizationName_default	= World Wide Web Pty Ltd

organizationalUnitName		      = Organizational Unit Name (eg, section)
organizationalUnitName_default	= {5}

commonName			= Common Name (eg, YOUR name)
commonName_default = {6}
commonName_max			= 64

emailAddress			= Email Address
emailAddress_default = {7}
emailAddress_max		= 64

# SET-ex3			= SET extension number 3

# [ req_attributes ]
# challengePassword		= A challenge password
# challengePassword_min		= 4
# challengePassword_max		= 20
# unstructuredName		= An optional company name

[ v3_ca ]
## Extensions for a typical CA

## PKIX recommendations:
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
## PKIX suggests we should include email address in subject alt name
# subjectAltName=email:copy
## But really they want it *only* there or the certs are "deprecated"
# subjectAltName=email:move
## And the issuer details
# issuerAltName=issuer:copy


## This is what PKIX recommends 
basicConstraints = critical,CA:true
## some broken software chokes on critical extensions, so you could do this instead.
#basicConstraints = CA:true

## For a normal CA certificate you would want to specify this.
## But it will cause problems for our self-signed certificate.
# keyUsage = cRLSign, keyCertSign

## You might want the netscape-compatible stuff too
# nsCertType = sslCA, emailCA

[ code_sign ]
# These extensions are added when we get a code_signing cert
## PKIX recommendations:
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

## PKIX suggests we should include email address in subject alt name
# subjectAltName=email:copy
## But really they want it *only* there or the certs are "deprecated"
# subjectAltName=email:move
## And the issuer details
# issuerAltName=issuer:copy

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE

# If nsCertType is omitted, the certificate can be used for anything *except* object signing.
# We just want to allow everything including object signing:
nsCertType = server, client, email, objsign
# This is the vital bit for code-signing
extendedKeyUsage       = critical, serverAuth,clientAuth,codeSigning

# This is typical in keyUsage for a client certificate.
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment

# This will be displayed in Netscape's comment listbox.
nsComment			= "OpenSSL Generated Certificate"

[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.

# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
"@ -f $keyBits,$Country,$State,$city,$company,$orgUnit,$CommonName,$email
}

if(!$OpenSSLLocation) {
   ## You should be running the script from the OpenSsl folder
   $OpenSSLLocation = Split-Path $MyInvocation.MyCommand.Path
   Write-Debug "OpenSSL: $OpenSSLLocation"
}
if( Test-Path $OpenSslLocation ) {
   ## The OpenSslLoction needs to actually have OpenSsl in it ...
   $files = ls (Join-Path $OpenSSLLocation "*.[de][lx][el]") -include libeay32.dll,ssleay32.dll,OpenSSL.exe # libssl32.dll,
   if($files.count -lt 3) {
      THROW "You need to configure a location where OpenSSL can be run from"
   }
} else { THROW "You need to configure a location where OpenSSL can be run from" }

## Don't touch these
[string]$SslCnfPath = (join-path (Convert-Path $CertStorageLocation) PoshOpenSSL.config)
New-Alias OpenSsl (join-path $OpenSSLLocation OpenSSL.exe)

if( !(Test-Path $CertStorageLocation) ) {
   New-Item -type directory -path $CertStorageLocation | Push-Location
   $forceNew = $true
} else {
   Push-Location $CertStorageLocation
}

Write-Debug "SslCnfPath: $SslCnfPath"
Write-Debug "OpenSsl: $((get-alias OpenSsl).Definition)"

## Process the CSR and generate a pfx file 
if($forceNew -or (@(Test-Path "$CodeSignName.crt","$CodeSignName.pfx") -contains $false)) {

   ## Generate the private code-signing key and a certificate signing request (csr)
   if($forceNew -or (@(Test-Path "$CodeSignName.key","$CodeSignName.csr") -contains $false)) {

      ## Generate the private root CA key and convert it into a self-signed certificate (crt)
      if($forceNew -or (@(Test-Path "$RootCAName.key","$RootCAName.crt") -contains $false)) {

         ## Change configuration before -batch processing root key
         $CommonName = "$company Certificate Authority"
         $orgUnit = "$department Certificate Authority"
         $email = Get-UserEmail
         Set-Content $SslCnfPath (Get-SslConfig $keyBits $Country $State $city $company $orgUnit $CommonName $email) ## My special config file

         OpenSsl genrsa -out "$RootCAName.key" -des3 -passout pass:$(Get-RootCAPassword) $keyBits
         OpenSsl req -new -x509 -days $daysCA -key "$RootCAName.key" -out "$RootCAName.crt" -passin pass:$(Get-RootCAPassword) -config $SslCnfPath -batch
      }

      ## Change configuration before -batch processing code-signing key
      $CommonName = "$UserName"
      $orgUnit = "$department"
      $email = Get-UserEmail
      Set-Content $SslCnfPath (Get-SslConfig $keyBits $Country $State $city $company $orgUnit $CommonName $email) ## My special config file

      OpenSsl genrsa -out "$CodeSignName.key" -des3 -passout pass:$(Get-CodeSignPassword) $keyBits
      OpenSsl req -new -key "$CodeSignName.key" -out "$CodeSignName.csr" -passin pass:$(Get-CodeSignPassword) -config $SslCnfPath -batch
   }

   ## Use the root CA key to process the CSR and sign the code-signing key in one step...
   OpenSsl x509 -req -days $days -in "$CodeSignName.csr" -CA "$RootCAName.crt" -CAcreateserial -CAkey "$RootCAName.key" -out "$CodeSignName.crt" -setalias $alias -extfile $SslCnfPath -extensions code_sign -passin pass:$(Get-RootCAPassword)
   ## Combine the signed certificate and the private key into a single file and specify a new password for it ...
   OpenSsl pkcs12 -export -out "$CodeSignName.pfx" -inkey "$CodeSignName.key" -in "$CodeSignName.crt" -passin pass:$(Get-CodeSignPassword) -passout pass:$script:CodeSignPassword
}

Pop-Location

if($import) {
   ## Now we need to import the certificates to the computer so we can use them...
   ## Sadly, the PowerShell Certificate Provider is read-only, so we need to do this by hand
   
   trap {
      if($_.Exception.GetBaseException() -is [UnauthorizedAccessException]) {
         write-error "Cannot import certificates as 'Root CA' or 'Trusted Publisher' except in an elevated console."
         continue
      }
   }
   
   ## In order to be able to use scripts signed by these certs
   ## The root cert that signed the code-signing certs must be loaded into the "Root" store
   $lm = new-object System.Security.Cryptography.X509certificates.X509Store "root", "LocalMachine"
   $lm.Open("ReadWrite")
   $lm.Add( (Get-PfxCertificate "$CertStorageLocation\$RootCAName.crt") )
   if($?) {
      Write-Host "Successfully imported root certificate to trusted root store" -fore green
   }
   $lm.Close()

   ## In order to avoid the "untrusted publisher" prompt
   ## The public code-signing cert must be loaded into the "TrustedPublishers" store
   $tp = new-object System.Security.Cryptography.X509certificates.X509Store "TrustedPublisher", "LocalMachine"
   $tp.Open("ReadWrite")
   $tp.Add( (Get-PfxCertificate "$CertStorageLocation\$CodeSignName.crt") )
   if($?) {
      Write-Host "Successfully imported code-signing certificate to trusted publishers store" -fore green
   }
   $tp.Close()

   if($importall) {
      ## It's a good practice to go ahead and put our private certificates in "OUR" store too
      ### Otherwise we have to load it each time from the pfx file using Get-PfxCertificate
      ##### $cert = Get-PfxCertificate "$CodeSignName.pfx"
      ##### Set-AuthenticodeSignature -Cert $cert -File Test-Script.ps1
      $my = new-object System.Security.Cryptography.X509certificates.X509Store "My", "CurrentUser"
      $my.Open( "ReadWrite" )
      Get-CodeSignPassword
      $my.Add((Get-PfxCertificate "$CertStorageLocation\$CodeSignName.pfx"))      #$script:CodeSignPassword, $DefaultStorage)
      if($?) {
         Write-Host "Successfully imported code-signing certificate to 'my' store" -fore yellow
      }
      $my.Close()
   }
}

# SIG # Begin signature block
# MIILCQYJKoZIhvcNAQcCoIIK+jCCCvYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUunVl0UTZlvAjOS219sL9EUT4
# EE6gggbgMIIG3DCCBMSgAwIBAgIJALPpqDj9wp7xMA0GCSqGSIb3DQEBBQUAMIHj
# MQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxEjAQBgNVBAcTCVJvY2hl
# c3RlcjEhMB8GA1UEChMYaHR0cDovL0h1ZGRsZWRNYXNzZXMub3JnMSgwJgYDVQQL
# Ex9TY3JpcHRpbmcgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MTcwNQYDVQQDEy5odHRw
# Oi8vSHVkZGxlZE1hc3Nlcy5vcmcgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MScwJQYJ
# KoZIhvcNAQkBFhhKYXlrdWxASHVkZGxlZE1hc3Nlcy5vcmcwHhcNMDkwMzE1MTkx
# OTE5WhcNMTAwMzE1MTkxOTE5WjCBqzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5l
# dyBZb3JrMRIwEAYDVQQHEwlSb2NoZXN0ZXIxITAfBgNVBAoTGGh0dHA6Ly9IdWRk
# bGVkTWFzc2VzLm9yZzESMBAGA1UECxMJU2NyaXB0aW5nMRUwEwYDVQQDEwxKb2Vs
# IEJlbm5ldHQxJzAlBgkqhkiG9w0BCQEWGEpheWt1bEBIdWRkbGVkTWFzc2VzLm9y
# ZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPfqxOG9TQN+qZjZ6KfM
# +zBK0YpjeyPL/cFgiGBhiIdYWTBtkbZydFr3IiERKRsUJ0/SKFbhf0C3Bvd/neTJ
# qiZjH4D6xkrfdLlWMmmSXXqjSt48jZp+zfCAIaF8K84e9//7lMicdVFE6VcgoATZ
# /eMKQky4JvphJpzDHYPLxLJQrKd0pjDDwspjdX5RedWkzeZBG7VfBnebLWUzgnMX
# IxRQKfFCMryQDP8weceOnJjfJEf2FYmdpsEg5EKKKbuHsQCMVTxfteKdPvh1oh05
# 1GWyPsvEPh4auJUT8pAVvrdxq+/O9KW/UV01UxjRYM1vdklNw8g7mkJTrrHjSjl7
# tuugCnJjt5kN6v/OaUtRRMR68O85bSTVGOxJGCHUKlyuuTx9tnfIgy4siFYX1Ve8
# xwaAdN3haTon3UkWzncHOq3reCIVF0luwRZu7u+TnOAnz2BRlt+rcT0O73GN20Fx
# gyN2f5VGBbw1KuS7T8XZ0TFCspUdgwAcmTGuEVJKGhVcGAvNlLx+KPc5dba4qEfs
# VZ0MssC2rALC1z61qWuucb5psHYhuD2tw1SrztywuxihIirZD+1+yKE4LsjkM1zG
# fQwDO/DQJwkdByjfB2I64p6mk36OlZAFxVfRBpXSCzdzbgKpuPsbtjkb5lGvKjE1
# JFVls1SHLJ9q80jHz6yW7juBAgMBAAGjgcgwgcUwHQYDVR0OBBYEFO0wLZyg+qGH
# Z4WO8ucEGNIdU1T9MB8GA1UdIwQYMBaAFN2N42ZweJLF1mz0j70TMxePMcUHMAkG
# A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgTwMCoGA1UdJQEB/wQgMB4GCCsGAQUF
# BwMBBggrBgEFBQcDAgYIKwYBBQUHAwMwCwYDVR0PBAQDAgTwMCwGCWCGSAGG+EIB
# DQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQUF
# AAOCAgEAmKihxd6KYamLG0YLvs/unUTVJ+NW3jZP16R28PpmidY/kaBFOPhYyMl2
# bBGQABe7LA5rpHFAs0F56gYETNoFk0qREVvaoz9u18VfLb0Uwqtnq0P68L4c7p2q
# V3nKmWjeI6H7BAyFuogxmMH5TGDfiqrrVSuh1LtPbkV2Wtto0SAxP0Ndyts2J8Ha
# vu/2rt0Ic5AkyD+RblFPtzkCC/MLVwSNAiDSKGRPRrLaiGxntEzR59GRyf2vwhGg
# oAXUqcJ/CVeHCP6qdSTM39Ut3RmMZHXz5qY8bvLgNYL6MtcJAx+EeUhW497alzm1
# jInXdbikIh0d/peTSDyLbjS8CPFFtS6Z56TDGMf+ouTpEA16otcWIPA8Zfjq+7n7
# iBHjeuy7ONoJ2VDNgqn9B+ft8UWRwnJbyB85T83OAGf4vyhCPz3Kg8kWxY30Bhnp
# Fayc6zQKCpn5o5T0/a0BBHwAyMfr7Lhav+61GpzzG1KfAw58N2GV8KCPKNEd3Zdz
# y07aJadroVkW5R+35mSafKRJp5pz20GDRwZQllqGH1Y/UJFEiI0Bme9ecbl2vzNp
# JjHyl/jLVzNVrBI5Zwb0lCLsykApgNY0yrwEqaiqwcxq5nkXFDhDPQvbdulihSo0
# u33fJreCm2fFyGbTuvR61goSksAvLQhvijLAzcKqWKG+laOtYpAxggOTMIIDjwIB
# ATCB8TCB4zELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRIwEAYDVQQH
# EwlSb2NoZXN0ZXIxITAfBgNVBAoTGGh0dHA6Ly9IdWRkbGVkTWFzc2VzLm9yZzEo
# MCYGA1UECxMfU2NyaXB0aW5nIENlcnRpZmljYXRlIEF1dGhvcml0eTE3MDUGA1UE
# AxMuaHR0cDovL0h1ZGRsZWRNYXNzZXMub3JnIENlcnRpZmljYXRlIEF1dGhvcml0
# eTEnMCUGCSqGSIb3DQEJARYYSmF5a3VsQEh1ZGRsZWRNYXNzZXMub3JnAgkAs+mo
# OP3CnvEwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwIwYJKoZIhvcNAQkEMRYEFGPa+3yKeAOuG8MGktIPE98U9IQyMA0GCSqG
# SIb3DQEBAQUABIICACukiWmmkw/T3q/IukaKIIO4/jJLng9v52P60RViKwJn7TOZ
# C6Qcov2zO8/LBm8oIlY+kQil8MXqA3+5D7TGtFfYpyzoUh+Nwks1C9KAMWeRBKAL
# b3H6CVX0H5nRh9PLa2a4WxbYHM6IxCOa/Z8clH4veAZbs5Zq5mtjLV14u8PszAYM
# 4P/H0sXHMZYb9nj0vKjsZdxOlM0g6JHqUszE40tND/5dFuzdr3Tyu/aC6/j/ZFGZ
# jdyaM88kE88qAU9Bs2M18LsSUJx6GsdlXwDD4eCBRH59+QtAnQZB4HUL5KkF53DG
# J0WtRuI+wWmeMU9nNtDMQgSGJev0LVEJ2Ui+UsVA+RvWH04VCBrzlXi2TLzS9bCQ
# 5Fo/t/czCbC4m/WrXQyYNDoHtI/fXE2ctSPq2QQaDF9Bu65MuMGzWa3iFSFmq0uA
# nYivtHSlgyqhPBBmu8fspePkye7PzYoH2Gpykp17R5fBx+rQriKjTkZcGNdAGdQY
# j7SEC93e0KjtZRQA+ABxmVacmNrO6NGbMN2Zd8Pheham1T38V3aWjKvq2d94iUfh
# dgqvWhSu6zw0yE/NaJPTKnixN0j+up/Y7jSO9Cytvl4TNWJkFjDp+u0exl4s6eQ5
# cspbWHwWyYWyg7e0YaclbL7mPygvjxQDWOWgMN9cddvHCq8fiq6VPNTJqeLB
# SIG # End signature block
