Device fingerprinting is a complex topic if you want to cover the many edge cases (e.g. hardware upgrades, OS upgrades, etc.) and if you are worried about people expending energy to either forge a known-good device fingerprint (good meaning the fingerprint is known to you and receives some benefit such as software usage rights), or create a brand-new fingerprint you have no knowledge of (e.g. the fingerprint was on a blacklist of some sort, and the user wants to create a new Device ID for the device that is not on the blacklist).
Some things are fairly static and a reasonable basis for a simple fingerprint. MAC address best fits that category, but is also very easy to change. Also, some computers have several MAC addresses (including MAC's from virtual machines), so you need to deal with that complexity. Some network devices are ephemeral, e.g. tethered cell phone.
Other aspects of the computer are harder (but by no means impossible) to forge, such as CPU information, BIOS checksum, hard drive serial numbers, etc. Those things that tend to be harder to forge, however, are also more likely to change (BIOS upgrade, new hard drive, removable hard drives, etc).
You also need to consider carefully protecting the code that actually generates the fingerprint. If that code is easy (enough) to hack, someone attempting to defeat your solution will simply insert whatever fingerprint they want directly in the code that gathers and evaluates components.