Self-Signed Certificates for Win10

on Friday, November 24, 2017

Browsers have implemented all sorts of great new security measures to ensure that certificates are pretty valid. So, using a self-signed certificate today is more difficult than it used to be. Also, IIS for Win8/10 gained access for using a Central Certificate Store. So, here’s some scripts that:

  • Create a Self-Signed Cert
    • Creates a self-signed cert with a DNS Name (browsers don’t like it when the Subject Alternative Name doesn’t list the DNS Name).
    • Creates a Shared SSL folder on disk and adds permissions for IIS’s Central Certificate Store account will read the certs with.
    • Exports the cert to the Shared SSL folder as a .pfx.
    • Reimports the certs to the machines Trusted Root Authority (needed for browsers to verify the cert is trusted)
    • Adds the 443/SSL binding to the site (if it exists) in IIS
  • Re-Add Cert to Trusted Root Authority
    • Before Win10, Microsoft implemented a background task which will periodically check the certs installed in your Machine Trusted Root Authority which are self-signed and removes them. So, this script re-installs them.
    • It will look through the shared SSL folder created in the previous script and add any certs back to the local Machine Trusted Root Authority that are missing.
  • Re-Add Cert to Trusted Root Authority Scheduled Task
    • Schedules the script to run hourly
### Create-SelfSignedCert.ps1

$name = "site.name.com" # only need to edit this


# get the shared ssl password for dev - this will be applied to the cert
$pfxPassword = "your pfx password"

# you can only create a self-signed cert in the \My store
$certLoc = "Cert:\LocalMachine\My"
$cert = New-SelfSignedCertificate `
            -FriendlyName $name `
            -KeyAlgorithm RSA `
            -KeyLength 4096 `
            -CertStoreLocation $certLoc `
            -DnsName $name

# ensure the path the directory for the central certificate store is setup with permissions
# NOTE: This assumes that IIS is already setup with Central Cert Store, where
#       1) The user account is "Domain\AccountName"
#       2) The $pfxPassword Certificate Private Key Password
$sharedPath = "D:\AllContent\SharedSSL\Local"
if((Test-Path $sharedPath) -eq $false) {
    mkdir $sharedPath

    $acl = Get-Acl $sharedPath
    $objUser = New-Object System.Security.Principal.NTAccount("Domain\AccountName") 
	$rule = New-Object System.Security.AccessControl.FileSystemAccessRule($objUser, "ReadAndExecute,ListDirectory", "ContainerInherit, ObjectInherit", "None", "Allow")
	$acl.AddAccessRule($rule)
	Set-Acl $sharedPath $acl
}


# export from the \My store to the Central Cert Store on disk
$thumbprint = $cert.Thumbprint
$certPath = "$certLoc\$thumbprint"
$pfxPath = "$sharedPath\$name.pfx"
if(Test-Path $pfxPath) { del $pfxPath }
Export-PfxCertificate `
    -Cert $certPath `
    -FilePath $pfxPath `
    -Password $pfxPassword


# reimport the cert into the Trusted Root Authorities
$authRootLoc = "Cert:\LocalMachine\AuthRoot"
Import-PfxCertificate `
    -FilePath $pfxPath `
    -CertStoreLocation $authRootLoc `
    -Password $pfxPassword `
    -Exportable


# delete it from the \My store
del $certPath # removes from cert:\localmachine\my


# if the website doesn't have the https binding, add it
Import-Module WebAdministration

if(Test-Path "IIS:\Sites\$name") {
    $httpsBindings = Get-WebBinding -Name $name -Protocol "https"
    $found = $httpsBindings |? { $_.bindingInformation -eq "*:443:$name" -and $_.sslFlags -eq 3 }
    if($found -eq $null) {
        New-WebBinding -Name $name -Protocol "https" -Port 443 -IPAddress "*" -HostHeader $name -SslFlags 3
    }
}
### Add-SslCertsToAuthRoot.ps1

$Error.Clear()

Import-Module PowerShellLogging
$name = "Add-SslCertsToAuthRoot"
$start = [DateTime]::Now
$startFormatted = $start.ToString("yyyyMMddHHmmss")
$logdir = "E:\Logs\Scripts\IIS\$name"
$logpath = "$logdir\$name-log-$startFormatted.txt"
$log = Enable-LogFile $logpath

