17

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();
        }
    }
}
Steve Kuo
  • 61,876
  • 75
  • 195
  • 257
Yuval Cohen
  • 233
  • 3
  • 11
  • 1
    As you've noticed, that MAC address can change or be not available, or more than one can be present. – Steve Kuo Aug 27 '12 at 18:58
  • Don't try this. It is a misuse of the MAC address. It is there to identify nodes on an Ethernet. It is not a unique system identifier, and should not be used as such. It can change with the hardware, the hardware state, or by user action. – user207421 Aug 27 '12 at 21:12

3 Answers3

5

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);
    }
  }
}
Alfabravo
  • 7,493
  • 6
  • 46
  • 82
  • Thanks for you comment Is there a way to select the ethernet out of this list? – Yuval Cohen Aug 27 '12 at 16:33
  • Well, it goes thru interface names, so it would be using getDisplayName method and comparing resulting string to "eth0" on *nix-like environments. – Alfabravo Aug 27 '12 at 17:40
  • @hypfco I'd try catching exceptions and checking them. The comment is too vague to guess what happened in your laptop. – Alfabravo May 15 '15 at 17:31
  • @Alfabravo. I was in a hurry, I had to concoct another code, i'll publish it whenever i return to it :)..... – Brethlosze May 20 '15 at 21:56
  • Is it supposed to work when the network is disconnected? Not for me, why? – Patrick Aug 08 '18 at 19:38
  • 1
    @Patrick if you have a problem, preferably with a error stack trace or a block of code to replicate your problem, then you can ask a question :) – Alfabravo Aug 08 '18 at 19:56
  • @Alfabravo .. run your test without network plugged in. I do not think this is a valid answer. – AnthonyJClink May 22 '19 at 18:56
  • @AnthonyJClink Well, OP never marked it as accepted and never added info about what he found while trying this code. Everything here says it might not be so. :) – Alfabravo May 22 '19 at 19:31
  • @Alfabravo Any ideas how to achieve it? – AnthonyJClink May 23 '19 at 01:33
  • @AnthonyJClink I'd recommend to a) ask yourself a question adding what you tried and the details on the environment you are using, or b) share here at least the OS you are using and Java version, so we can try and see why for some it worked and for you it didn't – Alfabravo May 23 '19 at 19:18
  • @Alfabravo I feel the scenario has not been tested by the commenters here. please turn off any wifi connections or disconnect the wire to the computer the answer runs on. And see if you get a different result. – AnthonyJClink May 24 '19 at 20:13
  • The first commenter seemed like trying it. Sorry, can't test the code right now. Feel free to give it a go or follow one of the options I mentioned – Alfabravo May 24 '19 at 21:02
3

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;
}
xtratic
  • 4,600
  • 2
  • 14
  • 32
  • 1
    I'm the primary maintainer for OSHI. The `oshi-demo` artifact has an example class that is based on another SO answer and does a lot more "fingerprint" kind of stuff. Take a look and possibly update your answer to refer to it! – Daniel Widdis Jun 12 '19 at 05:20
1

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()
}
janos
  • 120,954
  • 29
  • 226
  • 236