Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to get SSL working on Tornado

I am trying to implement hiroakis's project (https://github.com/hiroakis/tornado-websocket-example) over SSL.

I made required changes (see below) and also added the Certificate Authority's Public Certificate to Firefox's trusted certificate list. When I open https://localhost:8888, I get

SSLError: [SSL: SSLV3_ALERT_BAD_CERTIFICATE] SSLv3 alert bad certificate (_ssl.c:1750)

(Entire Traceback):

WARNING:tornado.general:error on read
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/tornado/iostream.py", line 630, in _handle_read
    pos = self._read_to_buffer_loop()
  File "/usr/local/lib/python2.7/dist-packages/tornado/iostream.py", line 600, in _read_to_buffer_loop
    if self._read_to_buffer() == 0:
  File "/usr/local/lib/python2.7/dist-packages/tornado/iostream.py", line 712, in _read_to_buffer
    chunk = self.read_from_fd()
  File "/usr/local/lib/python2.7/dist-packages/tornado/iostream.py", line 1327, in read_from_fd
    chunk = self.socket.read(self.read_chunk_size)
  File "/usr/lib/python2.7/ssl.py", line 603, in read
    v = self._sslobj.read(len or 1024)
SSLError: [SSL: SSLV3_ALERT_BAD_CERTIFICATE] sslv3 alert bad certificate (_ssl.c:1750)
ERROR:tornado.general:Uncaught exception
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 691, in _server_request_loop
    ret = yield conn.read_response(request_delegate)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 810, in run
    yielded = self.gen.throw(*sys.exc_info())
  File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 166, in _read_message
    quiet_exceptions=iostream.StreamClosedError)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
SSLError: [SSL: SSLV3_ALERT_BAD_CERTIFICATE] sslv3 alert bad certificate (_ssl.c:1750)

Here is the python code:

from tornado import websocket, web, ioloop, httpserver
import json

cl = []

class IndexHandler(web.RequestHandler):
    def get(self):
        self.render("/var/www/html/index.html")

class SocketHandler(websocket.WebSocketHandler):
    def check_origin(self, origin):
        print "Connection Received from ",origin
        return True

    def open(self):
        if self not in cl:
            cl.append(self)

    def on_close(self):
        if self in cl:
            cl.remove(self)

class ApiHandler(web.RequestHandler):

    @web.asynchronous
    def get(self, *args):
        self.finish()
        id = self.get_argument("id")
        value = self.get_argument("value")
        data = {"id": id, "value" : value}
        data = json.dumps(data)
        for c in cl:
            c.write_message(data)

    @web.asynchronous
    def post(self):
        pass

app = web.Application([
    (r'/', IndexHandler),
    (r'/ws', SocketHandler),
    (r'/api', ApiHandler),
    (r'/(favicon.ico)', web.StaticFileHandler, {'path': '../'}),
    (r'/(rest_api_example.png)', web.StaticFileHandler, {'path': './'}),
])

if __name__ == '__main__':
    server = httpserver.HTTPServer(app, ssl_options = {
        "certfile": "/local_repo/keys/server.crt",
        "keyfile": "/local_repo/server.key",
    })
    server.listen(8888)                                                                                                                                                                                
    ioloop.IOLoop.instance().start()

Apart from that, I modified (r'/ws', SocketHandler) to (r'/wss', SocketHandler)

Similary, the modified index.html (which uses javascript to create socket connection) is:

Index.html

<!DOCTYPE html>
<html>
<head>
  <title>tornado WebSocket example</title>
  <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
