@@ -220,6 +220,64 @@ extension _EasyHandle {
220
220
try ! CFURLSession_easy_setopt_ptr ( rawHandle, CFURLSessionOptionCAINFO, caInfo) . asError ( )
221
221
}
222
222
return
223
+ } else {
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
230
+ let certsFolders = [
231
+ " /apex/com.android.conscrypt/cacerts " , // >= Android14
232
+ " /system/etc/security/cacerts " // < Android14
233
+ ]
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.
258
+ for certsFolder in certsFolders {
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
273
+ }
274
+ }
275
+ }
276
+
277
+ try ! fs. close ( )
278
+
279
+ try ! CFURLSession_easy_setopt_ptr ( rawHandle, CFURLSessionOptionCAINFO, aggregateCertPath) . asError ( )
280
+ return
223
281
}
224
282
#endif
225
283
0 commit comments