LINUX.ORG.RU

SSL: NO_SHARED_CIPHER

 , ,


0

1

Добрых суток.

Есть небольшой многопоточный сервер на питоне, которому нужно обслуживать ssl подключения. Для определения имени хоста и выбора подходящего сертификата, используется SNI в TLS. Для этого использую set_servername_callback, для задания функции в которой происходит обработка имени сервера.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import time
import socket
import threading
import ssl

class client(threading.Thread):
	def __init__(self, sock, host, port):
		threading.Thread.__init__(self)
		self.daemon = True
		self.srvname = None
		self.sock = sock
		self.host = host
		self.port = port
		self.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		self.context.set_servername_callback(self.sni)

	def sni(self, sock, sni, context):
		self.srvname = sni
		if self.srvname:
			self.context.load_cert_chain(certfile = self.srvname + '.crt', keyfile = self.srvname + '.key')
		else:
			self.context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		print "SNI: ", sni

	def run(self):
		try:
			sslsock = self.context.wrap_socket(self.sock, server_side=True)
			sslsock.settimeout(5)
                        ...тут что-то делаем...
		except socket.error as msg:
			print "Exception in client thread: " + str(msg)

class server(threading.Thread):
	def __init__(self, host, port):
		threading.Thread.__init__(self)
		self.daemon = True
		self.port = port
		self.host = host
		self.clients = []
		self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

	def run(self):
		self.sock.bind((self.host, self.port))
		self.sock.listen(5)
		while True:
			sock, addr = self.sock.accept()
			clnt = client(sock, addr[0], addr[1])
			clnt.start()
			self.clients.append(clnt)

if __name__ == '__main__':
	srv = server('127.0.0.1', 9999)
	srv.start()

	try:
		while True:
			time.sleep(1)

	except KeyboardInterrupt:
		print "\rGoodbye!"
		sys.exit()

Но как бы я не менял код получаю такое:

SNI: SNI:  server.local.ru
 server.local.ru
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
 Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
SNI:  server.local.ru
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
Exception in client thread: [SSL: INAPPROPRIATE_FALLBACK] inappropriate fallback (_ssl.c:590)
SNI:  server.local.ru
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
SNI:  server.local.ru
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
Exception in client thread: [SSL: INAPPROPRIATE_FALLBACK] inappropriate fallback (_ssl.c:590)

Есть ли способ заставить работать такую конструкцию?

Заранее спасибо.

★★★★★

Ответ на: комментарий от fornlr

Если без sni и коллбэка, то всё работает.

class client(threading.Thread):
	def __init__(self, sock, host, port):
		threading.Thread.__init__(self)
		self.daemon = True
		self.srvname = None
		self.sock = sock
		self.host = host
		self.port = port
		self.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

	def run(self):
		try:
			self.context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
			sslsock = self.context.wrap_socket(self.sock, server_side=True)
			sslsock.settimeout(5)
			...что-то делаем...
		except socket.error as msg:
			print "Exception in client thread: " + str(msg)

в этом случае нет ошибки

cyclon ★★★★★ ()
Ответ на: комментарий от Harald

а что тут не так?

если:

self.context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
или так
self.context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
такое то же не прокатывает

cyclon ★★★★★ ()
Последнее исправление: cyclon (всего исправлений: 2)
Ответ на: комментарий от cyclon

современные софтинки, претендующие на секурность, не пользуются протоколами младше TLS 1.2

я не знаю, что там за реализация у пистона, но попробуй .PROTOCOL_TLSv12

Harald ★★★★★ ()
Ответ на: комментарий от cyclon

надо бы посмотреть, какой набор криптоалгоритмов предлагает клиент, и вообще кто клиент, что за софтина?

для отладки можно заюзать openssl s_server и openssl s_client

Harald ★★★★★ ()

Ещё интересный момент, если опустить проверку sni до уровня accept'а, основной поток сервера, то вроде как работает, правда на одно из 4х подключений лисы приходит [SSL: NO_SHARED_CIPHER] no shared cipher

cyclon ★★★★★ ()
Ответ на: комментарий от Harald

Нет, не this. Это просто устаревшее название, но значит он совершенно не то, что в названии. По умолчанию будет пробоваться TLS 1.2 (если openssl не очень старый), с возможностью понижения версии протокола до SSL v2. Но если выставить OP_NO_SSLv2 и OP_NO_SSLv3, то будет только TLS.

Vovka-Korovka ★★★★★ ()
Ответ на: комментарий от Harald

находит, когда не находит, там другая ошибка:

Exception in client thread: [SSL: CLIENTHELLO_TLSEXT] clienthello tlsext (_ssl.c:590)
Exception IOError: (2, 'No such file or directory') in <bound method client.sni of <client(Thread-4, started daemon 140162298279680)>> ignored