</head>
<body>
  <div class="container">
    <h1>tornado WebSocket example</h1>
    <hr>
      WebSocket status : <span id="message"></span>
    <hr>
    <h3>The following table shows values by using WebSocket</h3>

      <div class="row">
        <div class="span4">
          <table class="table table-striped table-bordered table-condensed">
            <tr>
              <th>No.</th><th>id</th><th>value</th>
            </tr>
            <tr id="row1">
              <td> 1 </td><td> id 1 </td><td id="1"> 0 </td>
            </tr>
            <tr id="row2">
              <td> 2 </td><td> id 2 </td><td id="2"> 0 </td>
            </tr>
            <tr id="row3">
              <td> 3 </td><td> id 3 </td><td id="3"> 0 </td>
            </tr>
          </table>
        </div>
        <div class="span4">
          <table class="table table-striped table-bordered table-condensed">
            <tr>
              <th>No.</th><th>id</th><th>value</th>
            </tr>
            <tr id="row4">
              <td> 4 </td><td> id 4 </td><td id="4"> 0 </td>
            </tr>
            <tr id="row5">
              <td> 5 </td><td> id 5 </td><td id="5"> 0 </td>
            </tr>
            <tr id="row6">
              <td> 6 </td><td> id 6 </td><td id="6"> 0 </td>
            </tr>
          </table>
        </div>
        <div class="span4">
          <table class="table table-striped table-bordered table-condensed">
            <tr>
              <th>No.</th><th>id</th><th>value</th>
            </tr>
            <tr id="row7">
              <td> 7 </td><td> id 7 </td><td id="7"> 0 </td>
            </tr>
            <tr id="row8">
              <td> 8 </td><td> id 8 </td><td id="8"> 0 </td>
            </tr>
            <tr id="row9">
              <td> 9 </td><td> id 9 </td><td id="9"> 0 </td>
            </tr>
          </table>
        </div>
      </div>

    <hr>
    <h3>REST API examples (use appropriate certificates with curl)</h3>
    <ol>
      <li>Set the "id 1" value to 100
        <ul><li>curl "https://localhost:8888/api?id=1&amp;value=100"</li></ul>
      </li>
      <li>Set the "id 1" value to 300 ( The row No 1 will change to yellow )
        <ul><li>curl "https://localhost:8888/api?id=1&amp;value=300"</li></ul>
      </li>
      <li>Set The "id 1" value to 600 ( The row No 1 will change to red )
        <ul><li>curl "https://hiroakis.com:8888/api?id=1&amp;value=600"</li></ul>
      </li>
    </ol>
    <ul>
      <li>value 201 - 500 : change to yellow</li>
      <li>value 501 - : change to red</li>
    </ul>
    <img src="./rest_api_example.png"/>
  </div>
  <script>
    var ws = new WebSocket('wss://localhost:8888/ws');
    var $message = $('#message');

    ws.onopen = function(){
      $message.attr("class", 'label label-success');
      $message.text('open');
    };
    ws.onmessage = function(ev){
      $message.attr("class", 'label label-info');
      $message.hide();
      $message.fadeIn("slow");
      $message.text('recieved message');

      var json = JSON.parse(ev.data);
      $('#' + json.id).hide();
      $('#' + json.id).fadeIn("slow");
      $('#' + json.id).text(json.value);

      var $rowid = $('#row' + json.id);
      if(json.value > 500){
        $rowid.attr("class", "error");
      }
      else if(json.value > 200){
        $rowid.attr("class", "warning");
      }
      else{
        $rowid.attr("class", "");
      }
    };
    ws.onclose = function(ev){
      $message.attr("class", 'label label-important');
      $message.text('closed');
    };
    ws.onerror = function(ev){
      $message.attr("class", 'label label-warning');
      $message.text('error occurred');
    };

  </script>
</body>
</html>

I created SSL certificates using these steps:

Create the CA private key:

openssl genrsa -des3 -out servercakey.pem 

Create the CA public certificate (When you create a certificate, there must be one unique name (a Distinguished Name (DN)), which is different for each certificate that you create):

openssl req -new -x509 -key servercakey.pem -out root.crt 

Create the server's private key file:

openssl genrsa -out server.key 

Create the server certificate request:

openssl req -new -out reqout.txt -key server.key 

Use the CA private key file to sign the server's certificate:

openssl x509 -req -in reqout.txt -days 3650 -sha1 -CAcreateserial -CA root.crt -CAkey servercakey.pem -out server.crt 

Create the client's private key file:

openssl genrsa -out client.key 

Create the client certificate request:

openssl req -new -out reqout.txt -key client.key 

Use the CA private key file to sign the client's certificate:

openssl x509 -req -in reqout.txt -days 3650 -sha1 -CAcreateserial -CA root.crt -CAkey servercakey.pem -out client.crt 

Creating pem file for Server:

cat server.crt root.crt > server.pem
like image 882
mohitsharma44 Avatar asked Mar 16 '23 15:03

mohitsharma44


1 Answers

Finally today I was able to find the source of problem.

When I was creating the certificates, in the FQDN (fully qualified domain name) part of creating certificate, I had entered 127.0.0.1 instead of localhost)

Whats surprising is that neither chrome nor firefox alerted me that I was accessing a website with the CA whose subject name did not match the target's host name.

It was only when I tried with curl, curl https://localhost:8888/ that it alerted me.

I think browsers are supposed to do that, don't they?

I also noted that my /etc/hosts file has 127.0.0.1 mapped to localhost. Then why is it that transferring data using curl to localhost fails but to 127.0.0.1 succeeds?

like image 151
mohitsharma44 Avatar answered Mar 24 '23 17:03

mohitsharma44