I've been reading some posts here and articles around the web but I can't picture a serial keys based system for my application.
http://www.brandonstaggs.com/2007/07/26/implementing-a-partial-serial-number-verification-system-in-delphi/
I read this one but I can't turn the code into Java and I'm not very familiar with the terms either.
What possible insight can you give me on this? Ideally my application will be for sale but I don't expect it to be much popular, I don't mind much if it gets cracked if I have users that appreciate the product and buy it, but I want to avoid it to be easily cracked. Please be as specific as you can, I'm somewhat new to Java.
Thanks in advance.
A product key, also known as a software key, serial key or activation key, is a specific software-based key for a computer program. It certifies that the copy of the program is original.
Noun. serial key (plural serial keys) (computing) A string of characters or equivalent access code that must be input into a program in order to activate it.
A Serial Number can be publicly displayed. It can be openly available and searched. A Product Key must be kept out of the public eye and reach... which means you might say that a Serial Number is the number posted on your house or mailbox, while the Product Key is the key to your front door.
If you've lost or can't find the product key, contact the manufacturer. To ensure your product key is genuine, see How to tell your software is genuine and How to tell your hardware is genuine.
I was quite interested in that article, so I implemented the code in Java. may be of use
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With