The simplest solution is probably to add a "password hash type" column to the database. Set it initially to "old"; when a user logs in, re-hash the password using the new algorithm and set the database type to "new".
A variant of this method is to store the hash type as part of the hash string. This works just as well, as long as you can unambiguously tell the different hash formats apart, and has the advantage that you can also include any other needed parameters (such as the salt and the work factor for key stretching) in the same string without having to add extra fields for each to your database.
For example, this is the approach typically used by modern Unix crypt(3) implementations (and the corresponding functions in various high-level languages like PHP): a classic DES-based (and horribly weak) password hash would look something like abJnggxhB/yWI
, while a (slightly) more modern hash might look like $1$z75qouSC$nNVPAk1FTd0yVd62S3sjR1
, where 1
specified the hashing method, z75qouSC
is the salt and nNVPAk1FTd0yVd62S3sjR1
the actual hash, and the delimiter $
is chosen because it cannot appear in an old-style DES hash.
The method you suggest, where the new hashes are calculated as:
hash = new_hash( old_hash( password ) )
can be useful in some cases, since it allows all existing records to be updated without having to wait for users to log in. However, it's only safe if the old hash function preserves enough of the entropy in the passwords.
For example, even a fairly old and weak cryptographic hash function, like unsalted MD5, would be good enough, since its output depends on the entire input and has up to 128 bits of entropy, which is more than almost any password will have (and more than enough to withstand a brute force attack, anyway). On the other hand, trying to apply this construction using the old DES-based crypt(3) function as the old hash would be disastrous, since old crypt(3) would ignore all but the first 8 characters of each password (as well as the most significant bits of even those characters).