I want to get the MAC of the ethernet card (for product key)
I tried using this kind of solution and searched here, the problem is that when all the networking is disconnected (ethernet and wifi) it returns empty MAC adress.
I rather get the adress of the ethernet even if it's disconnected.
Thanks!!
public static void main(String[] args)
{
InetAddress ip;
try {
ip = InetAddress.getLocalHost();
System.out.println("The mac Address of this machine is :" + ip.getHostAddress());
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
System.out.print("The mac address is : ");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++){
sb.append(String.format("%02X%s", mac[i],(i< mac.length - 1)?"-":""));
}
System.out.println(sb.toString());
}
catch (UnknownHostException e) {
e.printStackTrace();
}
catch (SocketException e) {
e.printStackTrace();
}
}
}
Using InetAddress
binds you to look in a list of IP addresses. If you have none because the interfaces are all disconnected, then you can't loop over such interfaces.
You should try with NetworkInterface
class.
public class MacAddressTest
{
public static void main(String[] args) throws Exception
{
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements())
{
NetworkInterface nif = interfaces.nextElement();
byte[] lBytes = nif.getHardwareAddress();
StringBuffer lStringBuffer = new StringBuffer();
if (lBytes != null)
{
for (byte b : lBytes)
{
lStringBuffer.append(String.format("%1$02X ", new Byte(b)));
}
}
System.out.println(lStringBuffer);
}
}
}
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;
}
It seems the Java standard library doesn't provide a way to enumerate all the network interfaces.
- The
InetAddress
is clearly not suitable, as it represents an IP address.
- The
NetworkInterface
is also not suitable, because it "represents a Network Interface made up of a name, and a list of IP addresses assigned to this interface", which implies that if a network interface doesn't have an IP address, then NetworkInterface
is not a good fit to represent it. And sure enough, despite the javadoc of NetworkInterface.getNetworkInterfaces()
saying "returns all the interfaces on this machine", in fact it returns only the network interfaces that have IP addresses.
Note that the official tutorial to retrieve "all" network interfaces also produces a list of only the network interfaces having IP addresses.
So what can you do?
I think you have no choice but to look for other ways to find this information in the runtime environment.
Depending on the operating system, you may be able to find the network interfaces by parsing through files in the filesystem (as in this answer for Linux), or by parsing the output of system commands such as ifconfig
in *NIX or ipconfig
in Windows.
But I don't think this would be very robust,
as you may need to deal with unpleasant problems such as different behaviors depending on operating system version or the availability and version of system commands.
Another option could be to develop a portable cross-platform tool in a language that is able to get the list of network interfaces natively, and produce it in consistent format that's easy to parse. Then, you could bundle that tool in your solution.
A good candidate that comes to mind is Go: it can get the list of network interfaces natively, and it's easy to compile to all required target platforms, and the binaries are ready to use without depending on additional runtime environments.
Here's a simple implementation to print a list of network interfaces, one per line, the name, MAC, and IP addresses separated by tabs:
package main
import (
"fmt"
"net"
"strings"
)
func macsAndIps() {
ifaces, err := net.Interfaces()
if err != nil {
fmt.Print(fmt.Errorf("could not get interfaces: %+v\n", err.Error()))
return
}
for _, iface := range ifaces {
addresses, err := iface.Addrs()
if err != nil {
fmt.Print(fmt.Errorf("could not get addresses for interface %v: %v\n", iface, err.Error()))
}
ips := []string{}
for _, address := range addresses {
// fmt.Printf("%#v\n", address)
switch address.(type) {
case *net.IPNet:
ips = append(ips, address.String())
}
}
name := iface.Name
mac := iface.HardwareAddr
fmt.Printf("%v\t%v\t%s\n", name, mac, strings.Join(ips, "\t"))
}
}
func main() {
macsAndIps()
}