One major worry with the "check word" idea is that it's vulnerable to shoulder-surfing. That shouldn't be a problem since the check word isn't inherently valuable, except unfortunately it's vastly easier to recover the actual password - if you have something (like the check word) to check against - than it should be.
The main problem in your password-handling code is that you're using fast hashes. MD5 is deprecated and shouldn't be used for literally anything anymore (its specific weaknesses are probably not relevant to how you're using it but... why risk it?) but even a very modern secure hash like SHA3-512 would be inappropriate by itself in either place you use hashes (generating the cipher key and generating the check word index). The problem is that these hash functions are designed to be extremely fast and use as little RAM as possible, which is important when you're using them in things like embedded systems or for creating and checking MACs on every TLS frame, but mean they're extremely vulnerable to parallelization. A modern high-end GPU can compute over ten billion of such hashes, each on a different candidate password, per second (the practical limit is lower because the limitation is getting the candidates in and then the digests back and checking if one of them produced the desired result, but it's still fast enough to make brute-forcing the password far easier than you probably expect).
The check word, in effect, saves the attacker a lot of time (if they know what its expected value is). They can simply run candidate passwords through MD5, see which ones produce a hash that leads to the correct index, and only then perform the more expensive check of trying to generate the decryption key, use it, and seeing if the file decrypts cleanly (it'll be pretty obvious if it does; plain text has way fewer bits of entropy per byte than the pseudo-random result of a cipher with the wrong key, and it's thus pretty trivial to programmatically check the output for "does this seem like a plausible password file?"... and that's without even getting into checking the padding, which is usually effectively a very quick checksum). Skipping this expensive check for the vast majority of candidates means a GPU can go on cranking through candidates at close to full speed.
The solution is to use a password hashing / key derivation function, which is a special class of hash functions designed to be slow, computationally expensive, and (in newer cases) memory intensive and resistant to side channel attacks. The current leading one is Argon2id, a derivative of the winner of a multi-year competition to advance the state of the art, but even the venerable (and memory-easy thus parallelizable) PBKDF2 (Password-Based Key Derivation Function 2) would be a vast improvement from your current approach. All password hashing functions include at least one tunable cost parameter (sometimes called the "work factor") which determines how computationally expensive hashing a single password is. For a system like yours, there's no reason to not use the most expensive cost you can personally stand to wait for, whether that's a quarter second or 15 seconds (the same work factor will of course take less time on faster hardware or in faster languages/implementations, though). Modern KDFs also include a tunable memory cost, which should again be as large as you can afford to make it; at the very least it ought to be large enough to force any CPU or GPU to use something beyond L1 or even L2 cache (this will drastically impair parallelization) so go for at least a few megabytes, but for your case in particular it's probably fine to make it take a gigabyte if you want (unless you're running this on very low-end hardware that won't have a gig of spare RAM). With high enough compute and memory costs, even a mediocre password can resist brute-force attacks for quite a long time.
Obviously, you need to use the KDF for both the check word index and the key itself (using a fast hash for the key isn't safe, no matter how slow the check word generation is, because the attacker will just switch to trying decryption directly and checking padding/text-ishness of the file). Assuming you want to keep the check word, since you want the check word digest and the key to be different, you should use a different salt for each one. The salts - like the IV - are not secret (you can and should include them, in plain text, in the encrypted file) but should be random.
Speaking of things that should be in the file in plain text, you should have some form of actual data integrity check, rather than just password correctness check, on your encrypted files. There are a number of ways to do this, but the usual ones are computing and including a Message Authentication Code (typically computed using a hash function, that is, an HMAC), or Authenticated Encryption (often abbreviated AEAD, Authenticated Encryption with Associated Data) cipher mode. HMAC-SHA2-256 or similar are fine, and there's some slight discussions to be had about whether you should use the same key for the HMAC as for the cipher and what exactly should be HMACed (the plaintext or the ciphertext or something else) but having one at all will be a big improvement. For AEAD, the most common modes are GCM (Galois/Counter Mode) for block ciphers (like AES), or Poly1305 (usable with any cipher but most common with the X20 "Latin Dances" stream cipher family, as in XChaCha20-Poly1305). In either case, you'll get an additional bit of data - the authentication code - which should be stored in plain text along with the ciphertext (typically appended or prepended to the ciphertext in the same file). Such authentication codes allow you to not only detect an incorrect key, they can also detect if the ciphertext was modified, even by a careful attacker (and with much higher confidence than just checking padding).
So... with all that said, is there some reason you don't want to just use a dedicated password manager app, even a purely local one like KeePass? Cryptography is really hard to get right, I'm sure there's stuff I missed even in this long response. Leave it to people who have done this a lot, and whose work has been extensively reviewed by other experienced people.