2010-09-01 20 views
8

Ho letto alcuni post qui e articoli sul Web, ma non riesco a immaginare un sistema basato su tasti seriali per la mia applicazione.Chiavi seriali: cosa fare?

http://www.brandonstaggs.com/2007/07/26/implementing-a-partial-serial-number-verification-system-in-delphi/

ho letto questo, ma non riesco a trasformare il codice in Java e io non sono molto familiare con i termini sia.

Quale possibile intuizione puoi darmi su questo? Idealmente la mia applicazione sarà in vendita ma non mi aspetto che sia molto popolare, non mi interessa molto se si rompe se ho utenti che apprezzano il prodotto e lo comprano, ma voglio evitare che sia facilmente incrinato. Si prega di essere il più specifico possibile, sono un po 'nuovo a Java.

Grazie in anticipo.

risposta

6

Non è così difficile, se sei un po 'flessibile nelle tue esigenze - forse lo schema di seguito potrebbe funzionare per te.

Si potrebbe semplicemente produrre K = [SN, H ([X, SN, Y])] che è la concatenazione di un numero seriale incrementale con un hash, dove l'hash è una funzione hash sicura della concatenazione del numero di serie tra costanti univoche X e Y che sono segrete "salt" you use to prevent the use of rainbow tables.

Utilizzare un algoritmo sicuro hash noto (ad esempio, SHA-1 o SHA-2; MD5 è probabilmente anche adeguata, dal momento che i punti deboli noti per MD5 sono attacchi di collisione, e nonpreimage attacks) e si dovrebbe essere tutto a posto , almeno per quanto riguarda la parte della chiave seriale (probabilmente vorrai impedire a due persone di usare la stessa chiave).

L'altra cosa che puoi fare è utile è usare K = [SN, T, H ([X, SN, T, Y])] - usa sia il numero seriale che un timestamp. Questo può essere usato per consentire solo una finestra di utilizzo ristretta per la chiave seriale: è valida entro N secondi dal timestamp, quindi impedirebbe il riutilizzo della chiave fuori da quella finestra.

Quindi codificare/decodificare K in una rappresentazione che può essere utilizzata per consentire facilmente agli utenti di immettere la chiave (ad esempio base64).

È meglio disporre di un algoritmo generale semplice e trasparente: l'offuscamento non ti aiuterà se qualcuno esegue il reverse-engineering del tuo schema.

+0

Grazie, Jason. Inizierò a lavorarci! – Qosmo

4

La protezione delle applicazioni in generale non è un'operazione semplice. Molte aziende stanno investendo un sacco di soldi per trovare nuovi algoritmi di sicurezza, che si rompono molto velocemente.

La protezione delle applicazioni Java è un po 'più difficile. Qualsiasi algoritmo di verifica seriale incorporato nella tua applicazione può essere decompilato, quindi un generatore di chiavi seriali sarà abbastanza facile da costruire.

Un buon punto di partenza è l'articolo che hai fornito. Ti dice come costruire un sistema di verifica delle chiavi e come generare le chiavi per i tuoi utenti (legittimi).

Dopo aver implementato tale algoritmo, suggerisco di proteggere un po 'il codice sorgente, in modo che la decompilazione diventi un po' più "complicata". Utilizzare tecniche di offuscamento del codice per nascondere l'implementazione dell'algoritmo di verifica. Ciò renderà anche più difficile per le persone che cercano di decifrare la tua applicazione semplicemente modificando il codice byte.

Una buona tecnica potrebbe essere quella di esportare l'algoritmo di verifica della chiave su un server remoto. Il client invia la chiave al server, che risponde con un "codice di convalida" per comunicare all'applicazione che la chiave è valida. Ma questo non impedisce agli utenti di modificare la tua applicazione per rimuovere qualsiasi procedura di verifica delle chiavi. E questo potrebbe essere molto fastidioso per gli utenti legittimi che non dispongono di una connessione Internet 24 ore al giorno. Sto pensando a Steam, che verifica la validità della chiave ad ogni lancio su Internet e che infastidisce molti utenti.

Per trovare una buona tecnica di protezione, guardarsi attorno e cercare di determinare come fanno gli altri, quali tecniche funzionano, quali no. Sono un sacco di esempi (industria dei videogiochi, in particolare). Ma tieni presente che anche le migliori aziende non sono in grado di proteggere correttamente le loro applicazioni. Nessuna tecnica è indistruttibile.

+0

Vorrei poter contrassegnare entrambi come risposta, ma la risposta di Jason mi ha dato qualcosa su cui iniziare a lavorare, quindi segnerò la sua risposta ai fini della mia domanda. Grazie! – Qosmo

12

Ero abbastanza interessato a quell'articolo, quindi ho implementato il codice in Java. potrebbe essere utile

import java.util.Locale; 
import java.util.Set; 
import java.util.TreeSet; 

public class KeyValidator { 
    private static final byte[][] params = new byte[][] { { 24, 4, 127 }, { 10, 0, 56 }, { 1, 2, 91 }, { 7, 1, 100 } }; 
    private static final Set<String> blacklist = new TreeSet<String>(); 

    static { 
     blacklist.add("11111111"); 
    } 

    private static byte PKV_GetKeyByte(final int seed, final byte a, final byte b, final byte c) { 
     final int a1 = a % 25; 
     final int b1 = b % 3; 
     if (a1 % 2 == 0) { 
      return (byte) (((seed >> a1) & 0x000000FF)^((seed >> b1) | c)); 
     } else { 
      return (byte) (((seed >> a1) & 0x000000FF)^((seed >> b1) & c)); 
     } 
    } 

