Server-side public key encryption with browser-side decryption - encryption

My application requires certain fields to be encrypted when stored at rest, and allow the user to decrypt them locally in the browser by entering a passphrase. However, these encrypted fields are not entered into the database by the authenticated user himself, but submissions from other people who are supplying the user with private information.
If I stored the passphrase in the database to encrypt new submissions, then anyone who got access to the database would be able to easily decrypt the encrypted information. So, I believe I need to store a public key in the database, encrypt incoming information with the public key, and then serve that encrypted info to the user and let him decrypt it with his passphrase in his browser. (Thus, serverside, we can never decrypt anything.)
I've looked into libraries like ursa, but the problem is that the user would have to save his private key. (Correct?) Is there any way that I can do this with a relatively short passphrase instead of a long private key?

You can use something like JSBN to generate a public-private key pair on the client and use CryptoJS in password encryption mode to encrypt the private key with AES. The encrypted private key and the plaintext public key are then posted to the server and added into the database.
The next time the user gets information encrypted with his/her public key, you send the encrypted information and the encrypted private key to the client/browser. The user can enter the passphrase again to decrypt the private key and then in a second phase decrypt the payload.
Note that this is not absolutely secure because unencrypted traffic (e.g. no TLS) will enable an attacker to extend the html/js to include a data stealing script. Furthermore, the developers of your system can also add such a script whenever they want regardless of TLS or not.

Related

Private key storage issue in OpenPGP encryption

