Как создать формат BKS (BouncyCastle) Java Keystore, который содержит цепочку сертификатов клиента

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

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

Для справки: команда, которая работает для создания надежного магазина BKS :
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

Ответ 1

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

  • Загрузите баунцикаст JAR из       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\безопасность
        • D:\Tools\jre1.5.0_09\Lib\безопасность
        • и добавьте следующую запись
          • security.provider.7 = org.bouncycastle.jce.provider.BouncyCastleProvider
      • Добавьте следующую переменную среды в раздел "Пользовательские переменные"
        • CLASSPATH =% CLASSPATH%; C:\bcprov-доб-jdk15on-1.46.jar
    • Добавьте bcprov-ext-jdk15on-1.46.jar в CLASSPATH вашего проекта и добавьте следующую строку в свой код
      • Security.addProvider(новый BouncyCastleProvider());
  • Создайте Keystore, используя 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 и добавьте следующую запись

      • < Разъем порт = "8443" keystorePass = "MyProject" псевдоним = "MyProject" Хранилище ключей = "C:/myproject.keystore" keystoreType = "БКС" SSLEnabled = "истина" clientAuth = "ложь" Протокол = "HTTP/1.1" Схема = "HTTPS" обеспечение = "истина" SSLProtocol = "TLS" sslImplementationName = "org.bouncycastle.jce.provider.BouncyCastleProvider" / >
    • Перезагрузите сервер после этих изменений.

  • Настройка BouncyCastle для Android-клиента
    • Не нужно настраивать, так как Android поддерживает Bouncy Castle Version 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);
        }
    }
}

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

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

:

keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS

Ответ 6

Ваша команда для создания хранилища 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);
    }
}

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

Ответ 7

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