@@ -221,22 +221,65 @@ extension _EasyHandle {
221
221
}
222
222
return
223
223
} 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
226
230
let certsFolders = [
227
231
" /apex/com.android.conscrypt/cacerts " , // >= Android14
228
232
" /system/etc/security/cacerts " // < Android14
229
233
]
230
234
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.
231
258
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
236
273
}
237
- return
238
274
}
239
275
}
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
240
283
}
241
284
#endif
242
285
0 commit comments