2010-09-01 2 views
8

Я читал некоторые сообщения здесь и статьи по сети, но я не могу представить систему на основе серийных ключей для своего приложения.Последовательные ключи: что делать?

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

Я прочитал это один, но я не могу превратить код Java, и я не очень хорошо знаком с условиями либо.

Какое возможное понимание вы можете мне дать? В идеале мое приложение будет продаваться, но я не ожидаю, что оно будет очень популярным, я не против, если он будет взломан, если у меня есть пользователи, которые ценят продукт и покупают его, но я хочу, чтобы это было легко трещины. Пожалуйста, будьте как можно более конкретны, я немного новичок в Java.

Заранее спасибо.

ответ

6

Это не так сложно, если вы несколько гибки в своих требованиях - возможно, приведенная ниже схема будет работать на вас.

Вы можете просто создать K = [SN, H ([X, SN, Y])], который является конкатенацией возрастающего серийного номера с хешем, где хеш является защищенной хеш-функцией конкатенации серийный номер между уникальными константами X и Y, которые являются секретными "salt" you use to prevent the use of rainbow tables.

Используйте хорошо известный надежный алгоритм хеширования (например, SHA-1 или SHA-2, MD5, вероятно, также адекватной, так как известные слабые места для MD5 являются столкновения атаки, и неpreimage attacks), и вы должны быть все в порядке , по крайней мере, насколько это касается серийного ключа (вы, вероятно, захотите, чтобы два человека не использовали один и тот же ключ).

Другое дело, что полезно использовать K = [SN, T, H ([X, SN, T, Y])] - используйте как серийный номер, так и временную метку. Это можно использовать, чтобы разрешить только узкое окно использования для серийного ключа: оно действует в течение N секунд временной метки, поэтому оно предотвратит повторное использование ключа вне этого окна.

Затем кодируйте/декодируйте K до представления, которое может использоваться, чтобы легко разрешить пользователям вводить ключ (например, base64).

Лучше всего иметь простой и прозрачный общий алгоритм - обфускация не поможет вам, если кто-то обратит ваше внимание на схему.

+0

Спасибо, Джейсон. Я начну работать над этим! – Qosmo

4

Защита приложений в целом - непростая задача. Многие компании инвестируют много денег, чтобы найти новые алгоритмы обеспечения безопасности, которые быстро взломаются.

Защита приложений Java немного сложнее. Любой алгоритм последовательной проверки, встроенный в ваше приложение, может быть декомпилирован, поэтому генератор серийного ключа будет довольно легко построить.

Хорошей отправной точкой является статья, которую вы дали. В нем рассказывается, как создать систему проверки ключей и как создавать ключи для ваших (законных) пользователей.

После реализации такого алгоритма я предлагаю вам немного исправить исходный код, поэтому декомпиляция станет немного более «сложной». Используйте методы обфускации кода, чтобы скрыть реализацию алгоритма проверки. Это также затруднит задачу для людей, пытающихся взломать ваше приложение, просто изменив байт-код.

Хорошей техникой может быть экспорт вашего ключевого алгоритма проверки на удаленном сервере. Клиент отправляет ключ на сервер, который отвечает «кодом проверки», чтобы сообщить вашему приложению, что ваш ключ действителен. Но это не мешает пользователям изменять ваше приложение, чтобы удалить любую процедуру проверки ключей. И это может быть очень неприятно для законных пользователей, у которых нет 24-часового подключения к Интернету. Я думаю о Steam, который проверяет правильность ключа при каждом запуске в Интернете и что раздражает многих пользователей.

Чтобы найти хорошую технику защиты, осмотритесь и попытайтесь определить, как работают другие люди, какие методы работают, а какие нет. Это много примеров (в частности, индустрия видеоигр). Но имейте в виду, что даже лучшие компании не в состоянии обеспечить правильное использование своих приложений. Никакие методы не являются нерушимыми.

+0

Мне жаль, что я не могу отметить оба ответа, но ответ Джейсона дал мне кое-что, чтобы начать работать, поэтому я помету его ответ для целей моего вопроса. Благодаря! – Qosmo

12

Меня очень интересовала эта статья, поэтому я реализовал код на Java. могут быть полезны

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

Я был бы рад, если бы вы продемонстрировали, как его реализовать. –

Смежные вопросы