3

I am totally new in Cryptography. I want to generate RSA key pairs from server side and send it to all of the clients (browsers). But before that I am testing the scenario by simply encrypting data in python and sending in to index.html file via pubnub and trying to decrypt it in JavaScript. The problem is that when I do encryption by;

random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
enc = enc_data[0] 

and send the encrypted data enc, it gives me this error:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 2: invalid continuation byte

I tried to convert it into

enc = base64.b64encode(enc_data[0])

and it is send with no error. but the JS decrypt method gets None

  var enc_from_python = $('#input').val();
  console.log("ENCRYPTED data:", enc_from_python);
  var decrypt = new JSEncrypt();
  decrypt.setPrivateKey($('#privkey').val());
  var uncrypted = decrypt.decrypt(enc_from_python);
  console.log(">>>",uncrypted);  //<-- this is None ! why ?

Both of the code do enc/dec very well on their own. I have also tried to enc/dec the data in JS with the received key pairs from python and that works well. I guess the problem is with the unicode encoding format of encoded data from Pycrypto which does not match. Could any one tell me what am I missing over here.

Full code for Python:

import time
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub

from Crypto.PublicKey import RSA
from Crypto import Random
import base64

pnconfig = PNConfiguration()
pnconfig.subscribe_key = 'demo'
pnconfig.publish_key = 'demo'
channel = "my_channel" 
pubnub = PubNub(pnconfig)

def my_publish_callback(envelope, status):
    if not status.is_error():
        pass  # Message successfully published to specified channel.
    else:
        pass  # Handle message publish error. Check 'category' property to find out possible issue


time.sleep(1)
random_generator = Random.new().read
key = RSA.generate(1024, random_generator)
print key.exportKey() #<--private key
public_key = key.publickey()
print public_key.exportKey() #<--public key
msg = "hello"
enc_data = public_key.encrypt(msg, 32)
print '----ENCRYPTED DATA----'
#enc = enc_data[0]
enc = base64.b64encode(enc_data[0])

print enc        
#print type(enc_data[0])
print '----ENCRYPTED DATA----'
print  ''
print '----DECRYPTED DATA begin----'
print key.decrypt(enc_data[0])
print '----DECRYPTED DATA end----'      

pubnub.publish().channel(channel).message({"data": enc , "private": (key.exportKey()), "public" : (public_key.exportKey())}).async(my_publish_callback)         

and full JS code is;

<!doctype html>
<html>
  <head>
    <title>JavaScript RSA Encryption</title>
    <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
    <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.12.0.min.js"></script>
    <script type="text/javascript">

      // Call this code when the page is done loading.
      $(function() {
        pubnub = new PubNub({
          publish_key: 'demo',
          subscribe_key: 'demo'
        });  

        pubnub.subscribe({
            channels: ['my_channel']
        }); 

    pubnub.addListener({
        message: function(message) {
            var msg = message.message;
            console.log("msg:" + JSON.stringify(msg));

             if (msg.private){
                $("#privkey").val(msg.private);
             }

             if(msg.public){
                $("#pubkey").val(msg.public);
             }

             if(msg.data){
                $("#input").val(msg.data);
             }           
        }    
    })      

        // Run a quick encryption/decryption when they click.
        $('#testme').click(function() {
          var enc_from_python = $('#input').val();
          console.log("ENCRYPTED data:", enc_from_python);
          // Decrypt with the private key...
          var decrypt = new JSEncrypt();
          decrypt.setPrivateKey($('#privkey').val());
          var uncrypted = decrypt.decrypt(enc_from_python);
          console.log(">>>",uncrypted);  //<-- this is None ! why ?
          // Now a simple check to see if the round-trip worked.
          if (uncrypted == $('#input').val()) {
            alert('It works!!!');
          }
          else {
            alert('Something went wrong....');
          }
        });
      });
    </script>
  </head>
  <body>
    <label for="privkey">Private Key</label><br/>
    <textarea id="privkey" rows="15" cols="65">   </textarea><br/>
    <label for="pubkey">Public Key</label><br/>
    <textarea id="pubkey" rows="15" cols="65">    </textarea><br/>
    <label for="input">Text to decrypt:</label><br/>
    <textarea id="input" name="input" type="text" rows=4 cols=70>This is a test!</textarea><br/>
    <input id="testme" type="button" value="Decrypt Me!!!" /><br/>
  </body>
