@@ -221,22 +221,65 @@ extension _EasyHandle {
221221 }
222222 return
223223 } else {
224- // When no certificate file has been specified, check the default Android locations
225- // like at https://github.com/apple/swift-nio-ssl/blob/main/Sources/NIOSSL/AndroidCABundle.swift
224+ // When no certificate file has been specified, assemble all the certificate files
225+ // from the Android certificate store and writes them to a single `cacerts.pem` file
226+ //
227+ // See https://android.googlesource.com/platform/frameworks/base/+/8b192b19f264a8829eac2cfaf0b73f6fc188d933%5E%21/#F0
228+
229+ // See https://github.com/apple/swift-nio-ssl/blob/d1088ebe0789d9eea231b40741831f37ab654b61/Sources/NIOSSL/AndroidCABundle.swift#L30
226230 let certsFolders = [
227231 " /apex/com.android.conscrypt/cacerts " , // >= Android14
228232 " /system/etc/security/cacerts " // < Android14
229233 ]
230234
235+ let aggregateCertPath = NSTemporaryDirectory ( ) + " /cacerts- \( UUID ( ) . uuidString) .pem "
236+
237+ if FileManager . default. createFile ( atPath: aggregateCertPath, contents: nil ) == false {
238+ return
239+ }
240+
241+ guard let fs = FileHandle ( forWritingAtPath: aggregateCertPath) else {
242+ return
243+ }
244+
245+ // write a header
246+ fs. write ( """
247+ ## Bundle of CA Root Certificates
248+ ## Auto-generated on \( Date ( ) )
249+ ## by aggregating certificates from: \( certsFolders)
250+
251+ """ . data ( using: . utf8) !)
252+
253+ // Go through each folder and load each certificate file (ending with ".0"),
254+ // and append them together into a single aggreagate file tha curl can load.
255+ // The .0 files will contain some extra metadata, but libcurl only cares about the
256+ // -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- sections,
257+ // so we can naïvely concatenate them all and libcurl will understand the bundle.
231258 for certsFolder in certsFolders {
232- var isDirectory : ObjCBool = false
233- if FileManager . default. fileExists ( atPath: certsFolder, isDirectory: & isDirectory) , isDirectory == true {
234- certsFolder. withCString { pathPtr in
235- try ! CFURLSession_easy_setopt_ptr ( rawHandle, CFURLSessionOptionCAPATH, UnsafeMutablePointer ( mutating: pathPtr) ) . asError ( )
259+ let certsFolderURL = URL ( fileURLWithPath: certsFolder)
260+ if ( try ? certsFolderURL. resourceValues ( forKeys: [ . isDirectoryKey] ) . isDirectory) != true { continue }
261+ let certURLs = try ! FileManager . default. contentsOfDirectory ( at: certsFolderURL, includingPropertiesForKeys: [ . isRegularFileKey, . isReadableKey] )
262+ for certURL in certURLs {
263+ // certificate files have names like "53a1b57a.0"
264+ if certURL. pathExtension != " 0 " { continue }
265+ do {
266+ if try certURL. resourceValues ( forKeys: [ . isRegularFileKey] ) . isRegularFile != true { continue }
267+ if try certURL. resourceValues ( forKeys: [ . isReadableKey] ) . isReadable != true { continue }
268+ try fs. write ( contentsOf: try Data ( contentsOf: certURL) )
269+ } catch {
270+ // ignore individual errors and soldier on…
271+ //logger.warning("bootstrapSSLCertificates: error reading certificate file \(certURL.path): \(error)")
272+ continue
236273 }
237- return
238274 }
239275 }
276+
277+ try ! fs. close ( )
278+
279+ aggregateCertPath. withCString { pathPtr in
280+ try ! CFURLSession_easy_setopt_ptr ( rawHandle, CFURLSessionOptionCAINFO, UnsafeMutablePointer ( mutating: pathPtr) ) . asError ( )
281+ }
282+ return
240283 }
241284#endif
242285
0 commit comments