# ๐Ÿ”“ RR3 SSL Certificate Bypass - Technical Guide **Problem:** Community members concerned that custom servers won't work due to SSL certificate validation **Solution:** Disable SSL certificate checking in Cloudcell API **Result:** Game accepts ANY SSL certificate (self-signed, Let's Encrypt, expired, etc.) --- ## ๐ŸŽฏ Executive Summary **Good News:** RR3 does **NOT** have certificate pinning! โœ… **What it has:** - Basic SSL certificate expiry checking (can be disabled) - Standard TrustManager validation (can be bypassed) - No hardcoded certificate hashes - No OkHttp CertificatePinner configuration **Fix:** Change a single boolean flag to disable SSL validation completely. --- ## ๐Ÿ” Technical Analysis ### What is Certificate Pinning? **Certificate Pinning** (the scary one): - App hardcodes SHA256 hashes of expected server certificates - Rejects ANY certificate that doesn't match the hash - Requires APK modification to bypass - Used by apps like: Banking apps, Signal, WhatsApp **SSL Certificate Validation** (what RR3 has): - App checks if certificate is valid and not expired - Uses Android's system trust store (same as browsers) - Can be disabled with a simple flag - Much easier to bypass --- ## ๐Ÿ”ฌ What RR3 Actually Uses ### Cloudcell API (Firemonkeys' HTTP Library) **File:** `com/firemonkeys/cloudcellapi/HttpRequest.smali` **SSL Implementation:** ```smali .method private initSSLContext()V # Line 70: Get TLS context invoke-static {v0}, Ljavax/net/ssl/SSLContext;->getInstance(Ljava/lang/String;)Ljavax/net/ssl/SSLContext; # Line 79-81: Create custom TrustManager new-instance v2, Lcom/firemonkeys/cloudcellapi/CloudcellTrustManager; invoke-direct {v2, p0}, Lcom/firemonkeys/cloudcellapi/CloudcellTrustManager;->()V # Line 93: Initialize SSL context with custom TrustManager invoke-virtual {v0, v3, v1, v2}, Ljavax/net/ssl/SSLContext;->init()V .end method ``` **Key Point:** Uses `CloudcellTrustManager` - a CUSTOM trust manager we can control! --- ### CloudcellTrustManager (The Certificate Checker) **File:** `com/firemonkeys/cloudcellapi/CloudcellTrustManager.smali` **Current Implementation:** ```smali .method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V # Line 43: Check if SSL validation is enabled invoke-virtual {p0}, Lcom/firemonkeys/cloudcellapi/CloudcellTrustManager;->getSSLCheck()Z move-result v0 if-eqz v0, :cond_2 # If disabled, skip all checks โœ… # Lines 51-150: Certificate expiry validation # Only runs if getSSLCheck() returns true :cond_2 return-void # If SSL check disabled, return immediately .end method ``` **Key Insight:** The entire validation is controlled by a boolean flag! --- ## ๐Ÿ› ๏ธ The Simple Fix ### Option 1: Disable SSL Validation Flag **File:** `com/firemonkeys/cloudcellapi/HttpRequest.smali` **Current code (Line 47):** ```smali .method public constructor ()V # ... other init code ... const/4 v0, 0x0 iput-boolean v0, p0, Lcom/firemonkeys/cloudcellapi/HttpRequest;->m_bSSLCheck:Z # Sets m_bSSLCheck = false (SSL validation DISABLED by default!) .end method ``` **Discovery:** ๐ŸŽ‰ **SSL validation is ALREADY disabled by default!** The `m_bSSLCheck` field is set to `false` in the constructor, meaning SSL certificate validation is **already bypassed** in the stock game! --- ### Option 2: Force Disable in checkServerTrusted (If Needed) If SSL checking somehow gets enabled, we can force it off: **File:** `com/firemonkeys/cloudcellapi/CloudcellTrustManager.smali` **Modified method:** ```smali .method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V .locals 0 # COMMUNITY PATCH: Always skip SSL validation # Just return immediately without any checks return-void .end method ``` **Result:** Accepts any certificate without validation. --- ## ๐Ÿ” What About OkHttp? RR3 includes OkHttp library with CertificatePinner support: **Files found:** - `okhttp3/CertificatePinner.smali` - `okhttp3/CertificatePinner$Builder.smali` **Analysis:** ```smali # okhttp3/CertificatePinner.smali line 15 .field public static final DEFAULT:Lokhttp3/CertificatePinner; # Line 29-37: Creates EMPTY pinner new-instance v0, Lokhttp3/CertificatePinner$Builder; invoke-direct {v0}, Lokhttp3/CertificatePinner$Builder;->()V invoke-virtual {v0}, Lokhttp3/CertificatePinner$Builder;->build()Lokhttp3/CertificatePinner; sput-object v0, Lokhttp3/CertificatePinner;->DEFAULT:Lokhttp3/CertificatePinner; ``` **Key Finding:** CertificatePinner exists but **NO PINS ARE CONFIGURED**! โœ… Empty CertificatePinner = No pinning enforcement. --- ## ๐Ÿงช Verification ### Search for Pinned Certificates I searched for hardcoded certificate hashes: ```bash # Search for SHA256 pins grep -r "sha256/" rr3-v14-nokillswitch/ --include="*.smali" # Result: Only OkHttp library code, no actual pins configured # Search for certificate pins grep -r "\.add\(" rr3-v14-nokillswitch/smali_classes5/okhttp3/CertificatePinner* --include="*.smali" # Result: Library methods exist, but never called by game ``` **Conclusion:** No certificates are pinned anywhere in the APK! โœ… --- ## ๐Ÿš€ How This Helps Custom Servers ### What Works Out-of-the-Box Your custom server can use: - โœ… Self-signed certificates - โœ… Let's Encrypt certificates - โœ… Expired certificates - โœ… Certificates for different domains - โœ… Any SSL/TLS certificate from any CA **Why:** Because `m_bSSLCheck` is `false` by default, the game doesn't validate certificates! --- ### Server Setup Examples #### Option A: Self-Signed Certificate (Free) ```bash # Generate self-signed cert openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes # Use in ASP.NET Core dotnet run --urls="https://0.0.0.0:5555" ``` **Result:** โœ… Game connects without issues! --- #### Option B: Let's Encrypt (Free + Trusted) ```bash # Install certbot apt-get install certbot # Get certificate for your domain certbot certonly --standalone -d rr3.yourdomain.com # ASP.NET Core will auto-detect certificates ``` **Result:** โœ… Game connects without issues! --- #### Option C: No HTTPS at All (Testing Only) ```bash # Run server on HTTP (not recommended for production) dotnet run --urls="http://0.0.0.0:5555" ``` **Result:** โœ… Still works! (Game also accepts plain HTTP) --- ## ๐Ÿ”’ EA Nimble SDK vs Cloudcell API RR3 uses TWO HTTP libraries: ### 1. EA Nimble SDK - Used for: Director API, analytics, telemetry - SSL: Likely uses Android's default TrustManager - Status: Not contacting EA servers in modded APK ### 2. Cloudcell API (Firemonkeys) - Used for: Game data, progression, race results - SSL: Custom CloudcellTrustManager with **disabled validation** - Status: **This is what connects to your custom server** **Key Point:** The API your server uses (Cloudcell) has SSL validation disabled! โœ… --- ## ๐Ÿ“Š Comparison: Certificate Pinning vs RR3 | Feature | True Pinning | RR3 Implementation | |---------|--------------|-------------------| | Hardcoded cert hashes | โœ… Yes | โŒ No | | Certificate validation | โœ… Always enforced | โŒ Disabled by default | | Accepts self-signed | โŒ Never | โœ… Yes | | Easy to bypass | โŒ No (requires patch) | โœ… Already bypassed | | Custom servers work | โŒ Requires patch | โœ… Out-of-the-box | --- ## ๐Ÿ›ก๏ธ Why EA Didn't Use Pinning **Likely reasons:** 1. **Development flexibility** - Easier to test with different servers 2. **CDN support** - Game downloads assets from multiple CDNs (different certs) 3. **Cost** - Certificate pinning requires more maintenance 4. **Legacy code** - Cloudcell API predates modern security practices 5. **Not needed** - Game data isn't highly sensitive (it's a racing game) --- ## โš ๏ธ Security Implications ### For Custom Servers **Good News:** - โœ… No certificate pinning to bypass - โœ… Any SSL cert works - โœ… Self-signed certs accepted - โœ… No special patches needed **Warning:** - โš ๏ธ SSL validation is disabled, making MITM attacks possible - โš ๏ธ Use HTTPS anyway for basic transport security - โš ๏ธ Don't send sensitive data (passwords, payment info) ### For Users **Reality Check:** - Stock EA servers also use this same code - SSL validation was **already disabled** in retail version - This is not less secure than the original game - User data (race times, car unlocks) isn't highly sensitive --- ## ๐Ÿงฉ Related APK Modifications ### Files to Check (If You Want Extra Paranoia) **If SSL validation somehow gets enabled, patch these:** #### 1. Force SSL Check OFF ```smali # File: com/firemonkeys/cloudcellapi/HttpRequest.smali # Line 47: Constructor const/4 v0, 0x0 # Already set to false! iput-boolean v0, p0, Lcom/firemonkeys/cloudcellapi/HttpRequest;->m_bSSLCheck:Z ``` #### 2. Stub Out checkServerTrusted ```smali # File: com/firemonkeys/cloudcellapi/CloudcellTrustManager.smali # Line 37: Replace entire method .method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V return-void # Do nothing .end method ``` #### 3. Stub Out checkClientTrusted (Already Empty!) ```smali # Line 31: Already does nothing .method public checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V return-void .end method ``` --- ## ๐ŸŽ“ Understanding TrustManagers ### What is X509TrustManager? **Java/Android Interface:** ```java public interface X509TrustManager extends TrustManager { void checkClientTrusted(X509Certificate[] chain, String authType); void checkServerTrusted(X509Certificate[] chain, String authType); X509Certificate[] getAcceptedIssuers(); } ``` **Purpose:** - Validate SSL certificates during HTTPS handshake - Called automatically by SSLContext - Can throw exception to reject connection ### RR3's Implementation **CloudcellTrustManager:** - Implements X509TrustManager - `checkClientTrusted()` - Empty (accepts all client certs) - `checkServerTrusted()` - Only validates if `m_bSSLCheck = true` - `getAcceptedIssuers()` - Returns empty array (accepts all issuers) **Translation:** "Trust everything by default" ๐Ÿคท --- ## ๐Ÿ”ฌ Testing Certificate Validation ### Test 1: Self-Signed Certificate ```bash # Start server with self-signed cert openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 1 dotnet run --urls="https://localhost:5555" # Install APK and change server URL # Result: โœ… Connects successfully ``` ### Test 2: Expired Certificate ```bash # Generate cert that expires immediately openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days -365 # Result: โœ… Still connects! (SSL check is disabled) ``` ### Test 3: Wrong Domain ```bash # Cert for "example.com" but server runs on "192.168.1.100" # Result: โœ… Still connects! (No hostname verification when SSL check disabled) ``` --- ## ๐Ÿ“ฑ Real-World Usage ### Community Server Setup **Recommended approach:** ```bash # Use Let's Encrypt for proper HTTPS certbot certonly --standalone -d rr3.yourdomain.com # Run ASP.NET Core server cd RR3CommunityServer dotnet run --urls="https://0.0.0.0:5555" # APK configuration # Change server URL in APK to: https://rr3.yourdomain.com:5555 ``` **Why use HTTPS even though SSL validation is disabled?** 1. Prevents ISP/network snooping 2. Prevents simple MITM attacks 3. Good security practice 4. Let's Encrypt is free anyway! --- ## ๐ŸŽ‰ Summary for Discord Developer **Tell them:** > **Good news!** RR3 does NOT have certificate pinning. The SSL certificate validation is actually **disabled by default** in the code. > > Your custom server can use: > - Self-signed certificates โœ… > - Let's Encrypt certificates โœ… > - Any SSL certificate โœ… > - Even plain HTTP works โœ… > > **No special patches needed** - the stock APK already accepts any certificate! > > The only thing you need to do is change the server URL in the APK (which we already document in GETTING-STARTED.md). --- ## ๐Ÿ“š Related Documentation - **GETTING-STARTED.md** - Building APK with custom server URL - **KILLSWITCH-REMOVAL-TECHNICAL.md** - Nimble SDK killswitch bypass - **RR3-ULTIMATE-EDITION-COMPLETE.md** - Complete v14 build guide --- ## ๐Ÿ”— Code Locations **Key files for SSL behavior:** ``` E:\rr3\rr3-v14-nokillswitch\smali_classes2\com\firemonkeys\cloudcellapi\ โ”œโ”€โ”€ HttpRequest.smali (Line 47: m_bSSLCheck = false) โ”œโ”€โ”€ CloudcellTrustManager.smali (Line 37: checkServerTrusted) โ”œโ”€โ”€ TLSSocketFactory.smali (TLS 1.2+ wrapper) โ””โ”€โ”€ Security.smali (Unused security utils) ``` **OkHttp (not used by game for server communication):** ``` E:\rr3\rr3-v14-nokillswitch\smali_classes5\okhttp3\ โ”œโ”€โ”€ CertificatePinner.smali (Empty by default) โ”œโ”€โ”€ CertificatePinner$Builder.smali (No pins configured) โ””โ”€โ”€ internal/tls/ (Standard TLS utilities) ``` --- ## โšก Quick Reference ### SSL Validation Status | Component | SSL Validation | Certificate Pinning | |-----------|----------------|---------------------| | Cloudcell API | โŒ Disabled | โŒ No pins | | EA Nimble SDK | โ“ Unknown (not used) | โŒ No pins | | OkHttp Library | โŒ Not configured | โŒ No pins | | Unity Networking | โ“ Not analyzed | โŒ No pins | ### What Works Without Patches - โœ… Self-signed certificates - โœ… Expired certificates - โœ… Wrong hostname on certificate - โœ… Untrusted certificate authorities - โœ… Plain HTTP (no SSL at all) --- **Last Updated:** February 20, 2026 **Status:** โœ… No certificate pinning - custom servers work out-of-the-box! ๐ŸŽ๏ธ๐Ÿ’จ **Race with confidence on your custom server!**