更新 HTTPS 证书后无法从 Android 应用程序访问网站
Can't access website from Android app after HTTPS certificate renewing
我有一个 Android 应用程序使用来自外部服务器(我无法控制)的 Web 服务。最近该服务器未能更新其 HTTPS 证书,并且有几个小时不可用。在此期间,我的应用程序的一些用户尝试使用这些服务,但自然失败了。
问题是现在服务器上的问题已经解决,这些用户仍然无法从我的应用程序访问该网站。一位用户甚至无法通过其移动设备的浏览器访问该网站,另一位用户仅在通过我的应用程序尝试时才被阻止。
我对 HTTPS 证书续订的经验有限,所以我想知道可能出了什么问题?这些设备似乎将过期证书保存在缓存中,并且不接受新证书。重新安装我的应用程序无法解决问题。
谢谢
感谢,我终于找到了解决方案。
首先下载问题网站的HTTPS证书(我是用Firefox做的)放到你的assets文件夹下。然后扩展应用程序,并添加以下内容:
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:
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(); }
}
}
}
现在,如果您想从 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());
}
如果您使用的是网络视图,则需要执行以下操作:
_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();
}
});
我有一个 Android 应用程序使用来自外部服务器(我无法控制)的 Web 服务。最近该服务器未能更新其 HTTPS 证书,并且有几个小时不可用。在此期间,我的应用程序的一些用户尝试使用这些服务,但自然失败了。 问题是现在服务器上的问题已经解决,这些用户仍然无法从我的应用程序访问该网站。一位用户甚至无法通过其移动设备的浏览器访问该网站,另一位用户仅在通过我的应用程序尝试时才被阻止。
我对 HTTPS 证书续订的经验有限,所以我想知道可能出了什么问题?这些设备似乎将过期证书保存在缓存中,并且不接受新证书。重新安装我的应用程序无法解决问题。
谢谢
感谢
首先下载问题网站的HTTPS证书(我是用Firefox做的)放到你的assets文件夹下。然后扩展应用程序,并添加以下内容:
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:
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(); }
}
}
}
现在,如果您想从 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());
}
如果您使用的是网络视图,则需要执行以下操作:
_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();
}
});