Normally, private-key authentication doesn't work for most of these things because users expect to be able to log in with something they can remember, and any private key of decent strength is too long.
How about this for a login solution? For each user, you generate a private key and keep it on the server. But, you encrypt that private key with their password, and you don't keep that password or anything derived from it anywhere.
To log in, the server sends the encrypted private key and an authentication challenge to the client. The client then uses the password to decrypt the private key and respond to the challenge. Replay doesn't work, since the response is only good for that challenge. Watching the traffic on the server doesn't work, since you can only get the encrypted private key and the response. Snarfing the contents of the database doesn't even help, since you only get the encrypted private key.
For bonus points, I don't know if this is actually possible, make it so that it's impossible to tell whether a particular decryption of the private key is valid without using it to respond to an authentication challenge and sending that response to the server. This way it's impossible to brute-force the encrypted key, substantially mitigating problems with weak passwords.
Of course, I may have missed something obvious here....