cyclon ★★★★★ ()
Последнее исправление: cyclon (всего исправлений: 1)

Ты как сервер свой тестируешь ?

[SSL: NO_SHARED_CIPHER] - значит, что у клиента с сервером не пересекающиеся наборы алгоритмов шифрования.

Vovka-Korovka ★★★★★ ()

Первое, ты хоть сокет, который слушаешь, оберни.

Второе, из документации

SLContext.set_servername_callback(server_name_callback)

A typical use of this callback is to change the ssl.SSLSocket‘s SSLSocket.context attribute to a new object of type SSLContext representing a certificate chain that matches the server name.

А ты вместо того, чтобы сокету context поменять, почему-то сам контекст меняешь.

Vovka-Korovka ★★★★★ ()
Ответ на: комментарий от Vovka-Korovka

Первое, ты хоть сокет, который слушаешь, оберни.

у меня работает вариант, когда обернут сокет установленного подключения, только без sni

cyclon ★★★★★ ()
Последнее исправление: cyclon (всего исправлений: 1)
Ответ на: комментарий от Vovka-Korovka

А ты вместо того, чтобы сокету context поменять, почему-то сам контекст меняешь.

The callback function, server_name_callback, will be called with three arguments; the first being the ssl.SSLSocket, the second is a string that represents the server name that the client is intending to communicate (or None if the TLS Client Hello does not contain a server name) and the third argument is the original SSLContext

Т.е. третий аргумент нужный контекст сокета, делаю так

def sni(self, sock, sni, context):
		self.srvname = sni
		if self.srvname:
			context.load_cert_chain(certfile = self.srvname + '.crt', keyfile = self.srvname + '.key')
		else:
			context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		print "SNI: ", sni

всё по старому, или опять не там контекст меняю?

Это то же ничего не дало:

def sni(self, sock, sni, context):
      ...
      sock.context.load_cert_chain(...)
      ...

То же самое и так:

def sni(self, sock, sni, context):
		self.srvname = sni
		sock.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		sock.context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		print ("SNI: " + sni)
		return None

cyclon ★★★★★ ()
Последнее исправление: cyclon (всего исправлений: 5)

Когда создаешь контекст в client, уже туда нужно загрузить дефолтный сертификат с ключом.

self.context.load_cert_chain("server.crt", keyfile="server.key")
Vovka-Korovka ★★★★★ ()

И sni переделай, туда не должно ничего пустого приходить. Если sni не включен, то будет работать дефолтный созданный SSLContext.

Vovka-Korovka ★★★★★ ()
Ответ на: комментарий от cyclon

То же самое и так:

Это правильный вариант. Не работало у тебя по другой причине.

Vovka-Korovka ★★★★★ ()
Ответ на: комментарий от Vovka-Korovka
class client(threading.Thread):
	def __init__(self, sock, host, port):
		threading.Thread.__init__(self)
		self.daemon = True
		self.srvname = None
		self.sock = sock
		self.host = host
		self.port = port
		self.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		self.context.load_cert_chain("server.crt", keyfile="server.key")
		if ssl.HAS_SNI:
			self.context.set_servername_callback(self.sni)

	def sni(self, sock, sni, context):
		self.srvname = sni
		sock.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		sock.context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		print ("SNI: " + sni)
		return None

	def run(self):
		try:
			sslsock = self.context.wrap_socket(self.sock, server_side=True)
			sslsock.settimeout(5)
			print ("GOT IT")
		except socket.error as msg:
			print ("Exception in client thread: " + str(msg))

Болт ((((

SNI: file.nethex.ru
SNI: file.nethex.ru
 Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
SNI: file.nethex.ru
Exception in client thread: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:590)
Exception in client thread: [SSL: INAPPROPRIATE_FALLBACK] inappropriate fallback (_ssl.c:590)

cyclon ★★★★★ ()
Ответ на: комментарий от Vovka-Korovka

Нашёл рабочий вариант)

	def sni(self, sock, sni, context):
		self.srvname = sni
		newcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		newcontext.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		sock.context = newcontext
		print ("SNI: " + sni)
		return None

Даже дефолтный chain не нужен.

Спасибо за помощь.

А вот так нельзя:

	def sni(self, sock, sni, context):
		self.srvname = sni
		sock.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
		sock.context.load_cert_chain(certfile = 'server.crt', keyfile = 'server.key')
		print ("SNI: " + sni)
		return None

cyclon ★★★★★ ()
Последнее исправление: cyclon (всего исправлений: 1)
Ответ на: комментарий от Vovka-Korovka

А если поддержка SNI выключена на клиенте ?

это понятно, я про то, что на работоспособности не сказывается, а так дефолтный chain конечно есть

cyclon ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.