Legrandin / pycryptodome

A self-contained cryptographic library for Python
https://www.pycryptodome.org
Other
2.74k stars 492 forks source link

"Incorrect decryption." using PyCryptodome's RSA functionality. #775

Closed github-user-en closed 6 months ago

github-user-en commented 8 months ago

Hello, I am trying to encrypt-decrypt user credentials on a website using RSA algorithm. The workflow is as follows:

  1. On the server, use PyCyptodome to generate key-pair and store them in the database as strings.
  2. Transmit the public key (as string) to the client browser as part of a javascript on login page.
  3. On the client browser, when user clicks 'Submit' button on the login page, change the user password to its encrypted form before transmitting it to the server.
  4. On the server, decrypt this encrypted password.

The libraries/frameworks involved in this process are:

  1. Django (web-framework to handle request-response cycles)
  2. PyCryptodome (to generate the key-pairs, and to decrypt user password)
  3. JSEncrypt (for client-side encryption)

The python-django-pycryptodome code I am using to generate the key-pair is as follows:

key = RSA.generate(1024)    
self.request.session["loginPrivateKey"] = key.export_key().decode()
self.request.session["loginPublicKey"] = key.publickey().export_key().decode()

At the client end, the javascript involving JSEncrypt is as follows:

<script type="text/javascript">
    // Call this code when the page is done loading.
    $(function() {

        // Run a quick encryption/decryption when they click.
        $('#submitbutton').click(function() {

            const publicKey = `{{request.session.loginPublicKey|safe}}`  // backticks indicate this shall be a multi-line string.

            // Encrypt with the public key...
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey(publicKey);

            let password = document.getElementById('id_password').value;
            document.getElementById('id_password').value = encrypt.encrypt(password);

        });
    });
</script>

Then, at the server-side, I am using the following code to retrieve my private key and decrypt the password:

  password = self.cleaned_data.get("password")    # retrieves password (in string format)

  loginPrivateKey = RSA.import_key(self.request.session["loginPrivateKey"].encode())    # encode() converts string to binary, which is used to create private key object.

  encryptedText = password[:loginPrivateKey.size_in_bytes()]
  cipher_rsa = PKCS1_OAEP.new(loginPrivateKey)
  try:
      decryptedText = cipher_rsa.decrypt(encryptedText.decode())
  except Exception as e:
      pass

The problem is that I am getting an exception saying "Incorrect decryption." Please point out what I'm doing incorrectly.

Varbin commented 8 months ago

JSEncrypt uses the old and less secure PCKS#1.5 padding (see https://github.com/travist/jsencrypt/blob/master/src/lib/jsbn/rsa.ts#L136-L157) instead of OAEP.