Olá galera, preciso gerar um código unico mensal para um sistema que desenvolvi, de forma que a cada pagamento, o cliente recebe um código de ativação, e este servirá para liberar o acesso ao sistema por um determinado espaço de tempo.
Existe uma maneira de gerar um UUID (ou outro tipo de código), que seja unico e que apenas a maquina “x” consiga validá-lo?
Se sim, como posso fazer essa validação?
Agradeço a atenção
Que tal usar algo como JWT?
No token, você vai colocar informações como a data de emissão e o tempo de duração. Só quem tem a senha que foi utilizada para gerar o token consegue verificar sua validade.
Alem disso, você pode colocar uma combinação de id do cliente/UUID qualquer, e utilizar isso também na validação: a primeira vez que o token for validado, você registra isso no servidor (ou sei lá aonde) e recusa revalidações, algo assim. Como você combina o id do cliente com um UUID (uma espécie de chave primária composta), não precisa se preocupar com o UUID ser único entre clientes distintos, aumentando a quantidade de combinações possíveis.
Só uma sugestão!
Olá Ivbarbosa, obrigado por sua dica. Infelizmente eu necessito de uma solução offline, eu já pensei em algo web, mas me traria alguns transtornos. Pretendo implementar uma solução desse tipo futuramente, mas no momento, realmente precisa ser offline.
Caso tenha outra idéia, gostaria muito de ouvir.
Olá @Tiago_Santana.
Cara, a solução que vejo para a sua necessidade é: você precisa criar um algoritmo que tome algum identificador único da máquina “x” e a partir disso você gere o seu código. Pode fazer isso usando criptografia, checksum ou até mesmo criar um algoritmo próprio em que você esconda o valor real do código criado e execute a operação inversa ao chegar no destino.
Obs.: da uma olhada no MessageDigest.
Abraço.
Bom Luan, voce me deu uma luz, mas logo ela se apagou.
Tenho o seguinte código(exemplo).
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
public class EncriptadorDeDados {
public static void main(String[] argv) {
try {
KeyGenerator gerador = KeyGenerator.getInstance("DES");
SecretKey chaveDES = gerador.generateKey();
Cipher desCipher;
//Criação do cipher que conterá os objetos de criptografia
desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
// Iniciando cipher para criptografia
desCipher.init(Cipher.ENCRYPT_MODE, chaveDES);
//Dados a serem cripografados
byte[] mensagemOriginal = "Mensagem oculta!".getBytes();
System.out.println("Conteúdo byte: " + mensagemOriginal);
System.out.println("Conteúdo texto: " + new String(mensagemOriginal));
// Excriptando os dados
byte[] mensagemCriptografada = desCipher.doFinal(mensagemOriginal);
System.out.println("Conteúdo criptografado : " + mensagemCriptografada);
// Iniciando o chipher para realizar a descriptografia
desCipher.init(Cipher.DECRYPT_MODE, chaveDES);
// Texto a ser descriptografado
byte[] textoDescriptografado = desCipher.doFinal(mensagemCriptografada);
System.out.println("Texto Descriptografado: " + new String(textoDescriptografado));
} catch (NoSuchAlgorithmException e) {
System.err.println("Não foi possível localizar o algorítmo de criptografia!" + e.getMessage());
} catch (NoSuchPaddingException e) {
System.err.println("O mecanismo de preenchimento solicitado não existe no ambiente (Sistema Operacional)!" + e.getMessage());
} catch (InvalidKeyException e) {
System.err.println("Chave inválida!" + e.getMessage());
} catch (IllegalBlockSizeException e) {
System.err.println("Tamanho do bloco da mensagem inválido!" + e.getMessage());
} catch (BadPaddingException e) {
System.err.println("Preenchimento incorreto de dados!" + e.getMessage());
}
}
}
Porém, ele gera criptografias repetidas, de tempos em tempos ele volta no mesmo tipo de código, mesmo mudando a mensagem. Me parece que ele usa algum dado físico (vou falar besteira, como se pegasse um endereço de memória).
Alguma solução para isso deixar de acontecer? De maneira que, sempre que eu gere uma criptografia com dados diferentes, a “String criptografada” mude, para que o usuário não usuflua disso, e coloque todo mês o mesmo código, o que seguindo a lógica do codigo acima, será validado
Depois de muito custo, resolvi meu problema, UFA!!!
Vou resumir o processo:
1 - eu gero um UUID e uso como key da criptografia.
2 - eu insiro a “frase/string” da mensagem que desejo criptografar.
3 - passo para o usuário dois códigos, a string criptografada (que é enorme) e a key.
4 - o suftware instalado no cliente, usa a key para descriptografar a string criptografada.
5 - a partir daí, o software possui as informações para validar a licença.
Lembrando que, um UUID é gerado aleatóriamente, e todo mês o usuário irá receber uma nova licença, logo o código e a key sempre serão diferentes.
Segue o código caso alguem precise. Agradeço a todos que me deram dicas até eu chegar neste resultado
import com.sun.org.apache.xml.internal.security.utils.Base64;
import java.text.SimpleDateFormat;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;
public class EncriptadorDeDados {
private static String mensagem;
private static SecretKey key;
private static byte[] mensagemEncriptada;
public static void main(String args[]) {
//a key só recebe 16 caracteres, não me interessei muito em saber pq, já que resolvia meu //problema, mas acredito que seja por usar um código de 128 bits (8bits x 16 caracteres = 128)
String a = UUID.randomUUID().toString().substring(0,16);
key = new SecretKeySpec(a.getBytes(), "AES");
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
/* Solicita ao usuŕio que informe sua mensagem a ser encriptada */
mensagem = new String(JOptionPane.showInputDialog("Insira a string que se tornará o código").trim());
/* Encripta a Mensagem */
mensagemEncriptada = cipher.doFinal(mensagem.getBytes());
/*Converte para a base 64 e amazena a mensagem em uma variavel*/
String codigo = Base64.encode(mensagemEncriptada);
System.out.println("Mensagem Encriptada: " + codigo+"\n"+"key: " + a);
} catch (Exception e) {
}
}
private static java.sql.Date formatarDataBanco (String date){
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
try {
java.sql.Date data = new java.sql.Date(format.parse(date).getTime());
return data;
}catch (Exception e) {
return null;
}
}
}
Para Desencriptar a mensagem:
private static SecretKey key;
private static byte[] mensagemEncriptada;
private static byte[] mensagemDescriptada;
private static String codigo1, codigo2;
public static boolean inserirLicença(){
try{
//Insere os códigos no input
codigo1 = new String(JOptionPane.showInputDialog("Insira o primeiro código fornecido pelo administrador do sistema"));
codigo2 = new String(JOptionPane.showInputDialog("Insira o segundo código fornecido pelo administrador do sistema"));
key = new SecretKeySpec(codigo2.getBytes(), "AES");
mensagemEncriptada = Base64.decode(codigo1);
//Cria a variavel de criptografia
Cipher cipher = Cipher.getInstance("AES");
//"ativa" a descriptografia
cipher.init(Cipher.DECRYPT_MODE, key);
//descriptografa a mensagem
mensagemDescriptada = cipher.doFinal(mensagemEncriptada);
//transforma a mensagem em string
codigo1 = new String(mensagemDescriptada);
A grande sacada foi converter a mensagem criptografada em Base64, por algum motivo, o cósigo em si perdia o valor correto ao ser passado para o usuário. Na maquina do usuário, é só fazer o processo inverso para retornar o código em byte[], que p que a classe precisa para descriptografar.
Como apanhei um bocado, segue o código pronto para o pessoal usufluir futuramente.
Abraço.
@Tiago_Santana desculpa a demora para responder.
Que bom que você conseguiu resolver seu problema, fico feliz em ter sido útil de alguma forma.
Abraço.