Translate

quinta-feira, 5 de setembro de 2013

Java - Como obter o id do computador em Windows e Linux Ubuntu

Alguns aplicativos dos tipos desktop, serviço ou licenciadores baseados em processamento fortemente identificado sempre requerem do programador desafios novos para pegar a identidade física do computador.

Quando surge o problema de pegar uma identidade sempre vem a cabeça do programador mais experiente as seguintes perguntas seguidas por mais perguntas:

1) Se eu pegar o macaddress da placa de rede?
2) Mas se a placa de rede queimar ou for trocada de posição?
3) Posso obter o número mais seguro?
4) Será este número o código de série da CPU porque o FCC cuida para não haver repetição de serial, ok?
5) Mas a máquina pode ser virtual e copiada, o macaddress e código da CPU podem ser clonados e assim o FCC perder controle?
6) Talvez o nome da máquina possar ser menos sujeito a alteração e se tiver clone, não vai funcionar na mesma rede, mas será que numa mudança de política de infra-estrutura não vão mudar o nome da máquina legalmente?
7) Como faço para saber se a máquina é virtual?

Não existe uma fórmula mágica para impedir as alterações das identidades de uma máquina, até de seu processador porque pode ser trocado.

Em caso de máquinas virtuais o problema é agravado mais ainda porque elas podem ser clonadas e funcionarem com identidades idênticas em redes (LAN) diferentes.

Por causa disso a única forma segura de garantir uma identidade única é resolvida através de tags ou tokens em forma de hardware para imprimir uma identidade imutável, no entanto, este método pode ser bastante caro e ainda reduzir bastante a atratividade de seu aplicativo num cenário mundial onde as pessoas buscam fazer as coisas no menor tempo, da forma mais simples e mais barata.

Mas não se desespere, para a maior parte dos casos, obter a identidade na primeira execução do software e persistir em um arquivo, registro do sistema operacional ou banco de dados de maneira ofuscada já é o suficiente para congelar e tornar imutável o conjunto de identidades da máquina. Esta técnica pode ser chamada de congelar ou tirar um snapshot dos identificadores da máquina para não sofrerem uma crise caso uma identidade utilizada para validação de sua aplicação seja legalmente mudada por razões diversas.

A classe a seguir se chama ComputerInfo e possui métodos para obter o número de série da CPU, endereço IP do computador, nome do computador, macaddress do primeiro adaptador de rede e se é um adaptador virtual. Não é objetivo discutir a utilização desta classe porque isso vai variar da necessidade de solução de cada um.

A classe ComputerInfo

Crie um arquivo classe chamado ComputerInfo.java:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.StringTokenizer;

