It looks like you're trying to build a zero-knowledge end-to-end encryption system. I commend your consideration of security and privacy. However, be aware that these systems are hard to build right.
Fundamentally, the scheme you describe makes sense. Client-side key generation is of course essential, as is client-side encryption of that key. The catch comes from where the key-encrypting-key (KEK) which encrypts (or "wraps") the user's private key (PvK) comes from. Deriving it from the user's password is an obvious candidate - that's a secret that the user already has, and has already entered into the client - but it comes with a bunch of pitfalls.
- Typically, the password is sent to the server during login. Obviously, if you don't want the server to be able to re-derive the KEK (or otherwise access the PvK), then the server can never see the password. But that complicates user authentication. One option is to perform extra hashing of the user's password on the client. For example, use a secure key derivation / password hashing function with a known salt (e.g. the username) to derive the KEK, and then hash the KEK itself another time to derive the authentication secret (the value that is sent to the server in place of the password).
- The server should further hash this authentication secret (password hash) with a server-only random salt before storing into / verifying against the database, to mitigate the risk of database leaks.
- If you encrypt anything with a key derived from a password, then changing passwords can become expensive. A password-derived key should not be used to encrypt data directly, otherwise in order to change a password you have to re-encrypt everything. However, encrypting just your PvK is generally fine; on password change, you only need to decrypt the PvK with the old password-derived KEK and re-encrypt with the new one.
- Forgotten passwords mean complete loss of user data. There are various options to mitigate this, such as generating a recovery key that also wraps the PvK (or even just displaying the PvK in hex format or something) and telling the user they need to copy it down and store it somewhere safe, but fundamentally this is a hard step (and people will forget their passwords; you must plan for this. Though the plan can be just clearly advising "if you forget this you will lose all of your data in the account, forever" if you want).
An additional consideration for data sharing and asymmetric cryptography: Asymmetric keys shouldn't ever be used to encrypt data directly, at least not of any significant length. They're far too computationally expensive to use for that, and not designed for bulk data encryption operations anyhow. However, they can be used to secure symmetric cryptographic keys that are used for bulk data encryption; this is known as a hybrid cryptosystem and is how ~everything using asymmetric encryption works. Generate (on the client) a data encryption key (DEK) for each datum, encrypt each datum with its DEK and each DEK with each public key of the users who have access. Store the collection of encryptions of the DEK for each datum along with the encrypted datum itself. This lets you share specific data to other users, without giving them the keys to all of your data.
Finally, as with all cryptosystems, there's a few solid pieces of advice:
- Never roll your own. Definitely don't create your own primitives (ciphers, hash functions, modes of operation, etc.) unless you have serious backgrounds in both math and cryptography. If at all possible, don't create your own constructions (authenticated encryption schemes, key exchange protocols, etc.). If you can help it, don't even design novel cryptosystems (such as this one); if there isn't an off-the-shelf design you can use, at least look at existing ones that are similar (for example, this system is similar to what zero-knowledge password managers use), copy from them where possible, and where you can't copy, understand why they made the choices they made and take that into consideration.
- Relatedly, always use well-tested implementations where possible. Don't write a cryptographic primitive yourself, even a well-known one. Use a library for that. Ideally, the library should avoid you even touching the primitives at all, though; you want a library that takes as few inputs from you as possible, to avoid the risk that you generated or handled something insecurely.
- Don't forget that encryption does not inherently provide authentication (proof that the message was encrypted by who you expect) or even integrity (proof that the message wasn't tampered with, inherently comes with authentication), and indeed many encryption schemes are trivially vulnerable to bit-flipping attacks. Libraries you should use will only offer constructions that include protections against this; other libraries (looking at you, OpenSSL) will make you jump through a bunch of hoops even if you think you're using authenticated encryption, and should be avoided.
There are more considerations - this is a very complicated topic, with a bunch of problems such as "if you're doing this via a webapp, it is ~impossible to make it secure against a malicious/compromised web server" - but that should be enough to give you some ideas of the typical pitfalls here.