Форум программистов, компьютерный форум CyberForum.ru

Программирование Android

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 36, средняя оценка - 4.97
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
#1

Https соединение с сертфикатам пользователя и сервера - Программирование Android

24.09.2012, 09:35. Просмотров 4893. Ответов 9
Метки нет (Все метки)

Добрый день. Есть следующая ситуация. Есть сервер, у него есть свой сертификат. Есть клиент, у него свой сертификат. Заходя через браузер на сервер - браузер проверяет сертификат сервера. После, уже сервер проверяет сертификат клиента. Обычная ситуация.
Вопрос. Мне надо сделать приложение под андройд с выходом к серверу с сертификатом клиента. Примерно так я это вижу: Кидаю сертификат пользователя в телефон(через шнур в любую папку как простой файл), потом указываю как-то путь к сертификату(какой-нить opendialog, по кторому получаю путь к сертификату) и устанавливаю https - соединение с сервером с авторизацией, при этом сертификат сервера принимаю по умолчанию. Почитала пару статей, но так ничего полезного не нашла. http://blog.crazybob.org/2010/02/and...tificates.html, Получение JSON с https сервера - тут только как принять сертификат сервера. А как установить соединение с сервером через сертификат клиента - ни где не могу найти. Занимаюсь данной проблемой уже несколько дней. Кто может подсказать, была бы очень признательна.
Как создать http соединение знаю, могу как с авторизацией, так и без нее. Мне осталось только одно сделать hhtps соединение с моим сертификатом. Но почему-то разобраться не могу.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
obrazer
70 / 70 / 1
Регистрация: 04.09.2012
Сообщений: 170
26.09.2012, 23:27     Https соединение с сертфикатам пользователя и сервера #2
Покопался тут чуть...
Вот чего понял (попытался понять, ибо проверить не на чем):
1. ключевой момент в этом месте
Java
1
2
3
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init( XXX , YYY , new java.security.SecureRandom());
final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
тут
XXX - X509KeyManager[] - набор менеджеров для работы с клиентскими сертификатами
YYY - X509TrustManager[] - набор менеджеров для работы с серверными сертификатами

В теме Получение JSON с https сервера имеется пример реализации интерфейса X509TrustManager.
Для X509KeyManager так же можно создавать реализацию интерфейса.
К примеру (очень грубо - не проверял) так
Java
1
2
3
final KeyManager[] ClientCerts = new KeyManager[] {new X509KeyManager() {
... Тут реализация всех методов интерфейса X509KeyManager
} };
В частности, на сколько сумел вкурить, для работы с сертификатом смотрим в сторону метода getCertificateChain(String alias). Что там с алиасами, честно говоря, не знаю. Вероятно надо реализовывать и другие методы, которые возвращают список алиасов и т.п.

Получение сертификата из файла (дабы вернуть его методом getCertificateChain):
Java
1
2
3
InputStream inStream = new FileInputStream("Имя файла с сертификатом");
X509Certificate cert = X509Certificate.getInstance(inStream);
inStream.close();
ps: в мануале имеется пример
Java
1
2
3
4
5
6
7
8
9
10
11
   KeyStore keyStore = ...;
   KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
   kmf.init(keyStore);
 
   SSLContext context = SSLContext.getInstance("TLS");
   context.init(kmf.getKeyManagers(), null, null);
 
   URL url = new URL("https://www.example.com/");
   HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
   urlConnection.setSSLSocketFactory(context.getSocketFactory());
   InputStream in = urlConnection.getInputStream();
keyStore - класс работающий с хранилищем ключей и сертификатов.
Но, так или иначе, финальная работа ведется с набором KeyManager, полученных kmf.getKeyManagers(). Я же выше предлагаю написать свою реализацию X509KeyManager, который будет отдавать SSLContext-у сертификат.

Вроде как все сходится
Проверить не могу, а потому не уверен в своих рассуждениях.