    private static String PKV_GetChecksum(final String s) { 
     int left = 0x0056; 
     int right = 0x00AF; 
     for (byte b : s.getBytes()) { 
      right += b; 
      if (right > 0x00FF) { 
       right -= 0x00FF; 
      } 
      left += right; 
      if (left > 0x00FF) { 
       left -= 0x00FF; 
      } 
     } 
     int sum = (left << 8) + right; 
     return intToHex(sum, 4); 
    } 

    public static String PKV_MakeKey(final int seed) { 
     // Fill KeyBytes with values derived from Seed. 
     // The parameters used here must be exactly the same 
     // as the ones used in the PKV_CheckKey function. 
     // A real key system should use more than four bytes. 
     final byte[] keyBytes = new byte[4]; 
     keyBytes[0] = PKV_GetKeyByte(seed, params[0][0], params[0][1], params[0][2]); 
     keyBytes[1] = PKV_GetKeyByte(seed, params[1][0], params[1][1], params[1][2]); 
     keyBytes[2] = PKV_GetKeyByte(seed, params[2][0], params[2][1], params[2][2]); 
     keyBytes[3] = PKV_GetKeyByte(seed, params[3][0], params[3][1], params[3][2]); 

     // the key string begins with a hexadecimal string of the seed 
     final StringBuilder result = new StringBuilder(intToHex(seed, 8)); 

     // then is followed by hexadecimal strings of each byte in the key 
     for (byte b : keyBytes) { 
      result.append(intToHex(b, 2)); 
     } 

     // add checksum to key string 
     result.append(PKV_GetChecksum(result.toString())); 

     final String key = result.toString(); 
     return key.substring(0, 4) + "-" + key.substring(4, 8) + "-" + key.substring(8, 12) + "-" + key.substring(12, 16) + "-" + key.substring(16, 20); 
    } 

    private static boolean PKV_CheckKeyChecksum(final String key) { 
     // remove cosmetic hyphens and normalise case 
     final String comp = key.replaceAll("-", "").toLowerCase(Locale.UK); 
     if (comp.length() != 20) { 
      return false; // Our keys are always 20 characters long 
     } 

     // last four characters are the checksum 
     final String checksum = comp.substring(16); 
     return checksum.equals(PKV_GetChecksum(comp.substring(0, 16))); 
    } 

    public static Status PKV_CheckKey(final String key) { 
     if (!PKV_CheckKeyChecksum(key)) { 
      return Status.KEY_INVALID; // bad checksum or wrong number of 
      // characters 
     } 

     // remove cosmetic hyphens and normalise case 
     final String comp = key.replaceAll("-", "").toLowerCase(Locale.UK); 

     // test against blacklist 
     for (String bl : blacklist) { 
      if (comp.startsWith(bl)) { 
       return Status.KEY_BLACKLISTED; 
      } 
     } 

     // At this point, the key is either valid or forged, 
     // because a forged key can have a valid checksum. 
     // We now test the "bytes" of the key to determine if it is 
     // actually valid. 

     // When building your release application, use conditional defines 
     // or comment out most of the byte checks! This is the heart 
     // of the partial key verification system. By not compiling in 
     // each check, there is no way for someone to build a keygen that 
     // will produce valid keys. If an invalid keygen is released, you 
     // simply change which byte checks are compiled in, and any serial 
     // number built with the fake keygen no longer works. 

     // Note that the parameters used for PKV_GetKeyByte calls MUST 
     // MATCH the values that PKV_MakeKey uses to make the key in the 
     // first place! 

     // extract the Seed from the supplied key string 
     final int seed; 
     try { 
      seed = Integer.valueOf(comp.substring(0, 8), 16); 
     } catch (NumberFormatException e) { 
      return Status.KEY_PHONY; 
     } 

     // test key 0 
     final String kb0 = comp.substring(8, 10); 
     final byte b0 = PKV_GetKeyByte(seed, params[0][0], params[0][1], params[0][2]); 
     if (!kb0.equals(intToHex(b0, 2))) { 
      return Status.KEY_PHONY; 
     } 

     // test key1 
     final String kb1 = comp.substring(10, 12); 
     final byte b1 = PKV_GetKeyByte(seed, params[1][0], params[1][1], params[1][2]); 
     if (!kb1.equals(intToHex(b1, 2))) { 
      return Status.KEY_PHONY; 
     } 

     // test key2 
     final String kb2 = comp.substring(12, 14); 
     final byte b2 = PKV_GetKeyByte(seed, params[2][0], params[2][1], params[2][2]); 
     if (!kb2.equals(intToHex(b2, 2))) { 
      return Status.KEY_PHONY; 
     } 

     // test key3 
     final String kb3 = comp.substring(14, 16); 
     final byte b3 = PKV_GetKeyByte(seed, params[3][0], params[3][1], params[3][2]); 
     if (!kb3.equals(intToHex(b3, 2))) { 
      return Status.KEY_PHONY; 
     } 

     // If we get this far, then it means the key is either good, or was made 
     // with a keygen derived from "this" release. 
     return Status.KEY_GOOD; 
    } 

    protected static String intToHex(final Number n, final int chars) { 
     return String.format("%0" + chars + "x", n); 
    } 

    public enum Status { 
     KEY_GOOD, KEY_INVALID, KEY_BLACKLISTED, KEY_PHONY 
    } 
} 
+0

Sarei lieto se mostri come implementarlo. –