Двусторонняя аутентификация SSL на Android

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

Какой сертификат нужен клиенту и как его получить, чтобы он автоматически отправил его на сервер во время процесса рукопожатия? Вот код клиента и сервера, который у меня есть до сих пор. Является ли мой подход неправильным?

Код сервера

while True: # Keep listening for clients c, fromaddr = sock.accept() ssl_sock = ssl.wrap_socket(c, keyfile = "serverPrivateKey.pem", certfile = "servercert.pem", server_side = True, # Require the client to provide a certificate cert_reqs = ssl.CERT_REQUIRED, ssl_version = ssl.PROTOCOL_TLSv1, ca_certs = "clientcert.pem", #TODO must point to a file of CA certificates?? do_handshake_on_connect = True, ciphers="!NULL:!EXPORT:AES256-SHA") print ssl_sock.cipher() thrd = sock_thread(ssl_sock) thrd.daemon = True thrd.start() 

Я подозреваю, что могу использовать неправильный файл для ca_certs …?

Код клиента

  private boolean connect() { try { KeyStore keystore = KeyStore.getInstance("BKS"); // Stores the client certificate, to be sent to server KeyStore truststore = KeyStore.getInstance("BKS"); // Stores the server certificate we want to trust // TODO: change hard coded password... THIS IS REAL BAD MKAY truststore.load(mSocketService.getResources().openRawResource(R.raw.truststore), "test".toCharArray()); keystore.load(mSocketService.getResources().openRawResource(R.raw.keystore), "test".toCharArray()); // Use the key manager for client authentication. Keys in the key manager will be sent to the host KeyManagerFactory keyFManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyFManager.init(keystore, "test".toCharArray()); // Use the trust manager to determine if the host I am connecting to is a trusted host TrustManagerFactory trustMFactory = TrustManagerFactory.getInstance(TrustManagerFactory .getDefaultAlgorithm()); trustMFactory.init(truststore); // Create the socket factory and add both the trust manager and key manager SSLCertificateSocketFactory socketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory .getDefault(5000, new SSLSessionCache(mSocketService)); socketFactory.setTrustManagers(trustMFactory.getTrustManagers()); socketFactory.setKeyManagers(keyFManager.getKeyManagers()); // Open SSL socket directly to host, host name verification is NOT performed here due to // SSLCertificateFactory implementation mSSLSocket = (SSLSocket) socketFactory.createSocket(mHostname, mPort); mSSLSocket.setSoTimeout(TIMEOUT); // Most SSLSocketFactory implementations do not verify the server's identity, allowing man-in-the-middle // attacks. This implementation (SSLCertificateSocketFactory) does check the server's certificate hostname, // but only for createSocket variants that specify a hostname. When using methods that use InetAddress or // which return an unconnected socket, you MUST verify the server's identity yourself to ensure a secure // connection. verifyHostname(); // Safe to proceed with socket now ... 

Я создал закрытый ключ клиента, сертификат клиента, закрытый ключ сервера и сертификат сервера с помощью openssl. Затем я добавил сертификат клиента к keystore.bks (который я хранил в /res/raw/keystore.bks ) Затем я добавил сертификат сервера в truststore.bks

Итак, теперь, когда клиент пытается подключиться, я получаю эту сторону сервера ошибок:

 ssl.SSLError: [Errno 1] _ssl.c:504: error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate 

И когда я пытаюсь сделать это в Android-клиенте

 SSLSession s = mSSLSocket.getSession(); s.getPeerCertificates(); 

Я получаю эту ошибку:

 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 

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

Что я должен положить в хранилище ключей, чтобы предотвратить это исключение?

Кроме того, является ли этот метод двухсторонней аутентификации SSL безопасным и эффективным?

Серверу необходимо доверять сертификату клиента. Обычный способ сделать это – создать CA, а затем подписать сертификат сервера и сертификат клиента. Каждый из них должен иметь сертификат CA в своих соответствующих хранилищах доверия. Затем вам нужно инициализировать SSLContext примерно так:

 KeyStore trustStore = loadTrustStore(); KeyStore keyStore = loadKeyStore(); TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); KeyManagerFactory kmf = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray()); SSLContext sslCtx = SSLContext.getInstance("TLS"); sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

Затем вы можете использовать SSLContext для создания фабрик сокетов по мере необходимости. Они будут инициализированы соответствующими ключами и сертификатами.

Обновить

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

Проделайте небольшое исследование, которое может помочь вашему путешествию. Mvsjes2 сообщил, что неправильные порты могут вызывать исключение SSLPeerUnverifiedException. Убедитесь, что вы используете порт 443, поскольку я не вижу в вашем коде, где вы устанавливаете порт.

Также проверьте emmby ответ, который я нашел также проницательным.

Оригинальное сообщение

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

Попытайтесь изучить эту статью об использовании подключения к полномочию Unknown Certificates, который позволит вам использовать сертификат, который не является стандартным для Android. , Возможно, вам придется включить публичный / промежуточный сертификат в приложение, чтобы убедиться, что существующие / более старые устройства смогут подключаться к вашему серверу. Следуйте за статьей и посмотрите, решила ли ваша проблема.

Кроме того, является ли этот метод двухсторонней аутентификации SSL безопасным и эффективным?

Только если вы можете получить его работу первым! Пока ваш код может проверить достоверность SSL-сертификата, он должен быть достаточно эффективным. Меры безопасности … И так долго сам сертификат ssl создается с сильной сигнатурой / ключом RSA, что сделает его более безопасным, чем просто простой http. Дайте мне знать, если у вас будут проблемы.