Now my main question is, HMAC seems redundant, but seems like the only good way to check that the key is valid, otherwise a hash of the plain text data would need to be compared which would be very easy to reverse engineer / brute force.
HMAC is important. AES CTR encryption gives you confidentiality. But that is not enough. You also need an integrity check, which HMAC can provide. Search forLook up Authenticated Encryption for more details.
Getting the implementation right can be tricky so I recommend using AES GCM, which has the integrity check built into the algorithm-in.
But since you're rolling your own integrity check...
Also, do I really need two separate keys for AES and HMAC?
Yes, but easy to do. Churn the same password through PBKDF2 but with different a different iteration count (say 20000). What's interesting about this approach is you could use the key derived for HMAC to authenticate the user, i.e. if the HMAC check fails, don't try decrypting the ciphertext - just return an error stating password was wrong. Although a simplification, this is how password managers work in a nutshell - same password is used for authentication as well as encryption.
Lastly, does PBKDF2 really help?
Yes, to mitigate against brute force attacks by:
- Intentionally making PBKDF2 an expensive operation, especially with higher iterations.
- Expanding the password to take up the entire 128 or 256 bit space of AES. Otherwise you'd need ridiculously long passwords.