try {

    #### FUNCTIONS - START ####
    Function Get-X509Certificate {
	Param (
        [Parameter(Mandatory=$True)]
		[ValidateScript({Test-Path $_})]
		[String]$PfxFile,
		[Parameter(Mandatory=$True)]
		[string]$PfxPassword=$null
	)

	    # Create new, empty X509 Certificate (v2) object
	    $X509Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2

	    # Call class import method using password
        try {
			$X509Certificate.Import($PfxFile,$PfxPassword,"PersistKeySet")
			Write-Verbose "Successfully accessed Pfx certificate $PfxFile."
		} catch {
			Write-Warning "Error processing $PfxFile. Please check the Pfx certificate password."
			Return $false
		}
	
        Return $X509Certificate
    }

    # http://www.orcsweb.com/blog/james/powershell-ing-on-windows-server-how-to-import-certificates-using-powershell/
    Function Import-PfxCertificate {
    Param(
	    [Parameter(Mandatory = $true)]
	    [String]$CertPath,
	    [ValidateSet("CurrentUser","LocalMachine")]
	    [String]$CertRootStore = "LocalMachine",
	    [String]$CertStore = "My",
	    $PfxPass = $null
    )
        Process {
	        $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2
	        if ($pfxPass -eq $null) {$pfxPass = read-host "Enter the pfx password" -assecurestring}
	        $pfx.import($certPath,$pfxPass,"Exportable,PersistKeySet")
 
	        $store = new-object System.Security.Cryptography.X509Certificates.X509Store($certStore,$certRootStore)

	        $serverName = [System.Net.Dns]::GetHostName();
	        Write-Warning ("Adding certificate " + $pfx.FriendlyName + " to $CertRootStore/$CertStore on $serverName. Thumbprint = " + $pfx.Thumbprint)
	        $store.open("MaxAllowed")
	        $store.add($pfx)
	        $store.close()
	        Write-Host ("Added certificate " + $pfx.FriendlyName + " to $CertRootStore/$CertStore on $serverName. Thumbprint = " + $pfx.Thumbprint)
        }
    }
    #### FUNCTIONS - END ####


    #### SCRIPT - START ####
    $sharedPath = "D:\AllContent\SharedSSL\Local"
    $authRootLoc = "Cert:\LocalMachine\AuthRoot"
    
    $pfxPassword = "your password" # need to set this

    $pfxs = dir $sharedPath -file -Filter *.pfx
    foreach($pfx in $pfxs) {    
        $cert = Get-X509Certificate -PfxFile $pfx.FullName -PfxPassword $pfxSecret.Password
        $certPath = "$authRootLoc\$($cert.Thumbprint)"
        if((Test-Path $certPath) -eq $false) {
            $null = Import-PfxCertificate -FilePath $pfx.FullName -CertStoreLocation $authRootLoc -Password $pfxPassword -Exportable
            Write-Host "$($cert.Subject) ($($cert.Thumbprint)) Added"
        } else {
            Write-Host "$($cert.Subject) ($($cert.Thumbprint)) Already Exists"
        }
    }
    #### SCRIPT - END ####

} finally {
    foreach($er in $Error) { $er }

    Disable-LogFile $log
}
### Install-Add-SslCertsToAuthRoot.ps1

$yourUsername = "your username" # needs local admin rights on your machine (you probably have it)
$yourPassword = "your password"

$name = "Add-SslCertsToAuthRoot"
$filename = "$name.ps1"
$fp = "D:\AllContent\Scripts\IIS\$filename"
$taskName = $name
$fp = "powershell $fp"

$found = . schtasks.exe /query /tn "$taskName" 2>null
if($found -ne $null) {
    . schtasks.exe /delete /tn "$taskName" /f
    $found = $null
}
if($found -eq $null) {
    . schtasks.exe /create /ru $yourUsername /rp $yourPassword /tn "$taskName" /sc daily /st "01:00" /tr "$fp"
    . schtasks.exe /run /tn "$taskName"
}

0 comments:

Post a Comment


Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.