Ответ 1
Я собираюсь пройтись по проблемам, которые я обнаружил (хотя некоторые уже были замечены кем-то другим, а также исправлены). Обратите внимание, что я пробовал на Win 10, используя Python 2.7.10, 2.7.13, 3.4.4, 3.5.3 и OpenSSL 1.0.2d, 1.0.2j (без ф/ф):
-
server.py:
- переменная conn инициализируется как None и что это. Предложение if в конце finally бесполезно. client_sock, вероятно, должен быть проверен
- Подсказка: серверный сокет (sock), вероятно, может быть упакован (вместо client_sock); таким образом client_sock (возвращаемый
sock.accept()
) уже будет упакован
-
client.py:
- 1- й является второстепенным: код во фрагменте (
raise Exception("Error")
) не совпадает с кодом в трассировке (raise Exception("Invalid SSL cert for host %s. Check if this is a man-in-themiddle attack!")
) -
Способ поиска атрибутов сертификата. Как заметил @TomaszPlaskota, индекс кортежа commonName неверен. Вот довольно распечатанный сертификат с моей машины (значения полей могут/будут отличаться):
{ 'issuer': ((('countryName', 'AU'),), (('stateOrProvinceName', 'Some-State'),), (('localityName', 'CJ'),), (('organizationName', 'Internet Widgits Pty Ltd'),), (('organizationalUnitName', 'OU'),), (('commonName', 'cfati-e5550-0'),), (('emailAddress', '[email protected]'),)), 'notAfter': 'Jun 5 17:21:03 2018 GMT', 'notBefore': 'Jun 5 17:21:03 2017 GMT', 'serialNumber': 'C4A03B2BE4F959A9', 'subject': ((('countryName', 'AU'),), (('stateOrProvinceName', 'Some-State'),), (('localityName', 'CJ'),), (('organizationName', 'Internet Widgits Pty Ltd'),), (('organizationalUnitName', 'OU'),), (('commonName', 'cfati-e5550-0'),), (('emailAddress', '[email protected]'),)), 'version': 3 }
Могут быть случаи (по крайней мере теоретически), когда сертификат будет неполным. Более надежная форма проверки:
def check_certificate(cert, field=("commonName", "test")): if not cert: return False for pairs in cert.get("subject", ()): if field in pairs: return True return False # .... if not check_certificate(cert): raise Exception("Error")
-
Способ обработки ошибок. Если сертификат сервера не в порядке, вы просто выдаете ошибку, которая нарушает связь и ненормально завершает работу клиентской программы. Тот факт, что на стороне сервера триггеры
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:590)
(из вашего другого вопроса: [SO]: взаимная аутентификация ssl в простом ECHO клиент/сервер [Python/sockets/ssl] modules], ssl.SSLEOFError: EOF произошла с нарушением протокола), когда сервер пытается выполнить обратную запись для клиента (ssl_client.write(data)
).
Хотя в некоторых случаях при завершении программы ОС очищает ресурсы, рекомендуется всегда выполнять очистку из кода. Итак, вместо того, чтобы просто вызвать исключение, сделайте что-то вроде (как предложено @JamesKPolk):plain_sock = secure_sock.unwrap() plain_sock.shutdown(socket.SHUT_RDWR) plain_sock.close() # Any other cleanup action here
- 1- й является второстепенным: код во фрагменте (
-
Самая большая проблема заключается в том, что вы используете 2 самозаверяющих сертификата (которые не имеют ничего друг к другу). Самоподписанный сертификат означает:
- Это собственный подписант (или родитель). Вот почему поля "Эмитент" и "Тема" совпадают (или, если быть более точными, расширения идентификатора ключа эмитента и идентификатора ключа субъекта совпадают)
- Это сертификат CA (Certificate Authority) (если посмотреть на его базовые ограничения, вы заметите, что Subject Type is CA). Это как следствие бывшей пули
Несколько слов о сертификатах: они организованы в деревья: это означает, что будет корневой узел, также известный как сертификат корневого центра сертификации. Этот узел может иметь несколько дочерних узлов (дочерний узел означает, что он подписан своим родителем). Этими узлами также могут быть сертификаты CA или конечного пользователя (ЕС). У каждого сертификата CA также могут быть дети, и есть наше дерево. Его листья являются сертификатами ЕС. Путь между корневым ЦС и листовым сертификатом (составленным из корневого ЦС, промежуточных ЦС и ЕС) называется цепочкой сертификатов.
Самоподписанный сертификат может быть представлен в виде дерева, состоящего из одного узла. Обратите внимание, что, будучи сертификатом CA (как в нашем случае), он также может быть использован для подписи других сертификатов (он может иметь детей).
Проверка сертификата - когда сертификат проверяется, чтобы удостовериться, что это "кто" утверждает, что это так. Это делается путем проверки его по отношению к его родительскому ЦС (и рекурсивно все ЦС в цепочке проверяются до достижения корневого ЦС). Если все в порядке, то сертификат действителен. Это, конечно, очень упрощенная версия, тема довольно сложная, но в Интернете можно найти много информации. Само собой разумеется, что объект, который выполняет проверку, должен иметь доступ к CA в цепочке. Вы можете взять веб-браузер в качестве примера, его сертификат "хранилище" содержит:
- Дополнительно: сертификаты (EU), которые были предоставлены вам для возможности подключения к некоторым сайтам (например, личные сертификаты)
- Сертификаты (CA), необходимые для проверки других сертификатов, представленных веб-серверами (Trusted Root/Intermediate Certification Authorities)
[IBM]: обзор рукопожатия SSL или TLS (хотя есть много других мест) кратко описывает, как работает соединение SSL.
Как правило, защищенная система будет выдавать (уникальный) сертификат (ЕС) для каждого из своих клиентов; этот сертификат будет привязан к клиентскому компьютеру (* IP8-адрес или полное доменное имя); здесь CRL и OCSP стоит упомянуть.
Вернемся к вопросу: поскольку у нас есть особая ситуация (2 сертификата, где каждый является CA, но они также используются как ЕС), это может быть не так очевидно, но я сделаю все возможное, чтобы объяснить. Учитывая, что проверка сертификата будет происходить на обоих концах связи (двусторонняя), оба сертификата должны быть загружены в обоих приложениях. Например, в серверном приложении:
- сертификат сервера должен быть загружен, чтобы быть представленным любому клиенту, который подключается, чтобы клиент мог его проверить
- клиентский сертификат должен быть загружен (как CA) для проверки сертификата, который представляет любой клиент (который будет тем же сертификатом). Взгляните на [Python 2]: ssl. wrap_socket (sock, ключевой файл= Нет, certfile = Нет, server_side = False, cert_reqs = CERT_NONE, ssl_version = {см. документы}, ca_certs = Нет, do_handshake_on_connect = True, suppress_ragged_eofs = True, аргумент caser = True, шифры = None = шифр
Очевидно, что для клиента все будет наоборот.