</html>
9
  • Depending on the algorithm implementation the python and javascript maybe be a bit different, sad but true Commented Jul 3, 2017 at 19:46
  • 2
    JSencrypt uses PKCS#1 v1.5 padding whereas your Python code doesn't use any padding (textbook RSA is bad). Commented Jul 3, 2017 at 19:54
  • 1
    You made a fundamental, all breaking error in applying RSA: you never transmit the private key. It always remains on the side where it was generated. Commented Jul 3, 2017 at 20:42
  • 1
    The length of the encrypted data is fine, it is the data to be encrypted must have a length less than the key length. But you are signing with the private key not encryption, right? It might be a good idea to have the entire authentication scheme reviewed. Commented Jul 3, 2017 at 20:53
  • 1
    If you're using asymmetric encryption, you need to make sure that the public key or private key that is delivered to the browser can be trusted. This is not an assumption that can generally be made, because any kind of JavaScript code can be injected into a HTTP connection. You would need HTTPS (with TLS) to protect the public key from being changed, but if you have that, you don't need JavaScript encryption anymore. Commented Jul 3, 2017 at 21:59

1 Answer 1

11

If you use Crypto.Cipher.PKCS1_v1_5 it is possible.

Here's the python code.

I have only tested with python 3, but I believe it should work the same in python 2 with the __future__ imports.

from __future__ import unicode_literals, print_function  # python2
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64

private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----"""

rsa = RSA.importKey(private_key)
cipher = PKCS1_v1_5.new(rsa)

def encrypt(msg):
    ciphertext = cipher.encrypt(msg.encode('utf8'))
    return base64.b64encode(ciphertext).decode('ascii')

def decrypt(msg):
    ciphertext = base64.b64decode(msg.encode('ascii'))
    plaintext = cipher.decrypt(ciphertext, b'DECRYPTION FAILED')
    return plaintext.decode('utf8')

ciphertext = encrypt('hello stackoverflow!')
print(ciphertext)
plaintext = decrypt(ciphertext)
print(plaintext)

Sample ciphertext output from plaintext "hello stackoverflow!":

tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==

In javascript, the original code should work. Here's a simplified version to demonstrate that this specific ciphertext and private rsa key works.

const decrypt = () => {
  const privateKey = document.getElementById('private_key').value
  const cipherText = document.getElementById('ciphertext').value
  const decrypt = new JSEncrypt()
  decrypt.setPrivateKey(privateKey)
  const plainText = decrypt.decrypt(cipherText) || 'DECRYPTION FAILED'
  document.getElementById('plaintext').innerHTML = plainText
}
document.querySelector('button').addEventListener('click', decrypt)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js"></script>
<button>Decrypt</button>

<p>plaintext: <em id=plaintext></em> </p>

<label>ciphertext:</label><br>
<textarea cols=64 rows=3 id=ciphertext>
tZDRXXcf7ppbVr9JBHQ3+2k3geofl8BdDmLT3HRoqBGvfknY+xISbvy5hYH2alPAUDu2ae4iSYsLyRFBOnzpgw==
</textarea>
<br>
<label>private key:</label><br>
<textarea cols=64 rows=10 id=private_key>
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANBOMQo9wX55+w1ijEaPoYRP2T4BOjoFv3ma0QWqYYQ8FH0z14Zc
B/jb0j2PWpyNcsUUBovj+yWxQnQohCck64kCAwEAAQJBAL4s9PbNpO9MfFkfBMSS
8zoyEDtcsYUxpDtojbandDpdXfvn5D279QaOVLb1C3DgQTTEmroYB8dbeZBc5YJC
2AECIQDqyUn68ehRcx/EyLMUB1IuckZBWCIApgfn7phgVwSwiQIhAOMgY4bN+xrx
UV15Ian4ZbkME1IbAvDPcWuNGHxdsaMBAiBoz0K/S44yDfp4lj+bCUmeglTqhrVn
JLcSymgrWa02QQIhAMJFvPvcilGkYl1atCHHt3LN0mTjd+N0/OXq3SvblIsBAiAc
8RzaV1GmjMEJxw9vM/tQwQg0kyAPlITMRXnwGA6E0A==
-----END RSA PRIVATE KEY-----
</textarea>

Sign up to request clarification or add additional context in comments.

3 Comments

That's a great solution, but I want to decrypt the data by using public key. I am generating key pair by RSA.generate and passing it on to PKCS1_v1_5 object. but JS fails to decrypt it using public key.
@AnumSheraz It's not possible to decrypt with a public key. You probably mean digital signatures.
But digital signature means that you need to have public key of the source from where data is coming from. Have a look at the picture below at this article, pubnub.com/blog/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.