Skip to content
Binary file modified app/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import com.felkertech.ussenterprise.R;
Expand Down Expand Up @@ -308,6 +311,67 @@ private WifiEnterpriseConfig buildEnterpriseConfig(EnterpriseWifiConnection conn
enterpriseConfig.setEapMethod(connection.getEap());
enterpriseConfig.setPhase2Method(connection.getPhase2());
}

// Android 12+ (API 31) requires certificate configuration for EAP methods
// that use server certificates (PEAP, TLS, TTLS, UNAUTH_TLS).
// We load CA certificates and set domain suffix match and TLS version.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
try {
X509Certificate[] certs;

// For UofT, try to load bundled certificates first
if ("UofT".equalsIgnoreCase(connection.getSsid())) {
certs = loadBundledCaCertificates();
if (certs != null && certs.length > 0) {
Logd("Loaded " + certs.length + " bundled CA certificate(s) for UofT");
} else {
// Fallback to system certificates if bundled certs not found
Logd("Bundled certificates not found, using system certificates");
certs = loadSystemCaCertificates();
}
} else {
// For non-UofT networks, use system certificates
certs = loadSystemCaCertificates();
}

if (certs != null && certs.length > 0) {
enterpriseConfig.setCaCertificates(certs);
// Set domain suffix match based on the network
// For UofT, use the official domain; for others, use common TLDs
String domainSuffix;
if ("UofT".equalsIgnoreCase(connection.getSsid())) {
domainSuffix = "utoronto.ca";
} else {
// For non-UofT networks, use common enterprise TLDs
domainSuffix = "com;org;edu;net;gov;ca;uk;de;fr;au;jp;cn;in;br";
}
enterpriseConfig.setDomainSuffixMatch(domainSuffix);

// Set minimum TLS version to 1.2 as per Android 11+ requirements
// This is available from API 23+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
// Use reflection to call setMinimumTlsVersion if available
// WifiEnterpriseConfig.setMinimumTlsVersion(int version)
// where version is 2 for TLS 1.2
java.lang.reflect.Method method = WifiEnterpriseConfig.class.getMethod("setMinimumTlsVersion", int.class);
method.invoke(enterpriseConfig, 2); // 2 = TLS 1.2
Logd("Set minimum TLS version to 1.2");
} catch (Exception tlsEx) {
// Method might not be available on all devices
Logd("Could not set TLS version: " + tlsEx.getMessage());
}
}

Logd("Loaded " + certs.length + " CA certificate(s) for validation");
} else {
Logd("Warning: No CA certificates found");
}
} catch (Exception e) {
Logd("Failed to configure certificates: " + e.getMessage());
}
}

return enterpriseConfig;
}

Expand Down Expand Up @@ -461,6 +525,84 @@ private X509Certificate findCaCertificate(String name) {
return null;
}

private X509Certificate[] loadSystemCaCertificates() {
try {
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
ks.load(null);
java.util.Enumeration<String> aliases = ks.aliases();
List<X509Certificate> certificates = new ArrayList<>();

while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
if (cert != null) {
certificates.add(cert);
}
}

return certificates.toArray(new X509Certificate[0]);
} catch (Exception e) {
Log.e(TAG, "Failed to load system CA certificates", e);
Logd("Failed to load system CA certificates: " + e.getMessage());
}
return null;
}

/**
* Load CA certificates bundled with the app from res/raw directory.
* Supports loading both root and intermediate certificates.
* Certificate files should be in PEM format (.cer extension).
*
* Expected files in res/raw:
* - uoft_root_ca.cer (Required: Root CA certificate)
* - uoft_intermediate_ca.cer (Optional: Intermediate CA certificate)
*
* @return Array of X509 certificates loaded from bundled resources, or null if none found
*/
private X509Certificate[] loadBundledCaCertificates() {
List<X509Certificate> certificates = new ArrayList<>();
CertificateFactory cf;

try {
cf = CertificateFactory.getInstance("X.509");
} catch (Exception e) {
Log.e(TAG, "Failed to get CertificateFactory", e);
return null;
}

// Try to load root CA certificate
try {
InputStream rootCaStream = getResources().openRawResource(R.raw.uoft_root_ca);
BufferedInputStream bis = new BufferedInputStream(rootCaStream);
X509Certificate rootCert = (X509Certificate) cf.generateCertificate(bis);
certificates.add(rootCert);
bis.close();
rootCaStream.close();
Log.d(TAG, "Loaded root CA certificate from bundle");
} catch (Exception e) {
Log.w(TAG, "Root CA certificate not found in bundle: " + e.getMessage());
}

// Try to load intermediate CA certificate (optional)
try {
InputStream intermediateCaStream = getResources().openRawResource(R.raw.uoft_intermediate_ca);
BufferedInputStream bis = new BufferedInputStream(intermediateCaStream);
X509Certificate intermediateCert = (X509Certificate) cf.generateCertificate(bis);
certificates.add(intermediateCert);
bis.close();
intermediateCaStream.close();
Log.d(TAG, "Loaded intermediate CA certificate from bundle");
} catch (Exception e) {
Log.d(TAG, "Intermediate CA certificate not found in bundle (optional): " + e.getMessage());
}

if (certificates.isEmpty()) {
return null;
}

return certificates.toArray(new X509Certificate[0]);
}

