FairEmail/app/src/main/java/eu/faircode/email/SSLHelper.java

192 lines
7.4 KiB
Java
Raw Normal View History

2023-12-08 07:01:25 +00:00
package eu.faircode.email;
2023-12-08 09:14:01 +00:00
import android.text.TextUtils;
2023-12-08 07:01:25 +00:00
import androidx.annotation.NonNull;
2023-12-30 13:22:24 +00:00
import com.appmattus.certificatetransparency.CTLogger;
import com.appmattus.certificatetransparency.CTTrustManagerBuilder;
import com.appmattus.certificatetransparency.VerificationResult;
2023-12-08 09:14:01 +00:00
import java.net.InetAddress;
import java.net.UnknownHostException;
2023-12-09 17:46:43 +00:00
import java.security.KeyStore;
2023-12-08 07:01:25 +00:00
import java.security.Principal;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
2023-12-09 17:46:43 +00:00
import java.security.cert.CertificateExpiredException;
2023-12-08 07:01:25 +00:00
import java.security.cert.X509Certificate;
2023-12-08 09:14:01 +00:00
import java.util.Arrays;
import java.util.List;
2023-12-08 07:01:25 +00:00
2023-12-09 17:46:43 +00:00
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
2023-12-08 07:01:25 +00:00
import javax.net.ssl.X509TrustManager;
public class SSLHelper {
static TrustManager[] getTrustManagers(
2023-12-30 13:22:24 +00:00
String server, boolean secure, boolean cert_strict, boolean transparency, boolean check_names, String trustedFingerprint, ITrust intf) {
2023-12-09 17:46:43 +00:00
TrustManagerFactory tmf;
try {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
} catch (Throwable ex) {
Log.e(ex);
tmf = null;
}
TrustManager[] tms = (tmf == null ? null : tmf.getTrustManagers());
Log.i("Trust managers=" + (tms == null ? null : tms.length));
if (tms == null || tms.length == 0 || !(tms[0] instanceof X509TrustManager)) {
Log.e("Missing root trust manager");
return tms;
}
if (tms.length > 1)
for (TrustManager tm : tms)
Log.e("Trust manager " + tm.getClass());
2023-12-30 13:22:24 +00:00
CTLogger logger = new CTLogger() {
@Override
public void log(@NonNull String host, @NonNull VerificationResult result) {
Log.w("Transparency: host=" + host + " result=" + result);
}
};
final X509TrustManager rtm = (transparency
? new CTTrustManagerBuilder((X509TrustManager) tms[0]).setLogger(logger).build()
: (X509TrustManager) tms[0]);
2023-12-09 17:46:43 +00:00
return new TrustManager[]{new X509TrustManager() {
2023-12-08 07:01:25 +00:00
// openssl s_client -connect <host>
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (secure)
rtm.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (intf != null)
intf.checkServerTrusted(chain);
if (secure) {
// Check if selected fingerprint
if (trustedFingerprint != null && matches(chain[0], trustedFingerprint)) {
Log.i("Trusted selected fingerprint");
return;
}
// Check certificates
try {
Log.i("Auth type=" + authType);
rtm.checkServerTrusted(chain, authType);
} catch (CertificateException ex) {
Principal principal = chain[0].getSubjectDN();
if (principal == null)
throw ex;
else if (cert_strict)
throw new CertificateException(principal.getName(), ex);
else if (noAnchor(ex) || isExpired(ex)) {
if (BuildConfig.PLAY_STORE_RELEASE)
Log.i(ex);
else
Log.w(ex);
} else
throw new CertificateException(principal.getName(), ex);
}
2023-12-08 09:14:01 +00:00
// Check host name
if (check_names) {
List<String> names = EntityCertificate.getDnsNames(chain[0]);
if (EntityCertificate.matches(server, names))
return;
// Fallback: check server/certificate IP address
if (!cert_strict)
try {
InetAddress ip = InetAddress.getByName(server);
Log.i("Checking server ip=" + ip);
for (String name : names) {
if (name.startsWith("*."))
name = name.substring(2);
Log.i("Checking cert name=" + name);
try {
for (InetAddress addr : InetAddress.getAllByName(name))
if (Arrays.equals(ip.getAddress(), addr.getAddress())) {
Log.i("Accepted " + name + " for " + server);
return;
}
} catch (UnknownHostException ex) {
Log.w(ex);
}
2023-12-08 09:14:01 +00:00
}
} catch (UnknownHostException ex) {
Log.w(ex);
} catch (Throwable ex) {
Log.e(ex);
2023-12-08 09:14:01 +00:00
}
String error = server + " not in certificate: " + TextUtils.join(",", names);
Log.i(error);
throw new CertificateException(error);
}
2023-12-08 07:01:25 +00:00
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return rtm.getAcceptedIssuers();
}
private boolean noAnchor(Throwable ex) {
while (ex != null) {
2023-12-09 17:46:43 +00:00
if (ex instanceof CertPathValidatorException)
2023-12-08 07:01:25 +00:00
return true;
ex = ex.getCause();
}
return false;
}
private boolean isExpired(Throwable ex) {
while (ex != null) {
2023-12-09 17:46:43 +00:00
if (ex instanceof CertificateExpiredException)
2023-12-08 07:01:25 +00:00
return true;
ex = ex.getCause();
}
return false;
}
2023-12-09 17:46:43 +00:00
}};
2023-12-08 07:01:25 +00:00
}
static boolean customTrustManager() {
return true;
}
2023-12-08 07:01:25 +00:00
private static boolean matches(X509Certificate certificate, @NonNull String trustedFingerprint) {
// Get certificate fingerprint
try {
String fingerprint = EntityCertificate.getFingerprintSha1(certificate);
int slash = trustedFingerprint.indexOf('/');
if (slash < 0)
return trustedFingerprint.equals(fingerprint);
else {
String keyId = EntityCertificate.getKeyId(certificate);
if (trustedFingerprint.substring(slash + 1).equals(keyId))
return true;
return trustedFingerprint.substring(0, slash).equals(fingerprint);
}
} catch (Throwable ex) {
Log.w(ex);
return false;
}
}
interface ITrust {
void checkServerTrusted(X509Certificate[] chain);
}
}