If you are looking to use some unique hardware identifier to validate a license then I recommend using OSHI (Operating System and Hardware Information) to get the hardware information you need.
The general principal would be encode the hardware identifier into the client license string then when the client runs it would decode that id from the license string and make sure that hardware is present to validate the license. Don't depend on indices because there's often no guarantee of hardware order, just iterate through the list of hardware in question and see if the one the key is bound to is present.
Note: When license validation is done locally it can always be cracked; The point is just to make it annoying to crack. If your license were just the plain MAC address by itself then it would be very easy for people to just look up their MAC and give themselves a valid license. Even if you encode/encrypt the MAC into the license, they could look at the decompiled code and find the how the decoding (and hence encoding) works so that they could encode their own valid license. You need to encrypt and obfuscate as much as possible to make it hard for people to generate their own licenses but in the end it only makes it more annoying to crack but not impossible.
Another option, aside from MAC address, is binding to a disk. Disk serial number is not as easy to change as MAC address. With this you could bind the license to a USB key which is kinda fun. The only thing to worry about is people changing out disks but if the software license is bound to the disk it's stored on then that's hardly a problem.
pom.xml
...
<dependencies>
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>3.13.2</version>
</dependency>
</dependencies>
...
ClientLicense.java
import oshi.SystemInfo;
import oshi.hardware.HWDiskStore;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
/**
* This class would be loaded with the client license and validate that the license is for the machine running this code.
*/
public class ClientLicense {
final String clientLicenseString;
public ClientLicense(String clientLicenseString) {
this.clientLicenseString = clientLicenseString;
}
public final boolean validateByDisk() {
int expectedModelSerialHash = decodeExpectedModelSerialHash(clientLicenseString);
for (HWDiskStore disk : new SystemInfo().getHardware().getDiskStores()) {
if (expectedModelSerialHash == Objects.hash(disk.getModel(), disk.getSerial())) {
return true;
}
}
return false;
}
public final boolean validateByMAC() {
String expectedMac = decodeExpectedMac(clientLicenseString);
for (NetworkIF netIF : new SystemInfo().getHardware().getNetworkIFs()) {
if (expectedMac.equals(netIF.getMacaddr())) {
return true;
}
}
return false;
}
private int decodeExpectedModelSerialHash(String clientLicenseString) {
// obfuscate license decoding, inverse of license encoding/encrypting
return 0; // return the expected hash of model and serial
}
private String decodeExpectedMac(String clientLicenseString) {
// obfuscate license decoding, inverse of license encoding/encrypting
return ""; // return the expected MAC address
}
}
Alternatively, you can see a lot of the hardware information that you can get in this example file.
Additionally, see example classes at oshi-demo
(as mentioned by Daniel Widdis); The ComputerID.java
example contains the following method:
/**
* Generates a Computer Identifier, which may be part of a strategy to
* construct a licence key. (The identifier may not be unique as in one case
* hashcode could be same for multiple values, and the result may differ
* based on whether the program is running with sudo/root permission.) The
* identifier string is based upon the processor serial number, vendor,
* processor identifier, and total processor count.
*
* @return A string containing four hyphen-delimited fields representing the
* processor; the first 3 are 32-bit hexadecimal values and the last
* one is an integer value.
*/
public static String getComputerIdentifier() {
SystemInfo systemInfo = new SystemInfo();
OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor();
ComputerSystem computerSystem = hardwareAbstractionLayer.getComputerSystem();
String vendor = operatingSystem.getManufacturer();
String processorSerialNumber = computerSystem.getSerialNumber();
String processorIdentifier = centralProcessor.getIdentifier();
int processors = centralProcessor.getLogicalProcessorCount();
String delimiter = "-";
return String.format("%08x", vendor.hashCode()) + delimiter
+ String.format("%08x", processorSerialNumber.hashCode()) + delimiter
+ String.format("%08x", processorIdentifier.hashCode()) + delimiter + processors;
}