I'm trying to implement OpenPGP end to end encryption on a simple messaging app which will be accessed on the web as well as mobile. I'm stuck at deciding where my private key should be generated and stored. The following two approaches are on the table(i don't want to go with approach 2 but that looks like the only feasible option) :-
Key storage approach 1:-
When the user logs into the Chat client the first time, the client (web/app) will generate a set of public and private keys.
The user's public key will be sent to the backend server. The user's private key will be encrypted and stored on the local storage of the client.
Problems with this approach:-
If the user has not logged into the client even once, how will the sender encrypt the message? (since the public key of the recipient is not available till now)
If the user logs into another client (another browser or different app), how will he get access to the private key? (since that is needed to decrypt the messages in the user's inbox)
Key storage approach 2:-
When the user is created on the backend server, create both public and private keys for the user using PGP on the backend itself. Public key can be stored in plain-text but the user's private key should be encrypted and stored. We can use symmetric encryption with a client-specific passphrase here.
When the user logs into the Chat client (web/app), the encrypted private key will be received from the backend upon login. In order to decrypt any message, then can decrypt their private key using their passphrase, then use the private key to decrypt the message received.
Problems with this approach:-
This is not truly end to end encryption, since who-ever knows the passphrase of the user, can have access to the private key.
Reg. approach 1: If the client hasn't had the opportunity to share their public key, then indeed, you cannot send encrypted messages. It's unclear how you would solve that problem. The private key you can encrypt with a passphrase and upload the encrypted version to your backend so that the client can then download it and have it available on other devices, as you write in your second approach. Or you can go a step further and generate the private from the passphrase.
Your approach 2 may actually not have much worse security properties, depending on how you look at it. You could argue that you have no infrastructure anyway to let clients make sure they have obtained the correct key material. So they cannot distinguish the actual public key of the user from a malicious one provided by the server.
How you design your system is up to you, of course. I suggest you start laying out your requirements. Your problems seems to become smaller if not-yet-known users do not need to receive encrypted messages.

En/decrypt data using password without database admin being able to decrypt it

This is a more conceptual question, but I'm trying to have some content be made available only to a specific user at a time. To do so, I thought about using a users password as an encryption key. However, the problem with that is that if I encrypt it using the plaintext password, I won't be able to encrypt anything as I'm obviously storing a hashed version in my database. If I encrypt it using the hashed password, then any database admin will be able to read the content of every user.
So basically, I need some kind of public/private key concept where I can encrypt it with a users public key but only they have access to their private key to decrypt it. Using actual RSA keys will be annoying in terms of usability though, as a user would have to write down their private key somewhere.
Is there a clever way for me to store data in a way that only a specific user can see it, somehow accessible through a password they set without being able to see their data as a server admin?
Example, assuming a website:
Random person chooses a receiver person, writes a message. That message should be stored in encrypted form in the database, using some form of public key.
Receiver person enters their password (Not a huge RSA key, optimally, but a standard passphrase), on the server side this password will be treated as some form of private key in order to deencrypt the data and send it back to the client.
So basically, I want to treat a simple passphrase as a private key, and generate a corresponding public key for it.
It looks like you want to use the same password for both authentication and for encryption.
Lets assume that only hashes of passwords are stored in a database (as it should be).
Issues:
"Remember me" function of web/mobile/desktop app will render encryption function impossible because user can log-in without password via token.
Admin can intercept login request to know user's password while it is transmitted in plain-text over https (simple infrastructure reconfiguration allows admin to sniff on traffic).
There is a way to secure transfers by using the same password in case:
you change your authentication procedure in a way that user sends to the server hashes only instead of plain-text password
and will save plain-text password at client side (for "remember me" scenario)
Then you could generate key pair during registration and save encrypted private key at server.
This way you will have access to your private key even after client side reinstallation (web/mobile/desktop).
So upon login you request your private key from server and use password which was used for authentication to decrypt your private key.
If you trust that admin(or whoever else) will not be able to meddle with software (especially in key exchange phase) then you have a way to implement the feature you need.
This will be hacker-proof solution until someone patches your code and every user in system gets wrong public keys of other users.

Is encrypting with private key instead of signing a bad idea?

I am using a local URL scheme to submit a payload to the client when a user clicks a link and have to make sure that this is only used in my specific web application.
So I am using a key pair, encrypting the payload on the server using the private key, generating a link that the protocol handler on the client can decrypt using the public key, verifying that the payload should be processed.
So is it less secure to send a private-key-encrypted payload instead of a cleartext payload plus signature (and if yes, why)?
Found out that there are other Stack Exchange sites that answer this question very well:
https://crypto.stackexchange.com/questions/2123/rsa-encryption-with-private-key-and-decryption-with-a-public-key
https://security.stackexchange.com/questions/11879/is-encrypting-data-with-a-private-key-dangerous
I should have searched a little bit longer before asking.
What you are doing is signing!
Encrypting is when a payload is being concealed with one’s public key and later decrypted with a private key. When I want to send you a secret message, I will take your public key (because I know it) and encrypt my message. This will make sure that only you can decrypt it with your private key.
Signing works vice versa. If during encryption I want to make sure that only you can decrypt me message, with signing I want to make sure that all recipients of the message can be sure in its authenticity. If you encrypt something with your private (signing), anyone with your public key can decrypt it and verify its sender.
In your case, if you just want to prove your identity (like certificates, jwt tokens, etc.), you would need to use signing methods. If you want to transfer payload securely, use encryption.
Hope this helps!

rsa keys verification

So I'm developing my messanging app with encryption evolved using RSA. Currently I came up with this algorithm:
= Update for auth =
Screenshot:
In this case private keys are generated only from password, and server knows only password hash just to authorize users (unhashed password is never transmitted to server), therefore is unable to generate private keys or decrypt any old message. If server will fake a public key recipent will be unable to verify signature encrypted with sender's private key.
The problem is that server can fake a sender's private, public key and password when he signs up on a service or requests a password change and recipent will be unable to detect it. So, how can I verify that keys were not faked by the server?
So, how can I verify that keys were not faked by the server?
You cannot. So long as the clients only communicate with the server, there's no way for them to distinguish between a "real" remote user and one that's being MITMed or otherwise faked by the server.
I see a couple other serious potential issues here:
Sending an unsalted hash of a password over the wire (during the login process) is hardly better than sending the password in the clear. An attacker can sniff the hash off the wire to log in -- they don't need the actual password! -- and a non-iterated SHA256 is highly vulnerable to brute force attacks.
"private key from pass" (also in the login process) implies that you're using some sort of KDF to generate a private key from the user's password. This has multiple negative implications:
Users are generally pretty bad at choosing passwords. This implies that the private keys will also be weak.
If two users happen to use the same password, they will end up with the same private key.
Any user can attack the password (and hence the private key) of any other user that they've communicated with by running the KDF on candidate passwords.
There is no process specified for negotiating a symmetric encryption key. Using RSA to encrypt messages directly limits each message to the size of the key, and makes it vulnerable to numerous attacks if the data being encrypted is not both random and unique.
In the message exchange, the user sends the server two copies of every message -- one encrypted to the target user's private key, one encrypted to their own private key. In some situations, this may make it possible to recover the message.

How to encrypt user data in Firebase

I am using the email/password sign in method for Firebase. I would like to encrypt the data users save into the realtime database before sending it to the database. Firebase already handle the user password, but can I somehow use it to encrypt data which can't be decrypted by me only the client? It would be nice if I could achieve it with the client sdk.
So my flow would be something like this:
User sign in with it's credentials (which is handled by firebase itself)
User encrypt some data with some unique key, which can be generated only from the credentials or from some data available only for the user, but not me. (this key needs to be persistent between sessions, or after the user changed his password.)
Data is saved into the database (I cant read it since its encrypted with the user credentials)
User log in on a different device (the decryption key can be generated right away and data can be decrypted.)
You can easily do that the following way:
After user A logs in a random public private key pair is generated on his phone. eg.: use Ecc Curve25519
The private key from A is stored securely on his phone
The public key from A is stored in firebase and is accessible to anybody that chats with A.
If X sends a message to A he fetches the public key from A from firebase encrypts the message for A locally and stores the encrypted message on firebase in the inbox from A
A downloads the encrypted message from firebase and decrypts it with his private key stored on his phone
(vice versa for A to X)
If A want's to move to another phone or wants to use multiple phones you can do this that way:
Ask A to define a strong password to encrypt his locally stored private key. (or create a random passphrase and use QR codes for key exchange)
Encrypt the private key locally (eg.: use AES256) on his phone with the password from step 1 and upload it to firebase. (optional sign it with his private key)
Download the encrypted private key from the second device from A
Ask for the passphrase on the second device from A and store the private key securely (optional check the signature with the public key from A)
Delete the encrypted private key from firebase if no backup is wanted

Resources