User can't communicate with proxy - python

I'm implementing an IMAP proxy which securely communicates with a client.However, I have a problem when handshaking.
The code of my proxy is:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind((host, port))
ssock, addr = self.sock.accept()
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
self.conn_client = context.wrap_socket(ssock)
And I receive the error:
ssl.SSLError: [SSL: UNEXPECTED_MESSAGE] unexpected message (_ssl.c:833)
The code of my tests is:
M = imaplib.IMAP4_SSL(IP_PROXY)
And I receive the error:
ssl.SSLError: [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:777)
However, when the code of the proxy is:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind((host, port))
ssock, addr = self.sock.accept()
self.conn_client = ssl.wrap_socket(ssock, certfile=CERT, server_side= True)
It correctly works but I don't want to use certificate.
Thank you

It correctly works but I don't want to use certificate.
SSL/TLS is almost everywhere used with a certificate to make sure that the client is talking to the expected server and not to some man in the middle. If you don't want to use a certificate you need to either use a different kind of authentication (like PSK) or use no authentication at all ("anonymous authentication" - very bad idea).
In any way you would need to set the relevant ciphers to enable this alternative authentication on both client and server. This can be done with the ciphers attribute to wrap_socket on the server side and in your client code it could probably be done by constructed a SSLContext with the necessary ciphers and using the ssl_context argument to specific the context to be used in IMAP4_SSL.
But this is only for your specific Python based IMAP client. Don't expect that you will be able to configure commonly used IMAP clients like Thunderbird or Outlook to be usable with a server without certificates. And like I said, it is a bad idea in the first place.

Related

How to detect if a server is using SNI for HTTPS with python

I'm trying to detect if a server is using the Server Name Indication SSL extension for its HTTPS certificate on a website.
Reproducing this command line with python
openssl s_client -servername www.SERVERNAME.com -tlsextdebug -connect www.YOURSERVER.com:443
Python :
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=sni) as sslsock:
Code below return error
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'sni'. (_ssl.c:992)
But openssl return the certificate correct 💯
Unfortunately im not that experienced in python
Thanks!
with context.wrap_socket(sock, server_hostname=sni) as sslsock:
Check with server_hostname set to the real server name (to use SNI) and to None (to not use SNI). If the results differ (i.e. connection fails, different certificates) then the server requires SNI.To check the certificate use getpeercert.
Depending on the kind of server you might need to disable certificate validation, i.e. set CERT_NONE for the context. While disabling certificate is a bad idea in general, it is acceptable in this case since all you want is to check the servers capabilities and not actually transfer any application data:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
In this case you need to call socket.getpeercert(binary_form=True) since it otherwise will not return information. For comparison this binary form should be sufficient though.

Python ssl socket server SSLV3_ALERT_CERTIFICATE_UNKNOWN issue

I am writing a python socket server with ssl and I am encountering certificate unknown error during ssl handshake.
I have created private key and certificate with openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes command on my own.
This server is intended to run in intranet(under wifi) in my PC, and users will contact my PC IPaddress with their browser. Hence I have not registered this certificate with any CA. and I don't think its mandatory in my case.
Below more details..
echoserver.py
import socket
import ssl
import threading
class echoserver:
def __init__(self,i,p):
self.ip=i
self.port=p
def handler(self,c,a):
msg=c.recv(1024)
c.send(msg)
def serve(self):
echoserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
echoserver = ssl.wrap_socket(echoserver, keyfile='keys/key.pem', certfile='keys/cert.pem', server_side=True)
echoserver.bind((self.ip, self.port))
echoserver.listen()
while True:
(c,a)=echoserver.accept()
threading.Thread(target=self.handler, args=(c,a)).start()
es=echoserver('192.168.43.124',443) #My PC's ip assigned under wifi network
es.serve()
#Connecting from mobile phone within same network as https://192.163.43.124
Error in server during ssl handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1108)
What I tried
Adding cert_reqs=ssl.CERT_NONE and ca_certs="/location/to/keys" parameters in wrap_socket function.
Doesn't seems to work. I assume these options are for client side.
Adding do_handshake_on_connect=False in wrap_socket function
In Chrome, When connected server throws same error and thread/connection closed with exception. and chrome seems to send same connection request immediately again, and the second request works flawlessly !!.
In firefox, First connection closed with same error and there is no second request.
Assigning common name in certificate same as IP address
Not working.
Checked certificate_unknown error in ietf specification here. It gives no clue except this explanation certificate_unknown: Some other (unspecified) issue arose in processing the certificate, rendering it unacceptable.
One other thing I noted is, if I use built-in ThreadedHTTPServer in the same way as below, It works beautifully, without any issues I mentioned above.
httpd = self.ThreadedHTTPServer((self.ip, self.port), self.handler)
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile='keys/key.pem', certfile='keys/cert.pem', server_side=True)
I am not sure why this happens and how should I proceed with this. and not sure how built-in server modules works fine.
Appreciate any leads. Thanks.
Below Python builtin HTTPServer works fine with ssl, not showing any error. Not sure how?
import ssl
from http.server import HTTPServer, BaseHTTPRequestHandler
class requesthandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type","text/html")
self.end_headers()
self.wfile.write("<html><body>It works</body></html>".encode('utf8'))
httpd = HTTPServer(('192.168.43.124', 443), requesthandler)
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile='keys/key.pem', certfile='keys/cert.pem', server_side=True)
httpd.serve_forever()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1108)
This means the client (browser) does not trust your certificate since it is issued by an unknown entity. If you want to use self-signed certificates you have to explicitly import these as trusted for all clients you want to use.
There is no way around this. The certificate in TLS is to make sure that the connection is done with the expected server and not some man in the middle claiming to be the expected server. If a client would trust arbitrary certificates then it would also trust certificates created by a man in the middle attacker.
Below Python builtin HTTPServer works fine with ssl, not showing any error. Not sure how?
The browser will still complain.
The only difference is that the server captures the exception and thus will not crash but continue. You can do the same in your code:
while True:
try:
(c,a)=echoserver.accept()
threading.Thread(target=self.handler, args=(c,a)).start()
except:
pass # ignore error

