我正在 Android 上开发一个使用 REST 网络服务的应用程序。 Web 服务采用 HTTPS。我无法提供 Web 服务的详细信息,因为它是 secret 信息。我按照以下链接中提到的步骤操作:
http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/
到目前为止,该应用程序运行良好,能够下载 Web 服务内容。上周突然连接失败并抛出“javax.net.ssl.SSLException:不可信的服务器证书”。我按照下面提到的步骤进行设置:
第 1 步:我在我的应用程序中使用了 GeoTrust 提供的根证书来启用安全通信。因此从 Geo 信任站点收集了根证书“GeoTrust_Global_CA.cer”:http://www.geotrust.com/resources/root-certificates/index.html
它是一种 Base64 编码的 X.509 格式。
第 2 步:下载 BouncyCaSTLe 提供程序并使用此创建的 keystore 。我从命令行使用 java 6 keytool 运行以下命令以生成 key 存储:
keytool -importcert -v -trustcacerts -file "G:\workspace\TestHttps\GeoTrustglobal.cer"-alias IntermediateCA -keystore "G:\workspace\TestHttps\res\raw\mykeystore.bks"-provider org.bouncycaSTLe .jce.provider.BouncyCaSTLeProvider -providerpath "G:\workspace\TestHttps\SigningProcess\bcprov-jdk16-146.jar"-storetype BKS -storepass mysecret
成功后,它会创建 myKeyStore.bks 并将其复制到我的应用程序的/res/raw 文件夹中。
然后通过运行以下命令验证此 keystore :
keytool -importcert -v -trustcacerts -file "G:\workspace\TestHttps\GeoTrustglobal.cer"-alias IntermediateCA -keystore "G:\workspace\TestHttps\res\raw\mykeystore.bks"-provider org.bouncycaSTLe .jce.provider.BouncyCaSTLeProvider -providerpath "G:\workspace\TestHttps\SigningProcess\bcprov-jdk16-146.jar"-storetype BKS -storepass mysecret
它返回了 1 个带有中间证书的结果,如下所示:
IntermediateCA,22.10.2010,trustedCertEntry,指纹 (MD5):98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
第 3 步:
现在我编写了以下从 DefaultHttpClient 扩展的代码
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mysecret".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
然后我使用以下代码调用了 DefaultHttpClient:
package com.test.https;
import java.io.IOException;
import java.net.UnknownHostException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.os.Bundle;
public class TestHttps extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
startConnect();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void startConnect() throws UnknownHostException{
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://ssltest15.bbtest.net/");
// Execute the GET call and obtain the response
HttpResponse getResponse = null;
try {
getResponse = client.execute(get);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch(UnknownHostException uhe){
uhe.printStackTrace();
throw new UnknownHostException();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String response = null;
if(getResponse != null){
HttpEntity responseEntity = getResponse.getEntity();
try {
response = EntityUtils.toString(responseEntity);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
当我执行这段代码时,它抛出以下异常:
java.security.cert.CertPathValidatorException:未找到 CertPath 的 TrustAnchor。
javax.net.ssl.SSLException: 不受信任的服务器证书
因此,我尝试使用同一个应用程序连接到各种其他 Web 服务,包括由 geotrust (https://ssltest15.bbtest.net/) 提供的服务。都抛出相同的异常。我对这个问题的猜测是,
提供 REST 网络服务的网络服务器可能在其方面进行了一些更改,这导致了此问题
我使用的 Geo Trust 证书已过期
我遵循的步骤有一些问题
具有上述代码的同一个应用程序在 1 周前工作正常,现在突然失败意味着服务器端发生了一些变化导致了这个问题。我不清楚确切的问题。
如果有人能帮助我理解这个问题,我们将不胜感激。提前致谢。
最佳答案
您似乎没有正确填充您的信任库。我知道你不能讨论你的网络服务的细节,但你的服务证书是由哪个 CA 签发的?您需要将 那个 链放入您的客户端应用程序的信任 keystore 中。
例如,如果您的 Web 服务已从中间 GeoTrust CA 签署,您应该将中间 GeoTrust CA 的证书以及根 GeoTrust CA 的证书放入您的商店。如果您仍然遇到错误,也请将您的 Web 服务证书也放入您的客户端信任库中。
查看 keytool -list 输出,您的信任库中只有一个条目。
关于android - 从 android 连接到 REST web 服务抛出 javax.net.ssl.SSLException : Not trusted server certificate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6219740/