拍打:"Trust anchor for certification path not found"

Smack: "Trust anchor for certification path not found"

我正在尝试从 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);

但是还是不行。有什么建议吗?

有两种方法可以解决您的问题:

  1. 通过受信任的第 3 方签署证书来修复您的服务器。

  2. 有你的客户accept the self-signed certificate you are using right now.

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

Common Problems Verifying Server Certificates This can happen for several reasons, including:

  1. The CA that issued the server certificate was unknown
  2. The server certificate wasn't signed by a CA, but was self signed
  3. The server configuration is missing an intermediate CA

The following sections discuss how to address these problems while keeping your connection to the server secure.

Unknown certificate authority

In this case, the SSLHandshakeException occurs because you have a CA that isn't trusted by the system. It could be because you have a certificate from a new CA that isn't yet trusted by Android or your app is running on an older version without the CA. More often a CA is unknown because it isn't a public CA, but a private one issued by an organization such as a government, corporation, or education institution for their own use.

Fortunately, you can teach HttpsURLConnection to trust a specific set of CAs. The procedure can be a little convoluted, so below is an example that takes a specific CA from an InputStream, uses it to create a KeyStore, which is then used to create and initialize a TrustManager. A TrustManager is what the system uses to validate certificates from the server and—by creating one from a KeyStore with one or more CAs—those will be the only CAs trusted by that TrustManager.

Given the new TrustManager, the example initializes a new SSLContext which provides an SSLSocketFactory you can use to override the default SSLSocketFactory from HttpsURLConnection. This way the connection will use your CAs for certificate validation.

Here is the example in full using an organizational CA from the University of Washington:

    // 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);

With a custom TrustManager that knows about your CAs, the system is able to validate that your server certificate come from a trusted issuer.

Caution: Many web sites describe a poor alternative solution which is to install a TrustManager that does nothing. If you do this you might as well not be encrypting your communication, because anyone can attack your users at a public Wi-Fi hotspot by using DNS tricks to send your users' traffic through a proxy of their own that pretends to be your server. The attacker can then record passwords and other personal data. This works because the attacker can generate a certificate and—without a TrustManager that actually validates that the certificate comes from a trusted source—your app could be talking to anyone. So don't do this, not even temporarily. You can always make your app trust the issuer of the server's certificate, so just do it.

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

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