1

I would like to encrypt my password using Public Key in RSA format with PKCS1-1.5 padding. Currently I try to use forge library from https://github.com/digitalbazaar/forge. Part of the solution uses Tom Wu's BigInteger().

modulus = new forge.jsbn.BigInteger(modulus,16);
exponent =  new forge.jsbn.BigInteger(exponent, 16);
var text = "Password";
var rsa = forge.pki.rsa;
var publicKey = rsa.setPublicKey(modulus, exponent);
var encryptedData = publicKey.encrypt(text, 'RSAES-PKCS1-V1_5');

provided exponent - 10001 seems to be parsed properly in BigInteger() as it returns single item array - 65537.

However when I push modulus 9bedc7ad20bdacf930f1471d0c2a9f7f1895e24d73957b145b621e7800589ec14a3122df556fae94cc45df7b1f5003062df5681a18d8165377a6dece1a8c36e0af6ce13e89890b6813eb94135bd8c4b2b743ef6d24cfc09cbd59a8105c3f31d56a0224b1db14c2e6396493571ef83d664c5b6169a1b42f988cfc3f7d39d50aa9

I get something strange as BigInteger() results: modulus screenshot

Therefore RSA keypair created later in the code is wrong. Could anyone point me to the right direction? What is even more frustrating is that I have working .py script and cannot convert it to JS.. I cannot use any server side programming here. (I am aware of the issues / risks with JS password encryption btw).

Update

Here's the working python Script:

import binascii
import Crypto
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import b64decode
def assymmetric_encrypt(val, public_key):

     modulusDecoded = long(public_key["n"], 16)
     exponentDecoded = long(public_key["e"], 16)
     keyPub = RSA.construct((modulusDecoded, exponentDecoded))
     print(keyPub)
     # Generate a cypher using the PKCS1.5 standard
     cipher = PKCS1_v1_5.new(keyPub)
     return cipher.encrypt(val)

# Encrypt the password used to login
encryptedPassword = assymmetric_encrypt(password_input,public_key)

It looks that JS should be doing the same but when providing the same modulus exponent in both scripts, the encrypted password is different (looks similar but is not equal)

Update: Funny thing is that if I run JS script with hardcoded n, e and password I get the same encrypted data every time. When I do the same with Python script I alway get different result.. So there might be something more in the Crypto library..

Update 2: problem was located in completely different place. Thanks to Maarten Bodewes comment, it turned out that padding library was broken (didn't generate new string each time). I changed forger lib to JSencrypt for this part and it works great. Going through pem file but this will be probably altered in the future for better performance:

var encrypt = new JSEncrypt();
encrypt.setPublicKey(pem);
var encrypted_jeencrypt= encrypt.encrypt(password);
var encrypted_jeencrypt_hex = base64toHEX(encrypted_jeencrypt);
darth0s
  • 165
  • 2
  • 13
  • could you try with `forge.pki.setRsaPublicKey(modulus, exponent);` ? – pedrofb Sep 28 '17 at 11:30
  • I think it didn't help.. should BigInteger() array be a decimal representation of hex modulus, just split? In other words, is there any way to check if bigInteger() for modulus is correct? – darth0s Sep 28 '17 at 11:38
  • Internally the BigInteger will of course be stored in smaller integers; your CPU doesn't work with large bigintegers internally. That it represents these smaller bigintegers as decimal is of course just a debugger setting. – Maarten Bodewes Sep 29 '17 at 08:41
  • 1
    You can check if the modulus is correct by encrypting in one runtime and decrypting in the other. If the encryption results in the same value then either you are using "raw/textbook" RSA (doesn't seem that way), the PKCS#1 padding is broken or there is something *seriously* wrong with the random number generator in JS. This could happen if you use a VM that isn't connected to the systems RNG, for instance. – Maarten Bodewes Sep 29 '17 at 09:34
  • I've checked RNG issue and _fortunately_ it seems RNG is working correctly. I've changed padding to RSA-OAEP and I am getting different encrypted hex every time both locally and on server – darth0s Sep 29 '17 at 13:56
  • @pedrofb I don't think this is correct. As Maarten Bodewes said, pkcs results should be different everytime as it's padded with random number. It turned out to be corrent - I changed library and it works ok. I will update answer when I have a second. – darth0s Oct 04 '17 at 20:59
  • @darth0s you are right. I reviewed it – pedrofb Oct 05 '17 at 05:23

1 Answers1

1

I believe the way you are building the modulus is correct. The problem is probably due you are using a string to encrypt and forge needs a buffer. Try this:

var buf = forge.util.createBuffer(text, 'utf8');
var encryptedData = publicKey.encrypt(buf, 'RSAES-PKCS1-V1_5');

If you are going to use RSA encryption to send passwords, I suggest you to use the more secure RSA-OAEP via a SSL/TLS channel. RSA_PKCS1-V1_5 and RSA-OAEP are non-deterministic and generate a different ciphertext each time. The correct way to compare if a message is correct is decrypting it

pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • changed to bytes (good point) but still didn't help. added working python script snippet. – darth0s Sep 28 '17 at 15:02
  • Please, try the python encryption/decryption code shown here: https://stackoverflow.com/questions/16823558/rsa-communication-between-javascript-and-python It is similar to documentation https://www.dlitz.net/software/pycrypto/api/2.6/ – pedrofb Sep 28 '17 at 15:18