digitalbazaar / forge

A native implementation of TLS in Javascript and tools to write crypto-based and network-heavy webapps
https://digitalbazaar.com/
Other
5.07k stars 784 forks source link

Create Key from Modulus and PubExp help #598

Open Gamecock opened 6 years ago

Gamecock commented 6 years ago

I assume this is operator error. I create a RSA key in open ssl openssl genrsa -out mykey.pem 2048, and then test with this script.

openssl rsa -in mykey.pem -outform PEM -pubout -out mykey.pub
echo 'Encrypting "password" with public key'
echo 'password' | openssl rsautl -inkey mykey.pub -pubin -encrypt | openssl base64 > encryptedpass.txt
echo `openssl base64 -d -in encryptedpass.txt | wc -c`
echo "Now decrypt with public key"
echo `openssl base64 -d -in encryptedpass.txt | openssl rsautl -inkey mykey.pem  -decrypt`
openssl  rsa -pubin -inform PEM -text -noout < mykey.pub`

It works as expected and outputs the mantissa and exponent.

writing RSA key
Encrypting "password" with public key
256
Now decrypt with public key
password
Modulus (2048 bit):
    00:a7:50:72:dd:e1:3d:3a:35:6c:b1:8e:d9:8a:21:
    0a:bd:bc:c4:94:2a:b8:4c:ad:16:b2:c9:f1:d2:53:
    39:e5:48:ec:34:7d:b1:cb:af:82:48:cf:12:25:fd:
    5b:f0:5d:a4:9f:27:47:b4:bc:41:95:5e:e6:76:a6:
    c7:f0:bf:74:41:9e:fb:58:65:f5:28:eb:07:61:85:
    67:64:f8:42:4c:76:68:8f:ca:f6:4b:f3:66:43:f3:
    35:39:f3:f4:da:c0:ca:9c:e5:dd:ad:4b:af:ea:b7:
    57:c1:78:4b:14:73:d5:09:70:30:f5:e6:cd:95:bc:
    fb:72:2f:40:b9:1b:ac:ca:16:b2:1a:f4:a3:bc:e5:
    fc:9e:ff:10:af:47:8a:52:88:1a:32:db:9b:e0:15:
    e9:96:21:41:10:e5:c9:54:7f:41:71:63:9e:6d:18:
    39:fb:ac:2d:62:71:e3:19:5a:68:eb:cc:c1:88:c1:
    1d:f8:88:43:c6:11:50:27:4a:c8:54:4b:14:42:47:
    7a:b9:f1:c3:6d:d7:c4:e9:2c:82:05:4d:a1:3d:7b:
    7d:6e:d1:bb:30:61:0b:e0:86:b8:9f:19:d3:99:b8:
    a6:df:63:6e:19:31:ab:b1:48:45:1d:6a:5b:99:f8:
    db:a2:37:7f:14:4d:ae:fc:fe:8d:30:5c:a6:02:12:
    bf:37
Exponent: 65537 (0x10001)

Now I take that mantissa and exponent and put into a JSON object. In production I'll get them from the server in this format. Then I rebuild the key and encrypt:

    data = JSON.parse('{"cert":{"n":' +
            '"00:a7:50:72:dd:e1:3d:3a:35:6c:b1:8e:d9:8a:21:' +
            '0a:bd:bc:c4:94:2a:b8:4c:ad:16:b2:c9:f1:d2:53:' +
            '39:e5:48:ec:34:7d:b1:cb:af:82:48:cf:12:25:fd:' +
            '5b:f0:5d:a4:9f:27:47:b4:bc:41:95:5e:e6:76:a6:' +
            'c7:f0:bf:74:41:9e:fb:58:65:f5:28:eb:07:61:85:' +
            '67:64:f8:42:4c:76:68:8f:ca:f6:4b:f3:66:43:f3:' +
            '35:39:f3:f4:da:c0:ca:9c:e5:dd:ad:4b:af:ea:b7:' +
            '57:c1:78:4b:14:73:d5:09:70:30:f5:e6:cd:95:bc:' +
            'fb:72:2f:40:b9:1b:ac:ca:16:b2:1a:f4:a3:bc:e5:' +
            'fc:9e:ff:10:af:47:8a:52:88:1a:32:db:9b:e0:15:' +
            'e9:96:21:41:10:e5:c9:54:7f:41:71:63:9e:6d:18:' +
            '39:fb:ac:2d:62:71:e3:19:5a:68:eb:cc:c1:88:c1:' +
            '1d:f8:88:43:c6:11:50:27:4a:c8:54:4b:14:42:47:' +
            '7a:b9:f1:c3:6d:d7:c4:e9:2c:82:05:4d:a1:3d:7b:' +
            '7d:6e:d1:bb:30:61:0b:e0:86:b8:9f:19:d3:99:b8:' +
            'a6:df:63:6e:19:31:ab:b1:48:45:1d:6a:5b:99:f8:' +
            'db:a2:37:7f:14:4d:ae:fc:fe:8d:30:5c:a6:02:12:' +
            'bf:37", "e":"010001"}}')
        cert = data.cert;

build a key and encrypt "password", and print out the key in PEM format.

    console.log("Encrypting ["+ text + "] with cert:" ,cert);
        var mod = new forge.jsbn.BigInteger(cert.n);
        var exp = new forge.jsbn.BigInteger(cert.e);
        var publicKey = forge.pki.rsa.setPublicKey(mod, exp);
        var pemPublic = forge.pki.publicKeyToPem(publicKey);
        console.log("Pem formatted key:", pemPublic)
        var encrypted = publicKey.encrypt(text);
        var base64 = forge.util.encode64(encrypted);
        console.log("Encrypted password ["+ base64 +"]");

Output:



Pem formatted key: -----BEGIN PUBLIC KEY-----
MIHyMA0GCSqGSIb3DQEBAQUAA4HgADCB3AKB1R6ExwcHe25g8tViUwLewsAUGpOL
q9bXY0SpT1AgAKAai4ZdJUygwRP5Ussokj3O+lpSDGKiYRG8H9yXW7GkcxzQWwWT
zSp5pTBon6YQ0lzGqGWg2jGa3DX32x7kYQvb57WfeuAb5qoKoYFkkE0t1hYl67l0
/V9/0Pp0QrlrayGmeaYRmnNBDCqOhdGBgxZYh7Ugk1z5C8lQfGKHscT4cU/LxCF+
48e5uivzikZlmcjXcF415gOC7PqQ7B4+UyJ4OSMmm5lOJ/G6uoopI1iwyc+bld4M
OQICJxE=
-----END PUBLIC KEY-----

Encrypted password [FprkQH4TR8Nk+VuBrTcXqoT7CHJpWXasVkT8c9AnMORAV3UhWmkU2oTRPWRwZwX2dMO7o9QCD0JYq5d2FcMF2p8uFYHZqYJhRMq7eAPPcYlYfoEusFKRjBlJJ0ivU2y4vB6Le7QnCugWe/LkyQlW5P5Fowq/PT5GGZxONblAUA0YessBQR6bC77IvzWJ9/0DPu41esdVqclNKNJJA8CBZkkI26WTTfs8BCaqqsnx8KK16nBGFXKgqqobffJXo4ee4wHEl/lvnzmFoUxhEWSUK7I0hnfl]```

