2010-09-01 18 views
8

He estado leyendo algunos mensajes aquí y artículos en la web, pero no puedo imaginar un sistema basado en claves de serie para mi aplicación.Claves de serie: ¿Qué hacer?

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

leí este, pero no puedo convertir el código en Java y no estoy muy familiarizado con los términos tampoco.

¿Qué posible idea me puede dar sobre esto? Idealmente, mi aplicación estará a la venta, pero no espero que sea muy popular, no me importa mucho si se agrieta si tengo usuarios que aprecian el producto y lo compran, pero quiero evitar que sea fácil. agrietado. Sea tan específico como pueda, soy algo nuevo en Java.

Gracias de antemano.

Respuesta

6

No es tan difícil, si tiene alguna flexibilidad en sus requisitos, tal vez el siguiente esquema le funcione.

Puede producir K = [SN, H ([X, SN, Y])] que es la concatenación de un número de serie creciente con un hash, donde el hash es una función hash segura de la concatenación del número de serie entre las constantes únicas X e Y que son secretas "salt" you use to prevent the use of rainbow tables.

Utilice un conocido algoritmo de hash seguro (por ejemplo, SHA-1 o SHA-2; MD5 es probable que también adecuada, ya que las debilidades conocidas para MD5 son los ataques de colisión, y nopreimage attacks) y usted debe estar todo listo , al menos en lo que respecta a la parte de la clave de serie (es probable que desee evitar que dos personas utilicen la misma clave).

La otra cosa que puede hacer que es útil es usar K = [SN, T, H ([X, SN, T, Y])] - use tanto el número de serie como una marca de tiempo. Esto se puede usar para permitir solo una ventana de uso limitado para la clave de serie: es válida dentro de N segundos de la marca de tiempo, por lo que evitaría la reutilización de la clave fuera de esa ventana.

Luego codifica/decodifica K en una representación que se puede usar para permitir fácilmente a los usuarios ingresar la clave (por ejemplo, base64).

Lo mejor es tener un algoritmo general simple y transparente: la ofuscación no lo ayudará si alguien realiza un ingeniería inversa de su esquema.

+0

Gracias Jason. ¡Comenzaré a trabajar en eso! – Qosmo

4

Proteger las aplicaciones en general no es una tarea sencilla. Muchas compañías están invirtiendo una gran cantidad de dinero para encontrar nuevos algoritmos de seguridad, que se agrietan muy rápido.

Proteger las aplicaciones Java es un poco más difícil. Cualquier algoritmo de verificación en serie incluido en su aplicación se puede descompilar, por lo que un generador de clave serial será bastante fácil de construir.

Un buen punto de partida es el artículo que usted dio. Le dice cómo crear un sistema de verificación de claves y cómo generar claves para sus usuarios (legítimos).

Después de implementar dicho algoritmo, sugiero que proteja un poco el código fuente, para que la descompilación se vuelva un poco más "engañosa". Use técnicas de ofuscación de código para ocultar su implementación de algoritmo de verificación. Esto también hará que la tarea sea más difícil para las personas que intenten descifrar su aplicación simplemente modificando el código de bytes.

Una buena técnica podría ser exportar su algoritmo de verificación de clave en un servidor remoto. El cliente envía la clave al servidor, que responde con un 'código de validación' para decirle a su aplicación que su clave es válida. Pero esto no impide que los usuarios modifiquen su aplicación para eliminar cualquier procedimiento de verificación de clave. Y esto puede ser muy molesto para los usuarios legítimos que no tienen una conexión a Internet las 24 horas. Estoy pensando en Steam, que verifica la validez de las claves en cada lanzamiento en Internet y que molesta a muchos usuarios.

Para encontrar una buena técnica de protección, mire a su alrededor y trate de determinar cómo otras personas hacen, qué técnicas funcionan, cuáles no. Son un gran ejemplo (la industria de los videojuegos, en particular). Pero tenga en cuenta que incluso las mejores compañías no pueden asegurar sus aplicaciones correctamente. Ninguna técnica es irrompible.

+0

Me gustaría poder marcar tanto como una respuesta, pero la respuesta de Jason me dio algo para empezar a trabajar en lo que voy a marcar su respuesta a los efectos de mi pregunta. ¡Gracias! – Qosmo

12

Estaba bastante interesado en ese artículo, así que implementé el código en Java. puede ser de utilidad

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

Estaría encantado si muestra cómo implementarlo. –

Cuestiones relacionadas