public class ComputerInfo {

public static final String NO_CPU_ID = "XXXXXXXXXX";

private static ComputerInfo ci = null;

private String cpuSerial;
private String hostName;
private String macAddress;
private String hostAddress;
private boolean virtualAdapter;


private ComputerInfo() {

try {

NetworkInterface netInter = null;

if (OS.OS == OS.WINDOWS) {
InetAddress localHost = InetAddress.getLocalHost();
netInter = NetworkInterface.getByInetAddress(localHost);
} else if (OS.OS == OS.LINUX) {
for (NetworkInterface n : Collections.list(NetworkInterface.getNetworkInterfaces())) {
netInter = n;
break;
}
}

byte[] macAddressBytes = netInter.getHardwareAddress();

String macAddress = "";

// Verifica se obteve macaddress
if (macAddressBytes != null && macAddressBytes.length > 5) {

macAddress = String.format(
"%1$02x%2$02x%3$02x%4$02x%5$02x%6$02x", macAddressBytes[0],
macAddressBytes[1], macAddressBytes[2], macAddressBytes[3],
macAddressBytes[4], macAddressBytes[5]).toUpperCase();
}

InetAddress addr = InetAddress.getLocalHost();

this.cpuSerial = getCPUID();
this.hostName = addr.getHostName();
this.macAddress = macAddress;
this.hostAddress = addr.getHostAddress();
this.virtualAdapter = netInter.isVirtual();

} catch (UnknownHostException e) {
System.err.println(e);
} catch (SocketException e) {
System.err.println(e);
}

}

public static synchronized ComputerInfo getIntance() {

if (ci == null) {
ci = new ComputerInfo();
}

return ci;

}

public String getCpuSerial() {
return cpuSerial;
}

public String getHostName() {
return hostName;
}

public String getMacAddress() {
return macAddress;
}

public String getHostAddress() {
return hostAddress;
}

public boolean isVirtualAdapter() {
return virtualAdapter;
}

public static String getCPUID() {

String result = null;

if (OS.OS == OS.WINDOWS) {
result = getCPUIDForWindows();
} else if (OS.OS == OS.LINUX) {
result = getCPUIDForLinux();
}

return result;

}

private static String getCPUIDForWindows() {

StringBuffer result = new StringBuffer();

try {
File file = File.createTempFile("tmp", ".vbs");
file.deleteOnExit();
FileWriter fw = new java.io.FileWriter(file);

StringBuffer vbs = new StringBuffer();
vbs.append("On Error Resume Next \r\n\r\n");
vbs.append("strComputer = \".\"  \r\n");
vbs.append("Set objWMIService = GetObject(\"winmgmts:\" _ \r\n");
vbs.append("    & \"{impersonationLevel=impersonate}!\\\\\" & strComputer & \"\\root\\cimv2\") \r\n");
vbs.append("Set colItems = objWMIService.ExecQuery(\"Select * from Win32_Processor\")  \r\n ");
vbs.append("For Each objItem in colItems\r\n ");
vbs.append("    Wscript.Echo objItem.ProcessorId  \r\n ");
vbs.append("    exit for  ' do the first cpu only! \r\n");
vbs.append("Next                    ");

fw.write(vbs.toString());
fw.close();
Process p = Runtime.getRuntime().exec(
"cscript //NoLogo " + file.getPath());
BufferedReader input = new BufferedReader(new InputStreamReader(p
.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
result.append(line);
}
input.close();
} catch (Exception e) {

}

if (result == null || result.toString().trim().length() < 1 ) {
result = new StringBuffer();
return NO_CPU_ID;
}

return result.toString().trim();

}

private static String getCPUIDForLinux() {

String result = null;

try {

Process p = Runtime.getRuntime().exec("dmidecode -t 4");

BufferedReader input =
new BufferedReader
(new InputStreamReader(p.getInputStream()));

String line;

while ((line = input.readLine()) != null) {
if (line.length()>3 && line.substring(0, 3).equals(("ID:"))) {
StringTokenizer tokenizer = new StringTokenizer(line, ":");
line = tokenizer.nextToken();
result = tokenizer.nextToken();
}
}
input.close();

if(result==null || result.trim().length()==0) {
result=NO_CPU_ID;
}
}
catch(Exception e){
System.err.println(e);
}
return result.trim();
}

}

A enum OS utilizada como dependência

Crie um arquivo enum (não é classe) chamado OS.java:

public enum OS {

WINDOWS("windows"), LINUX("linux");

public static final OS OS;

private static DGLog logger = new DGLog(br.com.digicon.dnserver.util.OS.class);

private String name;

static {
String tempOS = System.getProperty("os.name"); 

if(tempOS==null) {
tempOS = "N/A";
} else {
tempOS = tempOS.trim().toLowerCase();
}

if(tempOS.indexOf(WINDOWS.name)!=-1){
OS = WINDOWS;
} else if(tempOS.indexOf(LINUX.name)!=-1){
OS = LINUX;
} else {
OS=null;
logger.error("O sistema operacional %s não é compatível a aplicação", tempOS);
}
}
OS(String os) {
name = os;
}

public String toString() {
return this.name;
}

}

Obs.: Esta classe é de autoria do Rafael Lima que contribuiu para a melhoria de ComputerInfo.

Testes da classe ComputerInfo

Agora vamos testar com um método main para isso:

public static void main(String args[]) {

ComputerInfo cii = ComputerInfo.getIntance();

System.out.println(cii.getCpuSerial());
System.out.println(cii.getHostAddress());
System.out.println(cii.getHostName());
System.out.println(cii.getMacAddress());
System.out.println(cii.isVirtualAdapter());
}

Resultados

Após obter a instância por meio de uma função singleton getInstance(), a série de invocações de métodos para obtenção dos identificadores do computador gerou a seguinte sequência de resultados:

BFEBFBFF000300A9
192.168.0.8
BRE1010
005056C00001
false

Boa sorte!

Um comentário:

  1. Olá amigo tentei usar seu código, mas parece que ele esta incompleto.
    1 - de onde vem o DFLog?
    2 - (br.com.digicon.dnserver.util.OS.class) que ou de onde vem esse endereço;
    3 - o public void main() vai colocado no mesmo arquivo computerinfo?
    Agradeço a ajuda.

    ResponderExcluir