Creating an SSL socket with python

I'm using Python 2.4.4 and OpenSSL 0.9.8k (not by choice)
I've referred to the documentation: https://docs.python.org/release/2.4.4/lib/module-socket.html
and to pretty much every mention of "openSSL" and "python" on the internet, and I haven't found a solution to my problem.
I'm simply writing a test program to initiate an SSL connection. Here is the code:
server
#!/usr/bin/python
import socket
import _ssl
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 4433))
s.listen(5)
while True:
client, address = s.accept()
ssl_client = socket.ssl(client,
keyfile='keyfile',
certfile='certfile')
print "Connection: ", address
data = ssl_client.read(1024)
if data:
print "received data: ", data
ssl_client.write(data + " Hello, World!")
del ssl_client
client.close()
client
#!/usr/bin/python
import socket
import _ssl
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('host', 4433))
ssl_s = socket.ssl(s,
keyfile='keyfile',
certfile='certfile')
print 'writing ', ssl_s.write("Hello, World!"), ' bytes to ssl stream'
data = ssl_s.read(1024)
del ssl_s
s.close()
print "received data: ", data
Some notes about this code - keyfile and certfile are paths to my actual key and cert file. Those arguments are not the issue. The hostnames are also not the issue. I'm aware that the port used is 4433 - in our requirements, we're meant to use a generic port, not 443. I was unaware that it was possible to use SSL over a different port, but regardless, even when I use 443 I get the exact same error.
I can run the server fine, and then when I run the client, I get the following error on the wrap_socket lines for both client and server:
error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
I've read it's due to using a non-443 port, but again, using 443 didn't fix things. I've read it could be a protocol mismatch, but the client and the server are both defaulting to SSL2.3. We're meant to use TLS1.2 as per our requirements, but the docs don't seem to have any information on how to set the SSL protocol version. I'm unsure if that's related to my issue. Please keep in mind I'm not here to open a dialogue regarding to use of outdated SSL and Python versions.
socket.ssl is only able to initiate a SSL connection and the given optional cert and key are for use of client certificates. socket.ssl is not able to be used on the server side and it looks like python 2.4.4 does not offer this feature in any of the core modules at all. In later versions of python you can use the ssl module for this but 2.4.4 does not seem to have this.

Use Python to get an smtp server certificate

What's the easiest way to connect to a SMTP server that supports STARTTLS and get its server SSL certificate? I know it can be done using openssl with something like this
openssl s_client -starttls smtp -crlf -connect 192.168.0.1:25
How can I do it from within Python and I don't want to call openssl and parse its output. I looked at M2Crypto which is an openssl wrapper, but as far as I can tell that doesn't support starttls. An example of how to do it with a Python library would be very much appreciated.
This returns a certificate in binary format (DER-encoded):
import socket, ssl
s = socket.socket()
s.connect(("host", 25))
s.send("STARTTLS\n")
s.recv(1000)
ss = ssl.wrap_socket(s)
certificate_der = ss.getpeercert(True)
This is jus to give you an idea, error handling, etc. is required of course. If you want to decode the information from the certificate you either have to prodivde a certificate authorities bundle/directory for acceptable CAs (getpeercert() will return a meaningfull dict in this case), or use a more capable ssl library, M2Crypto for example.
You could try something like:
import ssl
cert = ssl.get_server_certificate(('imap.gmail.com',993))
to get server's certificate
As I can't comment abbot answer, just remark that depending on the server config you may need to send an EHLO before STARTTLS:
import socket, ssl
hostname = 'test.com'
port = 25
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
sock.recv(1000)
sock.send(b'EHLO\nSTARTTLS\n')
sock.recv(1000)
with context.wrap_socket(sock, server_hostname=hostname) as sslsock:
der_cert = sslsock.getpeercert(True)
smtplib provides the starttls() method which should deal with all issues:
http://docs.python.org/library/smtplib.html

No cipher can be selected

I am having problems making ssl connections with IDEA-CBC-SHA on python 2.7 / Win XP.
This is the code:
ciphers = "IDEA-CBC-SHA"
ssl_sock = ssl.wrap_socket(self.sock,
keyfile = keyfile,
certfile = certfile,
ciphers = ciphers)
ssl_sock.connect((address, port))
And this is the result:
SSLError: _ssl.c:319: No cipher can be selected.
When I query openssl, I see IDEA-CBC-SHA as one of the listed ciphers
OpenSSL> ciphers
DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DES-CBC3-MD5:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:AES128-SHA:IDEA-CBC-SHA:IDEA-CBC-MD5:RC2-CBC-MD5:RC4-SHA:RC4-MD5:RC4-MD5:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:DES-CBC-MD5:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC2-CBC-MD5:EXP-RC4-MD5:EXP-RC4-MD5
I've tested with other ciphers (DES-CBC3-SHA ,RC4-SHA, AES256-SHA, AES128-SHA), and they all worked fine.
Any idea why my ssl connection might be failing for this specific cipher?
Is there some way to disable (and then enable) ciphers?
Thanks!
Any idea why my ssl connection might be failing for this specific cipher?
Perhaps the server you are connecting to does not support IDEA. A packet capture would confirm this.
EDIT: In an ssl connection, the client suggests a list of cipher options it is willing to use. The server selects one of the options from that list that the server finds acceptable. If the server does not like any options the client proposed, then the connection will fail, because the server is unable to select a cipher suite.

Categories