public void printSavedWifiNetworks() {
final WifiManager wifiManager = (WifiManager) getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (wifiManager == null) {
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/raw/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Placeholder file to ensure the raw directory is tracked by git
# Place certificate files here:
# - uoft_root_ca.cer (Root CA certificate in PEM format)
# - uoft_intermediate_ca.cer (Optional: Intermediate CA certificate in PEM format)
36 changes: 36 additions & 0 deletions app/src/main/res/raw/uoft_intermediate_ca.cer
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-----BEGIN CERTIFICATE-----
MIIGTDCCBDSgAwIBAgIQLBo8dulD3d3/GRsxiQrtcTANBgkqhkiG9w0BAQwFADBf
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBgMQswCQYDVQQGEwJHQjEY
MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFB1Ymxp
YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gQ0EgT1YgUjM2MIIBojANBgkqhkiG9w0B
AQEFAAOCAY8AMIIBigKCAYEApkMtJ3R06jo0fceI0M52B7K+TyMeGcv2BQ5AVc3j
lYt76TvHIu/nNe22W/RJXX9rWUD/2GE6GF5x0V4bsY7K3IeJ8E7+KzG/TGboySfD
u+F52jqQBbY62ofhYjMeiAbLI02+FqwHeM8uIrUtcX8b2RCxF358TB0NHVccAXZc
FYgZndZCeXxjuca7pJJ20LLUnXtgXcjAE1vY4WvbReW0W6mkeZyNGdmpTcFs5Y+s
yy6LtE5Zocji9J9NlNnReox2RWVyEXpA1ChZ4gqN+ZpVSIQ0HBorVFbBKyhdZyEX
gZgNSNtBRwxqwIzJePJhYd4ZUhO1vk+/uP3nwDk0p95q/j7naXNCSvESnrHPypaB
WRK066nKfPRPi9m9kIOhMdYfS8giFRTcdgL24Ycilj7ecAK9Trh0VbjwouJ4WH+x
bt47u68ZFCD/ac55I0DNHkCpaPruj6e9Rmr7K46wZDAYXuEAqB7tGG/jd6JAA+H2
O44CV98NRsU213f1kScIZntNAgMBAAGjggGBMIIBfTAfBgNVHSMEGDAWgBRWc1hk
lfmSGrASKgRieaFAFYghSTAdBgNVHQ4EFgQU42Z0u3BojSxdTg6mSo+bNyKcgpIw
DgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgIw
VAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdv
UHVibGljU2VydmVyQXV0aGVudGljYXRpb25Sb290UjQ2LmNybDCBhAYIKwYBBQUH
AQEEeDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3Rp
Z29QdWJsaWNTZXJ2ZXJBdXRoZW50aWNhdGlvblJvb3RSNDYucDdjMCMGCCsGAQUF
BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEA
BZXWDHWC3cubb/e1I1kzi8lPFiK/ZUoH09ufmVOrc5ObYH/XKkWUexSPqRkwKFKr
7r8OuG+p7VNB8rifX6uopqKAgsvZtZsq7iAFw04To6vNcxeBt1Eush3cQ4b8nbQR
MQLChgEAqwhuXp9P48T4QEBSksYav7+aFjNySsLYlPzNqVM3RNwvBdvp6vgDtGwc
xlKQZVuuNVIaoYyls8swhxDeSHKpRdxRauTLZ+pl+wGvy0pnrLEJGSz9mOEmfbod
e/XopR2NGqaHJ6bIjyxPu6UtyQGI26En7UAEozACrHz06Nx2jTAY9E6NeB6XuobE
wLK025ZRmvglcURG1BrV24tGHHTgxCe8M3oGlpUSMTKQ2dkgljZVYt+gKdFtWELZ
MuRdi+X3XsrR8LFz+aLUiDRfQqhmw3RxjIyVKvvu9UPYY1nsvxYmFnUSeM+2q1z/
iPUry+xDY9MC6+IhleKT094VKdFVp7LXH42+wvU+17lRolQ2mK2N/nBLVBwaIhib
QXw4VYKwB86Bc6eS6iqsc94KEgD/U4VsjmgfhK+Xp4NM+VYzTTa3QeV3p8xOM0cw
q1p8oZFA+OBcz3FYWpDIe5j0NWKlw9hXsTyPY/HeZUV59akskSOSRSmDfe8wJDPX
58uB9/7lud0G3x0pxQAcffP0ayKavNwDTw4UfJ34cEw=
-----END CERTIFICATE-----
32 changes: 32 additions & 0 deletions app/src/main/res/raw/uoft_root_ca.cer
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY
MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp
YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa
ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz
SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf
iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X
ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3
IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS
VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE
SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu
+Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt
8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L
HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt
zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P
AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ
YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52
gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA
Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB
JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX
DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui
TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5
dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65
LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp
0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY
QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
-----END CERTIFICATE-----
Loading