The encrypted password is only 213 bytes instead of 256.
Password from openssl:
```GKOOv5utrInEw1cIdhXE1RHdLkoXVGyTvZNqB6PsLvv0PP5CKwLF5E9pne0AIzqS
iZGBtfGTkUj0oU2gWT2l5di+OOtPNBk4mR3d7pOabw4a7xR4K6ad0deZAoAwHU6Y
Iz4ec2AJUM/X4YvbXFaDSnVQAQtNEYb5hzYy0VPe/timCUOMMLn75INz7mbxEqz0
UMIns13PKWjoT+Fq909fajeS0OVI5Vr5juC+7Xlemon3XMqbw9jqtDyqpE44o3te
V+KZJoDnCnFYjd/FEnlEnGvaB2Hez+RGahYotaSHbXI1yX8mXSnXjD9xAyjdyEAu
Cnvy9P5kVSWxr84zHlLpbA==```

Public Key from Open SSL that I started with:
```-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1By3eE9OjVssY7ZiiEK
vbzElCq4TK0Wssnx0lM55UjsNH2xy6+CSM8SJf1b8F2knydHtLxBlV7mdqbH8L90
QZ77WGX1KOsHYYVnZPhCTHZoj8r2S/NmQ/M1OfP02sDKnOXdrUuv6rdXwXhLFHPV
CXAw9ebNlbz7ci9AuRusyhayGvSjvOX8nv8Qr0eKUogaMtub4BXpliFBEOXJVH9B
cWOebRg5+6wtYnHjGVpo68zBiMEd+IhDxhFQJ0rIVEsUQkd6ufHDbdfE6SyCBU2h
PXt9btG7MGEL4Ia4nxnTmbim32NuGTGrsUhFHWpbmfjbojd/FE2u/P6NMFymAhK/
NwIDAQAB
-----END PUBLIC KEY-----```

I am assuming it's operator error, but after reviewing the documentation and other issues I would appreciate help finding it.
mattcollier commented 6 years ago

Good morning @Gamecock, before I give anymore thought to your exp/matissa question...

Why not just use the public key PEM? Why store the exp/mantissa manually? The PEM can be stored as a file on any file system and could go into any database as a string.

Second, the public key is intended to be public. It serves no purpose to encrypt and decrypt anything under that key. You want to encrypt with the public key and decrypt with the private key. Looks like maybe your script is doing this properly but the console logging "Now decrypt with public key" is incorrect?

Third, one way hashing algorithms are preferred for password storage: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet

Lastly, I suspect that your issues related to the length of the encrypted data have something to do with RSA encryption padding. There are some options in forge related to this. https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Padding_schemes

The documentation shows how to use various padding schemes: https://github.com/digitalbazaar/forge#rsa

Gamecock commented 6 years ago

@mattcollier Thanks for the quick reply. Let me answer your questions. 1) Why not PEM format: Because the server I am connecting to provides key in this format. 2) When you do not want to send a private key it's customary to encrypt with a public key and decrypt with a private key. 3) Why not one way hash. They I am sending to the server it is using. It needs to be able to decrypt. 4) Length of text. Maybe, I was hoping forge would do that for me. 5) Examples, I thought this one was the correct one to copy, but I tries OAEP and got a similar short result.

// encrypt data with a public key (defaults to RSAES PKCS#1 v1.5)
var encrypted = publicKey.encrypt(bytes);

I'd be happy if it was just a logging issue. Do you see something there I could fix?

mattcollier commented 6 years ago

One tip I picked up from this article is that you should use echo -n 'password' in your shell script, otherwise you're getting an extraneous newline: https://crypto.stackexchange.com/a/12623

I found this comment that indicate that openssl supports OAEP but defaults to PKCS#1 v1.5: (as of Feb 2016) https://crypto.stackexchange.com/questions/20279/openssl-rsa-same-plaintext-but-different-ciphertext#comment75886_20280

Gamecock commented 6 years ago

http://www-cs-students.stanford.edu/~tjw/jsbn/ This worked instead.