There are two solutions which vary in their integration complexity but also in their feature-set.
Solution 1: The Signal Protocol
The Signal Protocol is, well, the protocol used by the famous Signal-Messenger and which was also adapted in quite a few other popular messengers such as WhatsApp. It is designed to perform best in the situation you're in: asynchronous chat applications. As such it ensures forward secrecy and allows to confirm other parties at any point. As a bonus there's the famous libsignal which should greatly ease implementation.
If you can this is absolutely the solution you should take.
Solution 2: E-Mail Encryption methods
If however integration of the Signal Protocol proves impossible for your deployment, the usage of standard E-Mail encryption methods should still work. Basically you let each party generate public-private keypair(s) and then use your favourite PGP encryption and signing library to sign and encrypt the message as if they were emails or you could use your favourite S/MIME library to do the same. However this doesn't provide forward secrecy and gives non-repudiation to the messages (ie "everyone" can verify that X send the message Y). Key confirmation can be solved by your provider (for when the users trust you) and by comparing key hashes by the users if they meet in person.
This is the second best solution which you should implement if the Signal Protocol really isn't an option.