Search code examples
javascriptencryptionpublic-key-encryptionpublic-key

JSEncrypt producing different output for identical message and public key each time


I've got this simple bit of JavaScript:

<html>
  <script type="text/javascript" src="http://cdn.rawgit.com/travist/jsencrypt/v2.1.0/bin/jsencrypt.js"></script>
  <script type="text/javascript">

    var message = "This is my message"

    // we create a new JSEncrypt object for rsa encryption
    var rsaEncrypt = new JSEncrypt();

    var publicKey = "-----BEGIN PUBLIC KEY-----" +
      "\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQK" +
      "BgQCQDxDFOYJpkCaqeX4CBVNCtBjX\nMZgGMo" +
      "lSs2wYrVu1ixr36KpeJcRcYhz+AnyhnUpYkgk" +
      "+hEqJtDSNirU5Dk5oVYYi\ngf2uLogV5Tp/ka" +
      "K49r9vtxldcHraZIgNQjcdeQUd/viKd/3DvM8" +
      "naWR/mTG0nCBE\nrEQkATW/xXynJh/COQIDAQ" +
      "AB\n-----END PUBLIC KEY-----"

    rsaEncrypt.setPublicKey(publicKey);

    var encryptedMessage = rsaEncrypt.encrypt(message);

    console.log(encryptedMessage)
  </script>
</html>

That I feel should be producing the same output in the console each time it runs, but it doesn't.

Example outputs:

abqE+YkCMKFWgsazbZpfGvoXLci9FL/wZLYUMR6ZFkolsvJC5MdJgq5yn+AXXy8xlKHDOry6czAaOQOTl2HXdKSfsypc8nqDU8Sx5PuEgMYjvJ/dEyfU6jVuxfH1Qmuk6aOGVHePNfDlC4kSjgp1RXToSP5NqAEi24EuMx3uulI=

OzZM03Pki3o631KOuZ5nyQKu1xXRbLHhrR0WnjE5Ns5SssoiCEwlrS+svtP0cbZaYWZJc+FlZQNFUam4iC233BKnY5Nrr5Ppj14eaBvJ4x3FR8FiLwtyEW7nTzisAS7Ys2RKPjUzmkiOCZHwIpXnUO10KVo8763+JIuDB0cDPS4=

Can anyone explain this behaviour?


Solution

  • That is expected.

    The RSA cryptosystem works in group modulo some product of two prime numbers (modulus). To ensure that all possible plaintexts are encrypted with the same security, the plaintexts are padded to produce a padded plaintext that is slightly smaller than the modulus. Since the padding is applied before encryption, the ciphertext looks completely different.

    JSEncrypt is based on JSBN which in turn implements only PKCS#1 v1.5 padding type 2 (RFC 2313). The second type of this padding introduces random bytes that are removed after decryption because of marker bytes. At least 11 bytes are needed for the padding.

    If you want to check the interoperability with other implementations, you need to do a full encryption-decryption cycle and make sure that you get the same plaintext back. This should be done in both directions.

    Note that nowadays, PKCS#1 v1.5 padding shouldn't be used anymore and PKCS#1 v.2 OAEP is preferred which is also randomized.