[英]Can't access website from Android app after HTTPS certificate renewing
I have an Android app using web services from an external server (that I do not control).我有一个使用来自外部服务器(我无法控制)的 Web 服务的 Android 应用程序。 Recently that server failed to renew its HTTPS certificate, and was unavailable for a few hours.
最近该服务器未能更新其 HTTPS 证书,并且有几个小时不可用。 During this time interval, a few users of my app attempted to use the services, which naturally failed.
在这段时间里,我的应用程序的一些用户尝试使用这些服务,这自然失败了。 The problem is that now that the problem is fixed on the server, these users are still unable to access the website from my app.
问题是现在服务器上解决了问题,这些用户仍然无法从我的应用程序访问该网站。 One user can't even access the website from his mobile device's browser, another one is only blocked when trying from my app.
一位用户甚至无法从其移动设备的浏览器访问该网站,另一位用户仅在使用我的应用程序尝试时才被阻止。
I have limited experience with HTTPS certificates renewing, so I'd like to know what could be wrong?我在 HTTPS 证书更新方面的经验有限,所以我想知道可能出了什么问题? It seems like these devices have kept in cache the expired certificate, and do not take the new one.
似乎这些设备将过期的证书保存在缓存中,而不是使用新的证书。 Reinstalling my app doesn't fix the problem.
重新安装我的应用程序并不能解决问题。
Thanks谢谢
I finally found a solution, thanks to Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error .我终于找到了解决方案,感谢Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, 通常是协议错误。
First download the HTTPS certificate of the problematic website (I used Firefox to do it) and put it in your assets folder.首先下载有问题的网站的HTTPS证书(我是用Firefox做的),放到你的assets文件夹中。 Then extend Application, and add the following:
然后扩展应用程序,并添加以下内容:
public class MyApplication extends Application {
private static SSLSocketFactory _sslSocketFactory = null;
@Override
public void onCreate() {
super.onCreate();
installSslIfNeeded();
loadSslSocketFactoryIfNeeded();
}
@Nullable
public static SSLSocketFactory getSslSocketFactory() {
return _sslSocketFactory;
}
private void installSslIfNeeded() {
// Install SSL certificates if needed:
// See: https://stackoverflow.com/questions/29916962/javax-net-ssl-sslhandshakeexception-javax-net-ssl-sslprotocolexception-ssl-han
try {
ProviderInstaller.installIfNeeded(this);
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
sslContext.createSSLEngine();
}
catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException | NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); }
}
private void loadSslSocketFactoryIfNeeded() {
// Create a static SSL factory trusting the server's HTTPS certificate whose authority
// is unknown for Android < 5
// https://developer.android.com/training/articles/security-ssl
if (_sslSocketFactory == null) {
try {
// Load certificate:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getAssets().open("theserver.crt");
Certificate ca;
//noinspection TryFinallyCanBeTryWithResources
try { ca = cf.generateCertificate(caInput); }
finally { caInput.close(); }
// Create a KeyStore containing our trusted CAs
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
_sslSocketFactory = sslContext.getSocketFactory();
}
catch (CertificateException e) { e.printStackTrace(); }
catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
catch (KeyStoreException e) { e.printStackTrace(); }
catch (IOException e) { e.printStackTrace(); }
catch (KeyManagementException e) { e.printStackTrace(); }
}
}
}
Now, if you want for instance to download a JSON file from a REST API, you can do it this way:现在,如果你想从 REST API 下载一个 JSON 文件,你可以这样做:
static JSONObject readJsonFromUrl(String urlString) throws IOException, JSONException {
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL(urlString);
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(MyApplication.getSslSocketFactory());
return readJsonFromInputStream(urlConnection.getInputStream());
}
If you're using a webview, you need to do the following:如果您使用的是 webview,则需要执行以下操作:
_webview.setWebViewClient(new WebViewClient() {
[...]
@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
final AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
builder.setMessage(R.string.my_ssl_error_message);
builder.setPositiveButton(R.string.common_continue, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
handler.proceed();
}
});
builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
handler.cancel();
}
});
builder.show();
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.