简体   繁体   中英

How to get jCryption work with django and python-rsa (or another python library)

I want to send some secret data to the client, so I want to encrypt it.

I'm using jCryption in client side. It uses handshaking in the beginning of the connection, the procedures happen as follows:

  1. client generate random AES password.
  2. client request RSA publicKey form server
  3. server send it
  4. Client use RSA public key to encrypt the password and send it to server.
  5. Now, both use that password to encrypt data they send to each other.

My problem is in the step of sending the password encrypted with RSA (step 4), since jCryption sends it in hex format, but python-rsa expect binary integer ,,

How to convert the jCryption output to a format python-rsa can deal with, or is there are another library that can do it ?

the RSA ciphertext is look like this:

TO\\xa75[\\x9a\\x07s4\\x86\\xbc\\xae\\xe3\\xd5s)1\\x0cd5\\xdfY\\xf7\\xeds9\\xf3~\\n\\x9fA$\\xa9\\xfb;\\x04\\x1e\\x18\\xf4\\xea\\x7f\\x91\\xd9\\xb7[\\xd3\\x138\\xb6b\\x9c\\xb6\\x1b\\xe7\\x11\\x9aB\\x1d@`y\\x9c0\\xe8\\xb6!\\x8b~lg\\xabO\\xbeny\\xf7Xu\\x89YW\\xb0\\xda@\\x10\\x0c\\xe7\\x85\\x9bX\\x8f\\x02e\\xdalf|\\xa6\\x0e\\x8e\\x8e\\x9d\\xd8=\\x9bQLO7\\x0fd\\x19/]t?\\xf1\\x96\\x1b\\xb9\\x8bv\\xb4\\xb4rS\\x1c\\xb9

and data send from jCryption is looked like this:

11a6ebb863c379255df711aba86ad3986d6ecc33402a1596e6036b8d33f41932909a3e8c10cc4e0d2ece5f369808020ac7241a4285c80e6e483a1f6b43d933149961f50b72a808c769d39215ce08c33cfdb543b68bb0cf644f32dccf7eb90547290d47b96758449df3e7d4ec 2b50aef21ff4735c79f74bf5214ff356e4338ff2b292110ad537d160e41e34b350c7bc857601a943f915285e62f308fb6bd61d275321b68fbf27a52fbffc27b9ad15810795ccdea6d9776246b84b00503c2711d49a3f101af6f2c822d697a71aeca684e20328071ce84da907

Ok I've done this quest but first I want to say that it's much easier to obtain LetsEncrypt free cert for https what i did later.

For this solution you need openssl to be installed.

Lets write views for our ajaxes

Getting public key. If we don't have one in project directory then generate the pair.

    def public_key(req):
        if not os.path.isfile(os.path.join(settings.BASE_DIR, 'form_key.pem')) or not os.path.isfile(os.path.join( settings.BASE_DIR,'form_key_pub.pem')):
            check_call(['openssl', 'genrsa', '-out', os.path.join(settings.BASE_DIR,'form_key.pem'), '4096'])
            check_call(['openssl', 'rsa', '-pubout', '-in', os.path.join(settings.BASE_DIR,'form_key.pem'), '-out', os.path.join(settings.BASE_DIR,'form_key_pub.pem')])
        f = open(os.path.join(settings.BASE_DIR,'form_key_pub.pem'))
        key = f.read()
        f.close()
        return JsonResponse({"publickey": key})

Ok, and the handshake. To CSRF protect this view we need to patch the jCryption javascript library what i didn't. I save AES key in session storage here.

    @csrf_exempt
    def handshake(req):
        if req.method == 'POST':
            encb64key = req.POST['key']
            encb64key = re.sub(r'[^a-zA-Z0-9/=+]', '', encb64key)
            enckey = b64decode(encb64key)
            openssl = Popen(['openssl', 'rsautl', '-decrypt', '-inkey', os.path.join(settings.BASE_DIR,'form_key.pem')], stdin = PIPE, stdout=PIPE, stderr=PIPE)
            key, stderr = openssl.communicate(enckey)
            print stderr
            key = re.sub(r'[^a-zA-Z0-9]', '', key)
            req.session['form_key'] = key 
            openssl = Popen(['openssl', 'enc', '-aes-256-cbc', '-pass', 'pass:'+key, '-a', '-e'], stdin = PIPE, stdout = PIPE, stderr = PIPE)
            enckey , stderr = openssl.communicate(key)
            print stderr
            enckey = re.sub('[^a-zA-Z0-9/+=]', '' , enckey)
            return JsonResponse({'challenge': enckey})
        raise Http404() 

Lets choose urls for the views in urls.py

    url('^pubkey', public_key, name = 'publickey'),
    url('^handshake', handshake, name = 'handshake'),

And the most tricky part. Our own middleware. You need to add it to MIDDLEWARE_CLASSES in settings.py . Something like 'myapp.views.JCryptionMiddleware' if you place it in your myapp's views.py file.

The trick is that we send the wrong POST data with only 'jCryption' attr. The middleware decrypts apropriate data in this attr and rewrites the POST data in the request object with it. Read about middlewares in Django documentation.

    class JCryptionMiddleware(object):
        def process_view(self, request, callback, callback_args, callback_kwargs):
            jcryptedb64 = request.POST.get('jCryption', '')
            if jcryptedb64:
                try:
                    jcrypted = b64decode(jcryptedb64)
                    p = Popen(['openssl', 'enc', '-aes-256-cbc', '-pass', 'pass:'+request.session['form_key'], '-d'], stdin = PIPE, stdout = PIPE, stderr = PIPE)
                    qstr, stderr = p.communicate(jcrypted)
                    print stderr
                    wasmutable = request.POST._mutable
                    request.POST._mutable = True
                    request.POST.__init__(qstr)
                    request.POST._mutable = wasmutable
                except Exception as e:
                    print e
            return None

And the client code in the page with the form template.

    <script src="{{ STATIC_URL }}js/jquery.min.js"></script>
    <script src="{{ STATIC_URL }}js/jcryption.js"></script>
    <script>
    $(function() {
        $('form').jCryption({"getKeysURL": "/pubkey", "handshakeURL": "/handshake"});
    });
    </script>

See the urls from our urls.py .

For example you can encrypt your admin login form. Copy login.html from django contrib admin to templates/admin/login.html and add this javascript code to the template.

ta da! Don't use this, use HTTPS.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM