diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 347e308eb0..95d8e6d2ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -140,13 +140,6 @@ jobs: if: runner.os == 'Windows' run: vcpkg install pcre sqlite3 - - name: Download CA certificates (Windows) - if: runner.os == 'Windows' - run: | - $binPath = Join-Path $PWD "vcpkg" "installed" "x64-mingw-dynamic-release" "bin" - Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile (Join-Path $binPath "cacert.pem") - shell: pwsh - - name: Set Xcode version (macOS M1) if: runner.os == 'macOS' && runner.arch == 'ARM64' uses: maxim-lobanov/setup-xcode@v1 diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 13ac4b796e..da22913e8d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -29,11 +29,15 @@ ## ============== ## ## On Windows the SSL library checks for valid certificates. -## It uses the `cacert.pem` file for this purpose which was extracted -## from `https://curl.se/ca/cacert.pem`. Besides -## the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you -## also need to ship `cacert.pem` with your `.exe` file. ## +## It uses a Certificate Authority (CA) bundle and/or the Windows Root +## Certificate store (only with OpenSSL >= 3.2.0) for this purpose. +## +## The CA bundle should be named `cacert.pem` and placed next to the +## program `.exe` or in `PATH` and can be obtained from +## `https://curl.se/ca/cacert.pem`. This bundle will be loaded +## regardless of whether the Windows Certificate Root store is used, +## but is only required when the Root Certificate store can not be used. ## ## Examples ## ======== @@ -668,7 +672,18 @@ when defineSsl: else: # Scan for certs in known locations. For CVerifyPeerUseEnvVars also scan # the SSL_CERT_FILE and SSL_CERT_DIR env vars - var found = false + var found = + when not defined(windows) or defined(openssl111): + false + else: + # Try loading the root certificate store on Windows. + # + # Note that we will still load cacert.pem after this, just that + # it won't be considered an error if we couldn't. This is because + # OpenSSL won't raise an error here if the store doesn't exist. + OpenSSL_version_num() >= 0x30200000 and + newCTX.SSL_CTX_load_verify_store("org.openssl.winstore:") == VerifySuccess + let useEnvVars = (if verifyMode == CVerifyPeerUseEnvVars: true else: false) for fn in scanSSLCertificates(useEnvVars = useEnvVars): if newCTX.SSL_CTX_load_verify_locations(fn, nil) == VerifySuccess: diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index f24b3b2f91..c812d49c8b 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -140,27 +140,3 @@ iterator scanSSLCertificates*(useEnvVars = false): string = defer: free(paths) for i in 0 ..< size: yield $paths[i] - -# Certificates management on windows -# when defined(windows) or defined(nimdoc): -# -# import std/openssl -# -# type -# PCCertContext {.final, pure.} = pointer -# X509 {.final, pure.} = pointer -# CertStore {.final, pure.} = pointer -# -# # OpenSSL cert store -# -# {.push stdcall, dynlib: "kernel32", importc.} -# -# proc CertOpenSystemStore*(hprov: pointer=nil, szSubsystemProtocol: cstring): CertStore -# -# proc CertEnumCertificatesInStore*(hCertStore: CertStore, pPrevCertContext: PCCertContext): pointer -# -# proc CertFreeCertificateContext*(pContext: PCCertContext): bool -# -# proc CertCloseStore*(hCertStore:CertStore, flags:cint): bool -# -# {.pop.} diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index ff68d3d3ce..19faa48c62 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -245,6 +245,8 @@ const when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): when not defined(openssl111): proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc: "SSL_get1_peer_certificate".} + + proc SSL_CTX_load_verify_store*(ssl: SslCtx, CAstore: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.} else: proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc: "SSL_get_peer_certificate".} else: @@ -290,6 +292,13 @@ else: ) if not thisProc.isNil: result = thisProc(ssl) + proc SSL_CTX_load_verify_store*(ssl: SslCtx, CAstore: cstring): cint {.gcsafe, tags: [].} = + {.cast(tags: []), cast(gcsafe).}: + var thisProc {.global.}: proc (ssl: SslCtx, CAstore: cstring): cint {.cdecl.} + if thisProc.isNil: + thisProc = cast[typeof(thisProc)](sslSymThrows("SSL_CTX_load_verify_store")) + result = thisProc(ssl, CAstore) + proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} proc OpenSSL_version_num*(): culong {.cdecl, dynlib: DLLUtilName, importc.}