Skip to content

Commit

Permalink
Use installed Android cacerts for URLSession
Browse files Browse the repository at this point in the history
  • Loading branch information
marcprux committed Jan 24, 2025
1 parent 0129358 commit 7f52f2c
Showing 1 changed file with 58 additions and 0 deletions.
58 changes: 58 additions & 0 deletions Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,64 @@ extension _EasyHandle {
try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, caInfo).asError()
}
return
} else {
// When no certificate file has been specified, assemble all the certificate files
// from the Android certificate store and writes them to a single `cacerts.pem` file
//
// See https://android.googlesource.com/platform/frameworks/base/+/8b192b19f264a8829eac2cfaf0b73f6fc188d933%5E%21/#F0

// See https://github.com/apple/swift-nio-ssl/blob/d1088ebe0789d9eea231b40741831f37ab654b61/Sources/NIOSSL/AndroidCABundle.swift#L30
let certsFolders = [
"/apex/com.android.conscrypt/cacerts", // >= Android14
"/system/etc/security/cacerts" // < Android14
]

let aggregateCertPath = NSTemporaryDirectory() + "/cacerts-\(UUID().uuidString).pem"

if FileManager.default.createFile(atPath: aggregateCertPath, contents: nil) == false {
return
}

guard let fs = FileHandle(forWritingAtPath: aggregateCertPath) else {
return
}

// write a header
fs.write("""
## Bundle of CA Root Certificates
## Auto-generated on \(Date())
## by aggregating certificates from: \(certsFolders)
""".data(using: .utf8)!)

// Go through each folder and load each certificate file (ending with ".0"),
// and append them together into a single aggreagate file tha curl can load.
// The .0 files will contain some extra metadata, but libcurl only cares about the
// -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- sections,
// so we can naïvely concatenate them all and libcurl will understand the bundle.
for certsFolder in certsFolders {
let certsFolderURL = URL(fileURLWithPath: certsFolder)
if (try? certsFolderURL.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) != true { continue }
let certURLs = try! FileManager.default.contentsOfDirectory(at: certsFolderURL, includingPropertiesForKeys: [.isRegularFileKey, .isReadableKey])
for certURL in certURLs {
// certificate files have names like "53a1b57a.0"
if certURL.pathExtension != "0" { continue }
do {
if try certURL.resourceValues(forKeys: [.isRegularFileKey]).isRegularFile != true { continue }
if try certURL.resourceValues(forKeys: [.isReadableKey]).isReadable != true { continue }
try fs.write(contentsOf: try Data(contentsOf: certURL))
} catch {
// ignore individual errors and soldier on…
//logger.warning("bootstrapSSLCertificates: error reading certificate file \(certURL.path): \(error)")
continue
}
}
}

try! fs.close()

try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, aggregateCertPath).asError()
return
}
#endif

Expand Down

0 comments on commit 7f52f2c

Please sign in to comment.