I'm working on a project where each each user will have a public/private RSA key-pair. Users will be able to send encrypted messages to each other using the each target user's public key so that only target users can read the message (similar to PHP's openssl_seal). Since we want the user to be able to use any device to read or send a message, the key-pair will be stored on an API server, and the private key will be encrypted with the user's password.
When a user logs in from a new device, they will use their password to decrypt the private key. The catch is that ideally, the API server should never be able to decrypt the user's private key, even to authenticate the user. Originally, I was just going to send the client the encrypted private key and let the client decrypt it. If the client can decrypt it, they must have the correct password, but this would be vulnerable to offline dictionary attacks. To prevent that, I need to authenticate the user before I give them the encrypted private key.
I could have the client send the password to the server and let the server attempt to decrypt the key using the password or even just validate the password against a hash, but then the server would temporarily have the password and be able to decrypt the user's private key. That's OK, but I'd rather figure out a way to authenticate the user without the server knowing their password.
In my case, the client will be a PHP web server or a native mobile or desktop app, not a browser, so I have access to a wide range of cryptographic methods. The only client authorized to make the call to the API server to create an account will be the PHP web server, and I'm currently planning on having it create the public/private key-pair and encrypt the private key with the user's password. It will then send that information to the API server (also running PHP) to create the account. The API server will store the encrypted private key, the public key, information about the user, and any encrypted messages, but won't be able to decrypt any of it.
I'm not trying to protect against a MITM attack here. All API calls will be over HTTPS. My goal is to build the API in such a way that even if the Chinese government could gain access to the API server, they still couldn't read any of the messages. The web server can have the private key temporarily, just not the API server.
How can I let the API server authenticate the user without being vulnerable to an offline brute-force attack and without the API server needing the user's password?
Feel free to tell me I'm doing something totally wrong and suggest something completely different.