且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

CertPathValidatorException:未找到证书路径的信任锚 - 改造 Android

更新时间:2021-07-08 22:36:42

免责声明:此答案来自 2015 年 7 月,并从那时起使用 RetrofitOkHttp.
查看此链接了解更多关于 Retrofit v2 和 这个用于当前的 OkHttp 方法.

DISCLAIMER: this answer is from Jul 2015 and uses Retrofit and OkHttp from that time.
Check this link for more info on Retrofit v2 and this one for the current OkHttp methods.

好的,我使用 Android 开发者指南.

就像 OP 一样,我正在尝试使用 RetrofitOkHttp 来连接到启用了自签名 SSL 的服务器.

Just as OP, I'm trying to use Retrofit and OkHttp to connect to a self-signed SSL-enabled server.

这是使事情正常工作的代码(我已经删除了 try/catch 块):

Here's the code that got things working (I've removed the try/catch blocks):

public static RestAdapter createAdapter(Context context) {
  // loading CAs from an InputStream
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  InputStream cert = context.getResources().openRawResource(R.raw.my_cert);
  Certificate ca;
  try {
    ca = cf.generateCertificate(cert);
  } finally { cert.close(); }

  // creating a KeyStore containing our trusted CAs
  String keyStoreType = KeyStore.getDefaultType();
  KeyStore keyStore = KeyStore.getInstance(keyStoreType);
  keyStore.load(null, null);
  keyStore.setCertificateEntry("ca", ca);

  // creating a TrustManager that trusts the CAs in our KeyStore
  String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
  TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
  tmf.init(keyStore);

  // creating an SSLSocketFactory that uses our TrustManager
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(null, tmf.getTrustManagers(), null);

  // creating an OkHttpClient that uses our SSLSocketFactory
  OkHttpClient okHttpClient = new OkHttpClient();
  okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());

  // creating a RestAdapter that uses this custom client
  return new RestAdapter.Builder()
              .setEndpoint(UrlRepository.API_BASE)
              .setClient(new OkClient(okHttpClient))
              .build();
}

为了帮助调试,我还在 RestAdapter 创建命令中添加了 .setLogLevel(RestAdapter.LogLevel.FULL),我可以看到它连接并从服务器获取响应.

To help in debugging, I also added .setLogLevel(RestAdapter.LogLevel.FULL) to my RestAdapter creation commands and I could see it connecting and getting the response from the server.

只需要保存在 main/res/raw 中的原始 .crt 文件..crt 文件,也就是证书,是使用 openssl 创建证书时创建的两个文件之一.通常,它是一个 .crt 或 .cert 文件,而另一个是 .key 文件.

All it took was my original .crt file saved in main/res/raw. The .crt file, aka the certificate, is one of the two files created when you create a certificate using openssl. Generally, it is a .crt or .cert file, while the other is a .key file.

Afaik,.crt 文件是您的公钥,.key 文件是您的私钥.

Afaik, the .crt file is your public key and the .key file is your private key.

如我所见,您已经有一个 .cert 文件,它是相同的,因此请尝试使用它.

As I can see, you already have a .cert file, which is the same, so try to use it.

PS:对于那些将来阅读它并且只有 .pem 文件的人,根据 这个答案,您只需要将其转换为另一个:

PS: For those that read it in the future and only have a .pem file, according to this answer, you only need this to convert one to the other:

openssl x509 -outform der -in your-cert.pem -out your-cert.crt

PS²:对于那些根本没有任何文件的人,您可以使用以下命令(bash)从任何服务器中提取公钥(又名证书):


PS²: For those that don't have any file at all, you can use the following command (bash) to extract the public key (aka certificate) from any server:

echo -n | openssl s_client -connect your.server.com:443 | 
  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt

只需替换 your.server.com 和端口(如果它不是标准的 HTTPS)并为要创建的输出文件选择一个有效的路径.

Just replace the your.server.com and the port (if it is not standard HTTPS) and choose a valid path for your output file to be created.