ps: кстати, в мануале так и написано - "A X509KeyManager can also be implemented directly. This can allow an application to return a certificate and private key from a non-KeyStore source or to specify its own logic for selecting a specific credential to use when many may be present in a single KeyStore.". По-русски - "X509KeyManager также может быть реализован непосредственно. Это может позволить приложению вернуть сертификат и закрытый ключ от не-KeyStore источника или указать собственную логику для выбора конкретных учетных данных для использования если их может находиться много в одном хранилище ключей."
obrazer
70 / 70 / 1
Регистрация: 04.09.2012
Сообщений: 170
28.09.2012, 20:41     Https соединение с сертфикатам пользователя и сервера #3
Olix, таки вышло чего путного?
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
01.10.2012, 07:34  [ТС]     Https соединение с сертфикатам пользователя и сервера #4
Не совсем
Если без клиентского сертификата делать, то все получается замечательно. Но этого мало. А если с клиентским..., то вылетает такая ошибка:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Это примерно значит что соответствующее хранилище сертификатов не используется при попытке соединения - сервер свою часть ключа предоставляет, а клиент - нет.
Сегодня хочу сделать последнюю попытку, добиться соединения с клиентским сертификатом. О результатах отпишусь
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
02.10.2012, 08:39  [ТС]     Https соединение с сертфикатам пользователя и сервера #5
Так что-то у меня ничего не получилось с клиентским сертифкатом.
Так работает. Но это самое простое.
Java
1
sslContext.init( null, trustAllCerts, new java.security.SecureRandom());
Так не хочет:
Java
1
sslContext.init( keyAllCert,null, new java.security.SecureRandom());
Ошибка - e javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

И так не получается:
Java
1
sslContext.init( keyAllCert, trustAllCerts, new java.security.SecureRandom());
Ошибка - e javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x781e0: Failure in SSL library, usually a protocol error. error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure (external/openssl/ssl/s3_pkt.c:1232 0x79668:0x00000003)

Все делаю по примерам. Сертификат клиентский рабочий - я с ним на сайт захожу-верификацию спокойно прохожу. Я его в keystore грузила и как *.p12 и в формате bks, через keytool импортированный. Но толку мало.
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
03.10.2012, 14:30  [ТС]     Https соединение с сертфикатам пользователя и сервера #6
Меня грызла это недоделанное https соединение. В итоге сегодня убила день на поиск информации и на различные попытки. И все такие труд вознаграждается!
Вот замечательная статейка ,которая помогла мне:
http://www.javadocexamples.com/java_...mple.java.html
1. Закидываю сертифкат в формате p12 на телефон
2. Узнаю путь к нему - path
3. "Создаем хранилище для клиентского сертфиката":

Java
1
2
3
4
5
6
        File f = new File(pathFilep12);
        final InputStream in = new FileInputStream(f);
        KeyManagerFactory mgrFact = KeyManagerFactory.getInstance("SunX509");
        KeyStore clientStore = KeyStore.getInstance("PKCS12");
        clientStore.load(in, PASSWORD.toCharArray());
        mgrFact.init(clientStore, PASSWORD.toCharArray());
Тут PASSWORD - пароль от самого сертификата. Где-то читала в какой-то статье, что пароль от хранилища должен быть равен паролю от сертификата, иначе будет ошибка.

4. "Создаем серверное хранилище" или как оно там еще называется:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
                    final TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                System.out.println("getAcceptedIssuers");
                 return null;
            }
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                System.out.println("Сведения о сертификате : " +       chain[0].getIssuerX500Principal().getName() + "\n Тип авторизации : " + authType);
            }
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                System.out.println("checkClientTrusted : " + authType);
            }
        } };
5. sslContext :

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
                SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            sslContext.init(mgrFact.getKeyManagers(),trustAllCerts, new java.security.SecureRandom());
        } catch (KeyManagementException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
6. Само подключение:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
                final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
                String url = "https://xxx.xxx.xxx";
        HttpURLConnection conn = null;      
        try {
            conn = (HttpURLConnection) new URL(url).openConnection();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
                conn.setRequestProperty("Authorization", "Basic "+encodeBase64("user:password") ); //это для тех, кому нужна еще авторизация 
                conn.setRequestProperty("Connection", "close");     
        conn.setRequestProperty("Accept-Charset", "cp1251");    
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        
        ((HttpsURLConnection) conn).setSSLSocketFactory(sslSocketFactory);
        ((HttpsURLConnection) conn).setHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String arg0, SSLSession arg1) {           
                return true;
            }
        });
        
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setReadTimeout(10000 );
        conn.setConnectTimeout(15000);
        try {
            conn.setRequestMethod("GET");
        } catch (ProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("e "+e.toString());
        }
        
        
        try {
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                System.out.println("error "+ conn.getResponseCode());
            }else{
                System.out.println("ok "+conn.getResponseCode()+" response "+conn.getResponseMessage());
                System.out.println("conn.getResponseMessage() "+);
                String resp= readStreamToString(conn.getInputStream(), "cp1251");
                System.out.println("Ответ: "+resp);             
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("e "+e.toString());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("e "+e.toString());
        }
 
...
 
 
private static String readStreamToString(InputStream in, String encoding)
            throws IOException {
        StringBuffer b = new StringBuffer();
        InputStreamReader r = new InputStreamReader(in, encoding);
        int c;
        while ((c = r.read()) != -1) {
            b.append((char)c);
        }
        return b.toString();
    }
