Как создать Java Keystore в формате BKS (BouncyCastle), который содержит цепочку сертификатов клиента - PullRequest
51 голосов
/ 01 ноября 2010

Я пишу приложение для Android, которое требует аутентификации клиента SSL. Я знаю, как создать хранилище ключей JKS для настольного Java-приложения, но Android поддерживает только формат BKS. Каждый способ, которым я пытался создать хранилище ключей, приводит к следующей ошибке:
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain

Похоже, клиент никогда не отправляет правильную цепочку сертификатов, возможно потому, что я не создаю хранилище ключей должным образом. Я не могу включить отладку SSL, как я могу на рабочем столе, так что это делает это намного сложнее, чем должно быть.

Для справки ниже приведена команда, которая работает над созданием BKS truststore :
keytool -importcert -v -trustcacerts -file "cacert.pem" -alias ca -keystore "mySrvTruststore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest


Вот команда, которую я пробовал, которая НЕ работает для создания клиента BKS keystore :

cat clientkey.pem clientcert.pem cacert.pem > client.pem

keytool -import -v -file <(openssl x509 -in client.pem) -alias client -keystore "clientkeystore" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest

Ответы [ 7 ]

62 голосов
/ 05 апреля 2012

Подробные пошаговые инструкции, которым я следовал, чтобы добиться этого

  • Загрузите JAR-файл bouncycastle из http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar или возьмите его из папки "doc".
  • Настройте BouncyCastle для ПК одним из следующих способов.
    • Статическое добавление поставщика BC (рекомендуется)
      • Скопируйте файл bcprov-ext-jdk15on-1.46.jar в каждый
        • D: \ tools \ jdk1.5.0_09 \ jre\ lib \ ext (JDK (в комплекте JRE)
        • D: \ tools \ jre1.5.0_09 \ lib \ ext (JRE)
        • C: \ (местоположение, которое будет использоваться в переменной env)
      • Измените файл java.security в
        • D: \ tools \ jdk1.5.0_09 \ jre \ lib \ security
        • D: \ tools\ jre1.5.0_09 \ lib \ security
        • и добавьте следующую запись
          • security.provider.7 = org.bouncycastle.jce.provider.BouncyCastleProvider
      • Добавьте следующую переменную среды в раздел «Пользовательские переменные»
        • CLASSPATH =% CLASSPATH%; c: \ bcprov-ext-jdk15on-1.46.jar
    • Добавьте bcprov-ext-jdk15on-1.46.jar в CLASSPATH вашего проекта и добавьте следующую строку в свой код
      • Security.addProvider (новый BouncyCastleProvider ());
  • Создание хранилища ключейиспользование Bouncy Castle
    • Выполните следующую команду
      • keytool -genkey -alias myproject -keystore C: /myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
    • Создает файл C: \ myproject.keystore
    • Выполните следующую команду, чтобы проверить, правильно ли он сгенерирован или нет
      • keytool -list-keystore C: \ myproject.keystore -storetype BKS
  • Конфигурировать BouncyCastle для TOMCAT

    • Открыть D: \ tools \ apache-tomcat-6.0.35 \ conf \ server.xml и добавьте следующую запись

    • Перезагрузите сервер после этих изменений.

  • Настройка BouncyCastle для клиента Android
    • Нет необходимостидля настройки, поскольку Android внутренне поддерживает Bouncy Castle версии 1.46 в прилагаемом «android.jar».
    • Просто внедрите свою версию HTTP-клиента (MyHttpClient.java можно найти ниже) и установите следующее в коде
      • SSLSocketFactory.setHostnameVerifier (SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    • Если вы этого не сделаете, выдается исключение, как показано ниже
      • javax.net.ssl.SSLException: имя хоста в сертификате не соответствует: <192.168.104.66>! =
    • В рабочем режиме измените приведенный выше код на
      • SSLSocketFactory.setHostnameVerifier (SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

MyHttpClient.java

package com.arisglobal.aglite.network;

import java.io.InputStream;
import java.security.KeyStore;

import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;

import com.arisglobal.aglite.activity.R;

import android.content.Context;

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.aglite);
            try {
                // Initialize the keystore with the provided trusted certificates.
                // Also provide the password of the keystore
                trusted.load(in, "aglite".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.ALLOW_ALL_HOSTNAME_VERIFIER);
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Как вызвать вышеуказанный код в вашемКласс деятельности:

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);
25 голосов
/ 03 февраля 2011

Я использую Portecle , и он работает как шарм.

4 голосов
/ 08 апреля 2011

Не уверен, что вы решили эту проблему или нет, но вот как я это делаю, и она работает на Android:

  1. Используйте openssl для объединения сертификата клиента (сертификат должен быть подписан центром сертификации, принятым сервером) и закрытым ключом в пару ключей формата PCKS12: openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
  2. Вам может потребоваться исправить вашу JRE, чтобы неограниченная сила шифрования зависела от силы вашего ключа: скопируйте файлы jar из JCE 5.0, неограниченная сила файлов политики юрисдикции и переопределите те в вашей JRE (например, C: \ Program Files \ Java \ jre6 \ Lib \ безопасность)
  3. Используйте инструмент Portecle, упомянутый выше, и создайте новое хранилище ключей в формате BKS
  4. Импортировать пару ключей PCKS12, созданную на шаге 1, и сохранить ее как хранилище ключей BKS. Это хранилище ключей работает с аутентификацией клиента Android.
  5. Если вам нужно создать цепочку сертификатов, вы можете использовать этот инструмент IBM: KeyMan , чтобы объединить пару ключей PCKS12 клиента с сертификатом CA. Но он генерирует только хранилище ключей JKS, поэтому вам снова понадобится Protecle для преобразования его в формат BKS.
4 голосов
/ 18 ноября 2010

Я не думаю, что ваша проблема с хранилищем ключей BouncyCastle;Я думаю, что проблема в сломанном пакете javax.net.ssl ​​в Android.Хранилище ключей BouncyCastle вызывает крайнее раздражение, поскольку Android изменил поведение Java по умолчанию, нигде не задокументировав его, и удалил провайдера по умолчанию, но он работает.

Обратите внимание, что для аутентификации SSL может потребоваться 2 хранилища ключей.Хранилище ключей «TrustManager», которое содержит сертификаты CA, и хранилище ключей «KeyManager», которое содержит открытые / закрытые ключи вашего сайта клиента.(Документация несколько расплывчата относительно того, что должно быть в хранилище ключей KeyManager.) Теоретически вам не нужно хранилище ключей TrustManager, если все ваши сертификаты подписаны «хорошо известными» органами сертификации, например, Verisign, Thawte,и так далее.Дайте мне знать, как это работает для вас.Вашему серверу также потребуется CA для того, что использовалось для подписи вашего клиента.

Я не смог создать соединение SSL с использованием javax.net.ssl.Я отключил клиентскую SSL-аутентификацию на стороне сервера и все еще не смог создать соединение.Поскольку моей конечной целью был HTTPS GET, я пробовал и пытался использовать HTTP-клиент Apache, который поставляется в комплекте с Android.Это вроде сработало.Я мог установить соединение HTTPS, но я все еще не мог использовать аутентификацию SSL.Если я включу SSL-аутентификацию клиента на моем сервере, соединение не будет установлено.Я не проверял код HTTP-клиента Apache, но подозреваю, что они используют собственную реализацию SSL и не используют javax.net.ssl.

2 голосов
/ 26 ноября 2014

командная строка:

keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
0 голосов
/ 28 сентября 2015

Используйте это руководство http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/ Это руководство действительно помогло мне. Важно соблюдать последовательность сертификатов в магазине. Например: сначала импортируйте самый нижний сертификат промежуточного ЦС, а затем вплоть до сертификата корневого ЦС .

0 голосов
/ 17 ноября 2010

Ваша команда для создания хранилища ключей BKS выглядит правильно для меня.

Как вы инициализируете хранилище ключей.

Вам нужно создать и передать свой собственный SSLSocketFactory.Вот пример, который использует Apache org.apache.http.conn.ssl.SSLSocketFactory

Но я думаю, вы можете сделать то же самое на javax.net.ssl.SSLSocketFactory

    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, "testtest".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);
    }
}

Пожалуйста, дайте мне знать, если это сработало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...