Why can't cURL properly verify a certificate on Windows?

I don't know why but I did not find this information all in one place.

  1. Download the SSL-aware version of Curl, or build the SSL-aware version yourself.

  2. From http://curl.haxx.se/docs/caextract.html , Download the cacert.pem file.

  3. Place the curl.exe and the .pem file in the same directory.

  4. Rename the cacert.pem file to curl-ca-bundle.crt

  5. Re-run curl.exe !


EDIT:

There are other ways to solve the problem. this particular way relies on a cacert produced by the maker of Curl. That may not be what you want, and in particular, it may not work for cases where you have a less-than-well-known certifying authority (such as an authority known only to your corporation) for the certificate used by the SSL site. In that case, you will want to generate your own curl-ca-bundle.crt file. You can use certreq.exe and openssl.exe to export such a cert from the IE/Windows store, and then convert-to-pem-format, respectively.


I have created a PowerShell script that is capable of writing the ca-cert.crt file based on the CA certificates that are installed in your Windows certification store (CurrentUser or LocalMachine). Run the script like this:

CreateCaCert.ps1 -StoreLocation CurrentUser | Out-File -Encoding utf8 curl-ca-cert.crt

This will create the curl-ca-cert.crt file that should be stored in the same directory as curl.exe and you should be able to validate the same sites as you can in your Windows applications (note that this file can also be consumed by git).

The "official" script can be found on GitHub, but the initial version is listed here for reference:

[CmdletBinding()]
Param(
    [ValidateSet(
        [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser,
        [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)]
    [string]
    $StoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
)

$maxLineLength = 77

# Open the store
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store ([System.Security.Cryptography.X509Certificates.StoreName]::AuthRoot, $StoreLocation)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly);

# Write header
Write-Output "# Root certificates ($StoreLocation) generated at $(Get-Date)"

# Write all certificates
Foreach ($certificate in $store.Certificates)
{
    # Start with an empty line
    Write-Output ""

    # Convert the certificate to a BASE64 encoded string
    $certString = [Convert]::ToBase64String($certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert));

    # Write the actual certificate
    Write-Output "# Friendly name: $($certificate.FriendlyName)"
    Write-Output "# Issuer:        $($certificate.Issuer)"
    Write-Output "# Expiration:    $($certificate.GetExpirationDateString())"
    Write-Output "# Serial:        $($certificate.SerialNumber)"
    Write-Output "# Thumbprint:    $($certificate.Thumbprint)"
    Write-Output "-----BEGIN CERTIFICATE-----"
    For ($i = 0; $i -lt $certString.Length; $i += $maxLineLength)
    {
        Write-Output $certString.Substring($i, [Math]::Min($maxLineLength, $certString.Length - $i))
    }
    Write-Output "-----END CERTIFICATE-----"
}

Actually we had the same problem with Typheous/Ruby. The Solution was downloading the cacert.pem and save it to C:\Windows\System32 (or whereever your Windows is). After that we set a global environment variable like discribed here where the "Variable Name" must be CURL_CA_BUNDLE and the "Variable Value" the path to the file %SystemRoot%\System32\cacert.pem.

When starting a new CMD Session you can now just use Typheous/Libcurl thing to authenticate SSL Connections. I've successfully tried this with Windows 8.1.

Tags:

Windows

Curl