Short answer: yes you are right and you can do this.
To be on the safe side however, you must ensure not to lower the HMAC function security by using it properly and not putting wrong expectation on the function used.
By design HMAC functions expects one parameter to be a secret key, and the other being a message which could be public (you can find more information here and there). This means that the arguments are not be freely interchangeable, the exact order depending on the language used.
A secret key is a key:
- Which is composed of computer chosen random characters,
- Whose minimum length is the length of the resulting hash,
- Which is never known by the remote user, even a part of it,
- Which cannot be controlled or influenced in any way by the remote user.
Depending on your actual need and implementation, all parameters you will provide to the HMAC function may not be usable as a secret key:
primary_key might be a service or domain name for which you will need to derive several keys, in which case it might be a public string accessible to the end-user,
keyn (key1, key2, etc.) may also be a predictable string corresponding for instance to the internal service for which the key need to be generated.
Applied to your question, we have then the following possibilities:
- None of the parameters can be considered as a truly secure key but one of them is a password or equivalent: if your
primary_key is a user provided password (ie. a potentially human generated random string), you can remain safe by using a password dedicated hash function. Such
primary_key may be vulnerable to dictionary attacks & co., but this threat will be mitigated by the use of a proper hash function at the expense of the application performance (such hash functions are designed to be slow).
- None of the parameters can be considered as a truly secure key and they do not even include a password: an example of such situation would be if
primary_key reflects or is deduced from user's provided domain name and
keyn contains the name of the service or server (or is deduced from such info) for which the final key will be generated. Then consider your system as flawed, end of story. Your system is wrong and not secure.
- One of these parameters can be considered as a secure key: pay attention to pass this parameter through the proper argument where the secret key is expected by the HMAC function. Not doing so may impair the cryptographic strongness of the HMAC function. For instance, let's say that
primary_key is a service or domain name, while
keyn is a secure key, compared with your question you may need to revert your arguments:
key1 = HMAC("secret_key_1", primary_key);, refer to your language documentation in case of doubt,
- Both of these parameters can be considered as secure keys: Then you have the highest security you could expect from this scheme, the arguments can be passed in any order without increasing or decreasing the security (the resulting keys will be obviously different but with the same security properties).
As a side note, HMAC RFC also mentions the possibility to truncate the resulting hash to reduce the information available to an attacker who would try to deduce
primary_key starting from a hash. The same RFC admits however that while some research "show some analytical advantages of truncating the output of hashed-based MAC functions", "the results in this area are not absolute". As far as your initial keys are secure, I have the personal feeling that you may have more to loose in such truncation due to shorter resulting keys. In all case, this RFC recommends to keep the leftmost part of the resulting hash and to keep at least half of it.