Это код приведен без проверки на ошибки, закрытия потоков. Выдернуты основные шаги. Надеюсь кому-нибудь это поможет)))
Всем Спасибо!
Drnkn
0 / 0 / 0
Регистрация: 20.09.2013
Сообщений: 2
20.09.2013, 17:02     Https соединение с сертфикатам пользователя и сервера #7
Если я всё правильно понял, то это не рабочий способ, а обходной путь, так как нет проверки сертификатов, то есть принимаются любые сертификаты, что сводит на нет всё https.
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
21.09.2013, 07:57  [ТС]     Https соединение с сертфикатам пользователя и сервера #8
Цитата Сообщение от Drnkn Посмотреть сообщение
Если я всё правильно понял, то это не рабочий способ, а обходной путь, так как нет проверки сертификатов, то есть принимаются любые сертификаты, что сводит на нет всё https.
Проверка на сервере происходит. Я пробовала с 2 сертификатами. Один был зарегистрирован на сервере, другой просто левый. С первым все выходило ок. Со вторым сервер присылал ответ(точно не вспомню, дело было давно), что не все хорошо))). Может что-то и не так. Но на тот момент это было лучшее, что могло быть и оно работало

Добавлено через 1 минуту
Было бы приятно увидеть рабочий способ. Потому что это очень актуально, а в интернете информации мало.
Drnkn
0 / 0 / 0
Регистрация: 20.09.2013
Сообщений: 2
21.09.2013, 08:19     Https соединение с сертфикатам пользователя и сервера #9
Цитата Сообщение от Olix Посмотреть сообщение
Проверка на сервере происходит. Я пробовала с 2 сертификатами. Один был зарегистрирован на сервере, другой просто левый. С первым все выходило ок. Со вторым сервер присылал ответ(точно не вспомню, дело было давно), что не все хорошо))). Может что-то и не так. Но на тот момент это было лучшее, что могло быть и оно работало

Добавлено через 1 минуту
Было бы приятно увидеть рабочий способ. Потому что это очень актуально, а в интернете информации мало.
То, что сервер проверяет сертификат это всё очень хорошо, только дело не в нём, так как злоумышленник может подсунуть любой сертификат, установить соединение с клиентом и клиент будет слать ему все данные. Может это, конечно, не так работает) Это я так понял.
Так вот, для меня почти работает следующий способ:
1. Сохраняем всю цепочку сертификатов, промежуточные и корневой. Я сохранял через firefox, но можно и через openssl.
2. Качаем BountyCastle отсюда.
3. Потом для всех сертификатов в правильном порядке (от промежуточных до корневого) добавляем сертификаты в keystore:
keytool -importcert -v -trustcacerts -file "path_to_your_cert\your.crt" -alias YOUR_ALIAS -keystore "path_to_your_keystore\your.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_provider\bcprov-jdk15on-1.46.jar" -storetype BKS -storepass secretpassword
4. Положить keystore в папку res/raw
5. Дальше в коде нужно сделать примерно следующее:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
        try {
            final KeyStore keystore = KeyStore.getInstance("BKS");
            keystore.load(BeeonlineApp.getAppResources().openRawResource(R.raw.keystore),
                    "secretpasword".toCharArray());         
            
            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keystore); 
            
            final SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
 
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (KeyStoreException e) {
            // TODO Auto-generated catch block
 
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch
                                                // block
            e.printStackTrace();
        } catch (CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (KeyManagementException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
И тогда, возможно, всё заработает. Для меня подобная схема заработала в одном случае и, возможно, только для 4ки, но хоть что то.

Полезные ссылки:
1. http://nelenkov.blogspot.ru/2011/12/...-store-on.html
2. http://blog.chariotsolutions.com/201...icates-on.html
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
22.09.2013, 08:16     Https соединение с сертфикатам пользователя и сервера
Еще ссылки по теме:
Basic-authentication Https SSL Android, loopj library Android
Запросы на API сайта из приложения перестали работать после перехода на https Android
Java Сети Https соединение не находит сертификат
PHP https & php (зашифрованное соединение)
Как сделать HTTPS соединение в Тomcat? Java

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Olix
6 / 6 / 0
Регистрация: 02.04.2009
Сообщений: 46
22.09.2013, 08:16  [ТС]     Https соединение с сертфикатам пользователя и сервера #10
Хм...надо будет посмотреть подробно и попробовать. Спасибо!
Yandex
Объявления
22.09.2013, 08:16     Https соединение с сертфикатам пользователя и сервера
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru