New-CodeSigningCert (modification of post by view diff)
embed code: <script type="text/javascript" src="http://PoshCode.org/embed/1049"></script>download | new post
Generates a self-signed certificate authority and a code-signing certificate using OpenSSL
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
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
PowerShell Code Repository