Post

Icon iOS HTTPS Quick Reference

Quick reference guide for developers and users working with HTTPS and self-signed certificates in the Krill iOS app

iOS HTTPS Quick Reference

For iOS Users

How to Trust Self-Signed Certificates

Fastest Method (Testing)

  1. Open Safari on your iPhone/iPad
  2. Go to https://your-server-address
  3. Tap “Show Details” → “Visit this website”
  4. Launch the app and connect

Proper Method (Production)

  1. Get the .crt certificate file (email/AirDrop)
  2. Tap to install the profile
  3. Settings → General → VPN & Device Management → Install
  4. Settings → General → About → Certificate Trust Settings
  5. Enable trust for your certificate

For Developers

Using the iOS HTTP Client

1
2
3
4
5
6
7
8
9
// Fetch and store certificate
val url = Url("https://192.168.1.100:8443")
val success = trustHttpClient.fetchPeerCert(url)

// Certificate is stored, but user must manually trust it
// Check logs for detailed instructions

// Make HTTPS request (after user trusts cert)
val response = httpClient.get("https://192.168.1.100:8443/api/data")

Quick Test Checklist

  • Certificate fetched and stored (check logs)
  • User instructions displayed in logs
  • Certificate installed via Settings
  • Trust enabled in Certificate Trust Settings
  • App restarted after enabling trust
  • HTTPS connection succeeds

Common Issues

Q: Connection fails after fetching certificate
A: User must manually trust it via Settings → Certificate Trust Settings

Q: Still fails after enabling trust
A: Restart the app, verify hostname matches certificate

Q: “Certificate not trusted” in logs
A: Check Settings → VPN & Device Management → verify profile is installed

Q: Works in Safari but not in app
A: Safari’s temporary trust doesn’t apply to apps, install profile properly

Key Differences from Android/JVM

1
2
3
4
5
6
7
8
9
10
// ❌ iOS CANNOT do this (no programmatic trust)
val keyStore = KeyStore.getInstance()
keyStore.setCertificateEntry("cert", cert) // Not possible on iOS

// ✅ iOS CAN do this (system trust)
handleChallenge { _, _, challenge, completionHandler ->
    if (certificateIsInSystemTrustStore) {
        completionHandler(UseCredential, credential)
    }
}

Files to Check

Development Tips

  1. Test with Simulator: Use Xcode simulator for initial testing
  2. Check Console.app: Connect device and view detailed networking logs
  3. Use Safari First: Easier to see certificate details and errors
  4. Test on Real Device: Simulator may behave differently for certificates
  5. Development Signing: Dev builds may have relaxed certificate validation

Production Considerations

  • ⚠️ App Store: Self-signed certificates won’t work for App Store apps
  • Enterprise: Use MDM to pre-install certificates
  • TestFlight: Document installation steps for beta testers
  • 🔒 Security: Use proper CA-signed certificates for production

Platform Support Matrix

PlatformAuto TrustUser ActionStatus
JVM✅ YesNone✅ Complete
Android✅ YesNone✅ Complete
iOS❌ NoInstall via Settings✅ Complete
WASMN/ATrust in Browser✅ Complete

Code Examples

Certificate Fetching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// In your connection logic
suspend fun connectToServer(hostUrl: String) {
    val url = Url(hostUrl)
    
    // First, fetch and store the certificate
    val certFetched = trustHttpClient.fetchPeerCert(url)
    
    if (certFetched) {
        println("Certificate stored successfully")
        println("User must now trust it via iOS Settings")
        // Show UI instructions to user
    } else {
        println("Failed to fetch certificate")
        // Handle error
    }
}

Making HTTPS Requests

1
2
3
4
5
6
7
8
9
10
11
// After user has trusted the certificate
suspend fun fetchData() {
    try {
        val response: HttpResponse = httpClient.get("https://192.168.1.100:8443/api/data")
        val data: String = response.bodyAsText()
        println("Success: $data")
    } catch (e: Exception) {
        println("Connection failed: ${e.message}")
        // May indicate certificate not trusted yet
    }
}

Checking Certificate Storage

1
2
3
4
5
fun isCertificateStored(hostname: String): Boolean {
    val certKey = "krill_trusted_cert_$hostname"
    val cert = NSUserDefaults.standardUserDefaults.dataForKey(certKey)
    return cert != null
}

Troubleshooting Flow

1
2
3
4
5
6
7
8
9
10
Connection Fails
    ↓
Is certificate stored?
    ├─ No → Run trustHttpClient.fetchPeerCert(url)
    └─ Yes → Is certificate trusted in iOS Settings?
        ├─ No → Guide user to Settings → Certificate Trust Settings
        └─ Yes → Check hostname matches certificate
            ├─ No → Fetch correct certificate
            └─ Yes → Check network connectivity
                └─ Still failing? → Check Console.app logs

Next Steps

  1. First Time Setup: Fetch certificate with trustHttpClient.fetchPeerCert()
  2. Trust Certificate: Follow instructions in device Settings
  3. Verify Connection: Make test HTTPS request
  4. Production Deploy: Use properly signed certificates

Need More Help?

This post is licensed under CC BY 4.0 by the author.