Smack: Якорь доверия для пути сертификации не найден

Я пытаюсь подключиться с 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: якорь доверия для пути сертификации не найден.

Я предполагаю, что это связано с сертификатом 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);

Но все равно не работает. Любое предложение?


person Héctor    schedule 03.09.2015    source источник
comment
возможный дубликат java.security.cert.CertPathValidatorException: якорь доверия для путь сертификации не найден   -  person Mickaël Rémond    schedule 03.09.2015
comment
ваша проблема решена?   -  person Mithun Sarker Shuvro    schedule 10.11.2016


Ответы (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
person GreyFairer    schedule 17.09.2015
comment
Ссылка, она сломана. Можете ли вы опубликовать другую ссылку или фактические шаги? - person Pierre; 05.07.2019

Обучение Android может решить именно эту проблему:

Распространенные проблемы при проверке сертификатов сервера Это может происходить по нескольким причинам, в том числе:

  1. ЦС, выдавший сертификат сервера, неизвестен.
  2. Сертификат сервера не был подписан центром сертификации, но был самоподписанным.
  3. В конфигурации сервера отсутствует промежуточный ЦС

В следующих разделах обсуждается, как решить эти проблемы, сохранив при этом безопасность соединения с сервером.

Неизвестный центр сертификации

В этом случае исключение SSLHandshakeException возникает из-за того, что у вас есть ЦС, которому система не доверяет. Это может быть связано с тем, что у вас есть сертификат от нового ЦС, которому еще не доверяет Android, или ваше приложение работает в более старой версии без ЦС. Чаще всего CA неизвестен, потому что это не публичный CA, а частный, выпущенный такой организацией, как правительство, корпорация или учебное заведение для их собственного использования.

К счастью, вы можете научить HttpsURLConnection доверять определенному набору центров сертификации. Процедура может быть немного запутанной, поэтому ниже приведен пример, который берет конкретный CA из InputStream, использует его для создания KeyStore, который затем используется для создания и инициализации TrustManager. TrustManager - это то, что система использует для проверки сертификатов с сервера, и - путем создания сертификата из KeyStore с одним или несколькими центрами сертификации - это будут единственные центры сертификации, которым доверяет этот TrustManager.

Учитывая новый TrustManager, пример инициализирует новый SSLContext, который предоставляет SSLSocketFactory, который вы можете использовать для переопределения SSLSocketFactory по умолчанию из HttpsURLConnection. Таким образом, соединение будет использовать ваши центры сертификации для проверки сертификата.

Вот полный пример использования организационного центра сертификации Вашингтонского университета:

    // 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, который знает о ваших центрах сертификации, система может проверить, что ваш сертификат сервера получен от доверенного эмитента.

Внимание: многие веб-сайты описывают плохое альтернативное решение, которое заключается в установке TrustManager, который ничего не делает. Если вы сделаете это, вы также можете не шифровать свое общение, потому что любой может атаковать ваших пользователей в общедоступной точке доступа Wi-Fi, используя уловки DNS для отправки трафика ваших пользователей через собственный прокси-сервер, который притворяется вашим сервером. Затем злоумышленник может записать пароли и другие личные данные. Это работает, потому что злоумышленник может сгенерировать сертификат и - без TrustManager, который фактически проверяет, что сертификат получен из надежного источника - ваше приложение может общаться с кем угодно. Так что не делайте этого, даже временно. Вы всегда можете сделать так, чтобы ваше приложение доверяло издателю сертификата сервера, так что просто сделайте это.

На странице есть дополнительные возможности, но эта кажется наиболее актуальной.

person Keith    schedule 16.09.2015

Есть 2 подхода к решению вашей проблемы:

  1. Исправьте свой сервер, подписав сертификаты доверенной третьей стороной.

  2. Пусть ваш клиент примет самозаверяющий сертификат, который вы используете прямо сейчас..

person Eelco    schedule 10.09.2015