6

In my environment we host a whole lot of websites and WCF webservices in IIS (on Windows 2008 R2 and Windows 2012).

We are in the process of enabling HTTPS on these sites. This goes as follows:

  • Create a domain signed *.environment.domain certificate in IIS, using the Active Directory Certificate Authority that is available in our network.
  • Enable HTTPS on the sites, by changing the site bindings and applying the generated certificate.

Given the number of machines and sites, we would like to automate the required actions. I can apply the correct site bindings to the sites during deployment, but I have not found a solution for creating and installing a domain signed certificate in IIS yet.

I have already found out how to do this manually, using the 'Server Certificates' icon in IIS, and invoking the 'Create Domain Certificate' action there. I can supply the correct credentials, and when I specify the Certificate Authority I can create the desired certificates.

I also found that you can view wich certificates are installed in the machine using the cert:\ PowerShell drive:

cert:\LocalMachine\My> Get-ChildItem

And I have found that on Windows Server 2012 there is a New-SelfSignedCertificate PowerShell CmdLet available.

However, I cannot seem to find the required PowerShell commandos to combine all these actions on Windwos Server 2008 R2.

How do I create and install a domain signed SSL certificate in IIS, using PowerShell?

kasperd
  • 30,455
  • 17
  • 76
  • 124
oɔɯǝɹ
  • 451
  • 6
  • 19
  • What research have you done already and what have you tried? – Colyn1337 Feb 20 '15 at 15:50
  • Don't have the time to write up a proper answer right now but you *will* have to call [`netsh` at some point to update the cert binding in the HTTP driver stack](http://serverfault.com/questions/385180/how-to-assign-an-different-ssl-certificate-for-the-iis7-management-service-on-s/385200#385200) - The rest can be done from PowerShell using the classes in the .NET [`X509Certificates` namespace](https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates%28v=vs.110%29.aspx) – Mathias R. Jessen Feb 20 '15 at 17:04
  • @MathiasR.Jessen I've got a script that does just that. I wouldn't mind sharing it so someone ping me when they figure the first part. – Colyn1337 Feb 20 '15 at 19:54
  • I just noticed there is a PowerShell module for this on Codeplex, https://pspki.codeplex.com/ – oɔɯǝɹ Feb 21 '15 at 11:37
  • You don't need to request the cert on each server. Do it once, and export the pfx. In your script you can just copy the pfx to each server's file system and install it using certutil or similar. Should be much easier than requesting the cert from a CA on each server. – Trondh Feb 22 '15 at 16:45

1 Answers1

6

Try the following:

function New-DomainSignedCertificate {
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]
        $Hostname,

        [parameter(Mandatory=$true)]
        [string]
        $Organization,

        [parameter(Mandatory=$true)]
        [string]
        $OrganizationalUnit,

        [parameter(Mandatory=$true)]
        [string]
        $Locality,

        [parameter(Mandatory=$true)]
        [string]
        $State,

        [parameter(Mandatory=$true)]
        [string]
        $Country,

        [parameter(Mandatory=$true)]
        [string]
        $CertificateAuthority,

        [parameter(Mandatory=$false)]
        [string]
        $Keylength = "2048",

        [string]
        $workdir = $env:Temp
    )

    $fileBaseName = $Hostname -replace "\.", "_" 
    $fileBaseName = $fileBaseName -replace "\*", ""

    $infFile = $workdir + "\" + $fileBaseName + ".inf"
    $requestFile = $workdir + "\" + $fileBaseName + ".req"
    $CertFileOut = $workdir + "\" + $fileBaseName + ".cer"

    Try {
        Write-Verbose "Creating the certificate request information file ..."
        $inf = @"
[Version] 
Signature="`$Windows NT`$"

[NewRequest]
Subject = "CN=$Hostname, OU=$OrganizationalUnit, O=$Organization, L=$Locality, S=$State, C=$Country"
KeySpec = 1
KeyLength = $Keylength
Exportable = TRUE
FriendlyName = "$Hostname"
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "dns=$Hostname&"
"@

        $inf | Set-Content -Path $infFile

        Write-Verbose "Creating the certificate request ..."
        & certreq.exe -new "$infFile" "$requestFile"

        Write-Verbose "Submitting the certificate request to the certificate authority ..."
        & certreq.exe -submit -config "$CertificateAuthority" -attrib "CertificateTemplate:WebServer" "$requestFile" "$CertFileOut"

        if (Test-Path "$CertFileOut") {
            Write-Verbose "Installing the generated certificate ..."
            & certreq.exe -accept "$CertFileOut"
        }

    }
    Finally {
        Get-ChildItem "$workdir\$fileBaseName.*" | remove-item
    }
}

It basically just uses certreg.exe rather than native PowerShell cmdlets, (I'm not sure they exists, and if they do, there are usually not on older OSes).

The request details are in the here string, fix the subject and other settings if you need to. You may want to move more values up into the parameters section.

I create an inf file for the new request and convert it into a request file.

Then I submit the request to the CA specified in $CAName, if the executing user is a domain admin, the request is issued right away.

Finally I complete the request with -accept and do some cleanup.

I usually also export the new certificate into a PFX file.

for the IIS binding and certificate assignment you can use something like this:

 New-WebBinding -Name www.test.local   -Port 443 -Protocol https
 $thumb = (Get-ChildItem cert:\LocalMachine\MY | where-object { $_.FriendlyName -like "www.test.local" } | Select-Object -First 1).Thumbprint
 $guid = [guid]::NewGuid()
 & netsh http add sslcert hostnameport=www.test.local:443 certhash=$thumb appid=`{$guid`} certstorename=MY
Mike
  • 1,271
  • 5
  • 18
  • 31
Peter Hahndorf
  • 14,058
  • 3
  • 41
  • 58
  • I've edited in my version of the script after testing it. Hope you like it. One of the improvements i made is that it now supports wild card hostnames (*.environment.local) as well. – oɔɯǝɹ Feb 21 '15 at 11:46