Powershell: StartSSL StartAPI für das automatisierte Validieren und Anfordern von SSL-Zertifikaten verwenden
WICHTIGER HINWEIS:
StartCom und WoSign Zertifikate werden seit einiger Zeit von den großen Betriebsystemen und Browsern nicht mehr als Vertrauenswürdig eingestuft. Bitte beachten sie dies bei der Verwendung dieser Anleitung!
Mozilla Security Blog - Distrusting New WoSign and StartCom CertificatesMicrosoft to remove WoSign and StartCom certificates in Windows 10 (8. August 2017)
Neuerdings stellt auch StartCom (http://www.startssl.com) ein Entwickler-API bereit um kostenlose Zertifikate automatisiert abzurufen.
Bisher war es dort ja bei kostenlosen Zertifikaten nur mit regelmäßigem manuellem Aufwand zu bewerkstelligen.
Also habe ich mal ein Powershell-Skript geschrieben das das Validieren einer Domain und das anschließende Abrufen eines kostenlosen Zertifikates ermöglicht. Enthalten ist die Validierung der Domain über den Upload einer HTML Datei auf den Webserver der Domain welche den nötigen Validierungscode von StartSSL enthält.
Es folgt nun eine Beschreibung der Vorgehensweise für das Anfordern eines kostenlosen DV-Zertifikats für eine Domain (max. 5 SAN kein Wildcard).
WICHTIG: Bitte alles lesen. Voreingestellt ist das Test-API die Zertifikate sind also nur 1 Tag gültig. Zur Umstellung auf das Produktiv-API siehe die Variablen-Tabelle ($global:APIURL)
Anfordern eines API-Authentifizierungszertifikats
Zuerst sollte man eine neue E-Mail Validierung wie üblich vornehmen (Hinweis: Es darf nicht die E-Mail sein die für den StartSSL-Account verwendet wird)Ist das erledigt, folgt das Anfordern der API-Authentifizierungsdaten:
Neues API-Zertifikat generieren und herunterladen:
Das *.p12 Zertifikat speichert man sich im Verzeichnis in dem später das PS-Skript liegt.
Ebenfalls kopiert man sich die API-Token ID und fügt sie im Skript im Variablenbereich ein
Anpassungen des Skripts
Im Skript-Header befinden sich ein Variablenbereich für die Anpassung des Skripts.Es folgt nun eine Tabelle mit Erläuterungen zu den anpassbaren Variablen:
Variable | Bedeutung |
---|---|
$global:domains | Hier werden die FQDNs mit Komma getrennt für das Zertifkat hinterlegt. Die erste Domain wird der CN (Common Name) des Zertifikates |
$global:api_token | Das API Token welches wir aus dem Account von StartSSL kopiert haben |
$global:auth_cert_path | Der Pfad und Name des API-Authentifizierungs-Zertifikats (Default ist hier im Pfad des PS-Skripts) |
$global:auth_cert_password | Das Passwort des API-Authentifizierungs-Zertifikats |
$global:outpath | Der Pfad in dem die heruntergeladenen Zertifikate abgelegt werden |
$global:ftp_path | Die komplette URL auf dem FTP-Server welches der ersten Domain in der Variablen $global:domains entpricht. Dies wird benötigt wenn das Skript die Validierung der Domain übernehmen soll. Ist das nicht gewollt muss die Validierung manuell im Account von StartSSL vorgenommen werden. ACHTUNG. Validierungen sind meist immer nur für 30 Tage gültig! |
$global:ftp_username und $global:ftp_username | Username und Passwort für den FTP-Account |
$global:csr | Der CSR (Certificate Signing Request) für das anzufordernde Zertifikat. Diesen Request erstellt man sich mit den gängigen Tools und fügt den CSR ins Skript ein. |
$global:APIURL | Die API-URL. Voreingestellt ist die Test-API URL damit Ihr das Skript bei euch erst einmal testen könnt. Läuft es dann so wie gewünscht stellt man die URL auf die Produktiv-URL 'https://api.startssl.com' |
Powershell-Skript
# =============================================================================
# StartSSL automated domain validation and certificate request / (c) @colinardo
# =============================================================================
# ==== Start Variable section =======================================================
# comma seperated list of domains to create certificate for (first gets common name CN and others SAN)
$global:domains = 'domainXY.de,sub1.domainXY.de,sub2.domainXY.de'
# Personal API-Token from startssl.com
$global:api_token = 'tk_XXXXXXXXXXXXXXXXXXXXXXXXX'
# Path to API-Authentification certificate
$global:auth_cert_path = "$(Split-Path $MyInvocation.MyCommand.Definition -Parent)\my_domain_auth_certificate.p12"
# Password of API-Authentification certificate
$global:auth_cert_password = 'VeryStrongPassword'
# Path to store downloaded certificates to (default same as script path)
$global:outpath = Split-Path $MyInvocation.MyCommand.Definition -Parent
# ---- FTP-Data -------
# FTP-Path which corresponds to CN-Domain
$global:ftp_path = 'ftp://ftp.domain.de/subpath'
# FTP-Username
$global:ftp_username = 'FTP-USERNAME'
# FTP-Password
$global:ftp_password = 'FTP-PASSWORD'
# certificate request
$global:csr = '-----BEGIN CERTIFICATE REQUEST-----
MIICpDCCAYwCAQAwFjEUMBIGA1UEAxMLcm9ldGdlbi5uZXQwggEiMA0GCSqGSIb3
....
....
MmNmcPSKaJIFF71jXT7fhmkwvwwJUlIqrGgVMvpCEnZ8dPfdPCI3+ymaGMzcsW2U
bXCudv/Uf5w=
-----END CERTIFICATE REQUEST-----'
# API URL Test-API: 'https://apitest.startssl.com' / Production-API: 'https://api.startssl.com'
$global:APIURL = 'https://apitest.startssl.com'
# <<<< End Variable section =======================================================
if ($PSVersionTable.PSVersion.Major -lt 3){write-host "ERROR: Minimum Powershell Version 3.0 is required!" -F Yellow; return}
# create x509 certificate object from authentification cert file
$global:auth_cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2($global:auth_cert_path,$global:auth_cert_password)
# >>> Start Helper functions section =============================================
function md5([string]$string){
[System.BitConverter]::ToString([System.Security.Cryptography.MD5]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($string))).replace('-','').toLower()
}
# <<<< End Helper functions section =============================================
# ------------------------------------------------------
# Fetches certificate from StartSSL
# (Returns a X509 certificate-object and saves ther certificate to defined outputfolder)
# ------------------------------------------------------
function Get-StartSSLCertificate(){
param(
[parameter(mandatory=$true)][ValidateSet('DVSSL','IVSSL','OVSSL','EVSSL')][string]$certType = 'DVSSL',
[parameter(mandatory=$true)][string]$domain,
[parameter(mandatory=$true)][string]$csr
)
$body = @{
tokenID = $global:api_token
actionType = 'ApplyCertificate'
certType = $certType
domains = $domain
CSR = $csr
} | ConvertTo-Json
$body = "RequestData=$([System.Net.WebUtility]::UrlEncode($body))"
try{
$result = irm -Uri $global:APIURL -Body $body -Method Post -Certificate $global:auth_cert -ContentType 'application/x-www-form-urlencoded'
if ($result.status -eq 1 -and $result.data.orderStatus -eq 2){
if ((md5 $result.data.certificate) -eq $result.data.certificateFieldMD5){
$outcert = "$outpath\$($domain.split(',')).crt"
write-host "Request was successfull, saving certificate to '$outcert' ..." -F Green
[System.IO.File]::WriteAllBytes($outcert,[System.Convert]::FromBase64String($result.data.certificate))
return [System.Security.Cryptography.X509Certificates.X509Certificate]::CreateFromCertFile($outcert)
}else{
throw "There was a problem in certificate transmission, the MD5 of the submitted certificate did not match!"
}
}elseif($result.status -eq 0 -and $result.errorCode -eq -1025){
# domain name is not validated, initiate validation
write-host "Domain is not validated, initiating validation process ..." -F Yellow
$val_result = Validate-StartSSLDomain -domain ($domain.split(','))
if ($val_result.status -eq 1){
Get-StartSSLCertificate -certType $certType -domain $domain -csr $csr
}else{throw $val_result}
}else{
throw $result
}
}catch{
throw $_
}
}
# ---------------------------------------------------------------
# Validate Domain ownership via html file upload to ftp of domain
# ---------------------------------------------------------------
function Validate-StartSSLDomain(){
param(
[parameter(mandatory=$true)][string]$domain
)
function Upload-File([string]$path,[string]$url,$username,$password){
try{
$request = [System.Net.FtpWebRequest]::Create($url)
$request.Method = [System.Net.WebRequestMethods+FTP]::UploadFile
$request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
[bytes[]] $bytes = [System.IO.File]::ReadAllBytes($path)
[System.IO.Stream]$stream = $request.GetRequestStream();
$stream.Write($bytes,0,$bytes.Length)
$stream.Close(); $stream.Dispose()
$response = [System.Net.FtpWebResponse]$request.GetResponse()
$result = $response.StatusDescription
$response.Close()
return $result
}catch{
throw $_.Exception.Message
return $false
}
}
function Delete-FtpFile([string]$url,$username,$password){
try{
$request = [System.Net.FtpWebRequest]::Create($url)
$request.Method = [System.Net.WebRequestMethods+FTP]::DeleteFile
$request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
$response = [System.Net.FtpWebResponse]$request.GetResponse()
$result = $response.StatusDescription
$response.Close()
return $result
}catch{
throw $_.Exception.Message
return $false
}
}
$body = @{
tokenID = $global:api_token
actionType = 'ApplyWebControl'
hostname = $domain
} | ConvertTo-Json
$body = "RequestData=$([System.Net.WebUtility]::UrlEncode($body))"
try{
# first request for domain validation
$result = irm -Uri $global:APIURL -Body $body -Method Post -Certificate $global:auth_cert -ContentType 'application/x-www-form-urlencoded'
if($result.status -eq 1){
# upload validation html file to ftp server
$result.data | Set-Content "$env:TEMP\$domain.html"
write-host "Uploading validation file to ftp server..." -F Green
Upload-File -path "$env:TEMP\$domain.html" -url "$ftpfolder/$domain.html" -username $global:ftp_username -password $global:ftp_password
# second request to initiate domain ownership validation
$body = @{
tokenID = $global:api_token
actionType = 'WebControlValidation'
hostname = $domain
} | ConvertTo-Json
$body = "RequestData=$([System.Net.WebUtility]::UrlEncode($body))"
write-host "Validating domain ownership..." -F Green
$result = irm -Uri $global:APIURL -Body $body -Method Post -Certificate $global:auth_cert -ContentType 'application/x-www-form-urlencoded'
if($result.status -eq 1){
write-host "Validation successful, removing validation file from ftp..." -ForegroundColor Green
Delete-FtpFile "$ftpfolder/$domain.html" -username $global:ftp_username -password $global:ftp_password
return $result
}else{throw $result}
}else{
throw $result
}
}catch{
throw $_
}
}
# Initiate certificate process
Get-StartSSLCertificate -certType DVSSL -domain $domains -csr $global:csr
Wichtige Hinweise
Voreingestellt ist das Test-API, für eure Tests. Wollt Ihr auf das Produktiv-API umstellen macht Ihr das indem Ihr die API-URL in der Variablen $global:APIURL auf$global:APIURL = 'https://api.startssl.com'
Die automatisierte Domain-Validierung funktioniert hier folgendermaßen: Es wird vom API ein Validation-Code angefordert der dann in einem HTML-File auf den FTP-Server der primären Domain hochgeladen wird. Anschließend wird die Validierung per API angestoßen. Dabei ruft das API diese HTML-Datei ab und überprüft den Inhalt der Datei. Kann die Datei mit dem richtigen Code abgerufen werden ist die Validierung erfolgreich und das Zertifikat kann nun angefordert werden.
Wie immer alles ohne Gewähr auf Vollständigkeit und Fehlerfreiheit. Getestet wurde es hier mit einem kostenlosen Account bei StartSSL mit der einfachsten Klasse 1 Validierung.
Für die Einbindung des Zertfikates in eure Anwendungen seit Ihr ja frei das Skript um die entsprechenden Schritte z.B. für Exchange etc. zu erweitern.
Wünsche nun viel Spaß mit dem Skript.
Gruß @colinardo
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 307762
Url: https://administrator.de/tutorial/powershell-startssl-startapi-fuer-das-automatisierte-validieren-und-anfordern-von-ssl-zertifikaten-verwenden-307762.html
Ausgedruckt am: 31.03.2025 um 22:03 Uhr