Smack:"找不到证书路径的信任锚点"

3

我正在尝试从 Android 连接到本地的 Apache Vysper XMPP 服务器。我使用Smack框架执行 XMPP 操作:

AbstractXMPPConnection connection = new XMPPTCPConnection("bigdestroyer", "", ip);
  try {
      connection.setPacketReplyTimeout(10000);        
      connection.connect();
  } catch (SmackException e) {
      e.printStackTrace();
  } catch (IOException e) {
      e.printStackTrace();
  } catch (XMPPException e) {
      e.printStackTrace();
  }

但是我收到了这个错误:
``` javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. ```
我猜这与SSL证书有关,但我不知道该怎么做。你能帮我吗?
我已经尝试将`cert`文件(与服务器相同)放在`assets`文件夹中,并通过以下方式创建连接:
  XMPPTCPConnectionConfiguration connectionConfiguration = configuration.setConnectTimeout(10000)
                    .setUsernameAndPassword("admin", "admin")
                    .setHost(ip)
                    .setServiceName(ip)
                    .setKeystorePath("file:///android_asset/bogus_mina_tls.cert")
                    .build();

XMPPTCPConnection connection = new XMPPTCPConnection(connectionConfiguration);

但是它仍然无法正常工作。有任何建议吗?

你的问题解决了吗? - Mithun Sarker Shuvro
3个回答

3

KeystorePath应该指向一个密钥库,而不是一个简单的证书。Android默认使用KeystoreType BKS,因此您应该创建一个并将您的证书导入其中:

keytool -importcert -v -trustcacerts \
  -file "[YOUR_PUBLIC_CERTIFICATE_PATH]" \
  -alias [YOUR_ALIAS] -keystore "[BKS_TARGET_PATH]" \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath "[BOUNCY_CASTLE_JAR_PATH]" -storetype BKS \
  -storepass [YOUR_PASSWORD]

如果您不想使用命令行,也可以使用Portecle(http://portecle.sourceforge.net/)来完成此操作。

要获取证书文件,您可以使用openssl s_client命令:

openssl s_client -showcerts -connect <SERVER_URL>:<SERVER_PORT>  </dev/null

链接已经失效了。你能否发布另一个链接或者实际步骤? - Pierre

2

Android培训可能会解决这个确切的问题:

验证服务器证书时常见问题

这可能是由于以下原因之一造成的:

  1. 颁发服务器证书的CA未知
  2. 服务器证书未经CA签名,而是自签名的
  3. 服务器配置缺少中间CA

下面的部分将讨论如何解决这些问题,同时保持与服务器的连接安全。

未知证书颁发机构

在这种情况下,SSLHandshakeException出现是因为您有一个系统不信任的CA。这可能是因为您拥有来自尚未被Android或您的应用程序信任的新CA的证书,或者您的应用程序在较旧的版本上运行而没有该CA。更常见的情况是CA未知,因为它不是公共CA,而是由政府、公司或教育机构等组织为其自身使用发行的私人CA。

幸运的是,您可以教HttpsURLConnection信任特定的一组CA。该过程可能有点复杂,因此下面是一个示例,它从InputStream中获取特定的CA,使用它创建一个KeyStore,然后用它创建和初始化一个TrustManager。TrustManager是系统用于验证来自服务器的证书的内容,通过从具有一个或多个CA的KeyStore中创建一个TrustManager,这些将是该TrustManager信任的唯一CA。

给定新的TrustManager,该示例初始化了一个新的SSLContext,提供了一个SSLSocketFactory,您可以使用它来覆盖HttpsURLConnection的默认SSLSocketFactory。这样连接将使用您的CA进行证书验证。

这里是使用华盛顿大学组织CA的完整示例:

    // Load CAs from an InputStream
    // (could be from a resource or ByteArrayInputStream or ...)
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    // From https://www.washington.edu/itconnect/security/ca/load-der.crt
    InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
    Certificate ca;
    try {
        ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
    } finally {
        caInput.close();
    }

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    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 context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    // Tell the URLConnection to use a SocketFactory from our SSLContext
    URL url = new URL("https://certs.cac.washington.edu/CAtest/");
    HttpsURLConnection urlConnection =
        (HttpsURLConnection)url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());
    InputStream in = urlConnection.getInputStream();
    copyInputStreamToOutputStream(in, System.out);

使用自定义的 TrustManager 知道您的 CA,系统能够验证您的服务器证书是否来自受信任的发行商。

请注意:许多网站描述了一种较差的替代解决方案,即安装一个什么都不做的 TrustManager。如果您这样做,那么您可能就没有加密通信了,因为任何人都可以通过使用 DNS 技巧将用户流量发送到他们自己的代理中来攻击您的用户在公共 Wi-Fi 热点。攻击者然后可以记录密码和其他个人数据。这是有效的,因为攻击者可以生成证书,并且 - 如果没有 TrustManager 实际上验证证书来自受信任的来源 - 您的应用程序可能正在与任何人交谈。所以不要这样做,甚至是暂时的也不要。您始终可以使您的应用程序信任服务器证书的颁发机构,所以只需这样做就可以了。

页面继续提供了其他可能性,但这似乎是最相关的一个。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接