Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building an 'Activation Key' Generator in JAVA

I want to develop a Key generator for my phone applications. Currently I am using an external service to do the job but I am a little concerned that the service might go offline one day hence I will be in a bit of a pickle.

How authentication works now.

  1. Public key stored on the phone.
  2. When the user requests a key the 'phone ID' is sent to the "Key Generation Service" and the encrypted key key is returned and stored inside a license file.
  3. On the phone I can check if the key is for the current phone by using a method getPhoneId() which I can check with the the current phone and grant or not grant access to features.

I like this and it works well, however, I want to create my own "Key Generation Service" from my own website.

Requirements:

  1. Public and Private Key
  2. Encryption:(Bouncy Castle)
  3. Written in JAVA
  4. Must support getApplicationId() (so that many applications can use the same key generator) and getPhoneId() (to get the phone id out of the encrypted license file)
  5. I want to be able to send the ApplicationId and PhoneId to the service for license key generation.

Can someone give me some pointers on how to accomplish this? I have dabbled around with some java encryption but am definitely no expert and can't find anything that will help me.

A list of the Java classes I would need to instantiate would be helpful.

like image 390
jax Avatar asked Nov 05 '22 12:11

jax


1 Answers

Interesting question which made me experiment a little bit with it. But as you might already have guessed, I haven't done this before. But maybe somebody else can confirm my thoughts - or rebut them but please without downvoting ;)

I'd use an asymmetric algorithm such as RSA to sign a string that consists of all the data that has to match in order to be valid. This signature is stored along with the public key in order to verify it without the need to access the server.

Expressed as Java code, this would look like this (based on Signature Sign and Verify):

import java.security.*;

public class Main {
  public static void main(String args[]) throws Exception {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

    // our server, imagine it's a webservice
    KeyServer server = new KeyServer(42);

    // init client with a copy of public key from server
    KeyClient client = new KeyClient(server.getPublicKey());

    // create string that identifies phone and application
    byte[] data = (getPhoneId() + ":" + getApplicationId()).getBytes("utf-8");

    // send data to server for signature creation
    byte[] digitalSignature = server.signData(data);

    // verify on client side
    System.out.println("verified = " + client.verifySig(data, digitalSignature));

    // bad data
    byte[] wrongData = ("anotherPhoneId" + ":" + getApplicationId()).getBytes("utf-8");
    System.out.println("verified = " + client.verifySig(wrongData, digitalSignature));

    // bad signature
    digitalSignature[5] = (byte) 0xff;
    System.out.println("verified = " + client.verifySig(data, digitalSignature));
  }

  private static String getPhoneId() {
    return "somephone";
  }

  private static String getApplicationId() {
    return "someapp";
  }

  public static class KeyClient {

    private PublicKey _publicKey;
    private Signature _signer;

    public KeyClient(PublicKey publicKey) {
      if (publicKey == null) {
        throw new NullPointerException("publicKey");
      }
      _publicKey = publicKey;

      try {
        _signer = Signature.getInstance("SHA1withRSA");
      } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("failed to get Signature", e);
      }
    }

    public boolean verifySig(byte[] data, byte[] sig) throws Exception {
      synchronized (_signer) {
        _signer.initVerify(_publicKey);
        _signer.update(data);
        return (_signer.verify(sig));
      }
    }
  }

  public static class KeyServer {

    private KeyPair _keyPair;
    private Signature _signer;

    public KeyServer(int seed) {
      try {
        _keyPair = generateKeyPair(seed);
      } catch (Exception e) {
        throw new RuntimeException("failed to generate key pair for seed " + seed, e);
      }

      try {
        _signer = Signature.getInstance("SHA1withRSA");
      } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("failed to get Signature", e);
      }
    }

    public PublicKey getPublicKey() {
      return _keyPair.getPublic();
    }

    public byte[] signData(byte[] data) throws InvalidKeyException, SignatureException {
      synchronized (_signer) {
        _signer.initSign(_keyPair.getPrivate());
        _signer.update(data);
        return (_signer.sign());
      }
    }

    private KeyPair generateKeyPair(long seed) throws Exception {
      KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
      SecureRandom rng = SecureRandom.getInstance("SHA1PRNG", "SUN");
      rng.setSeed(seed);
      keyGenerator.initialize(2048, rng);
      return (keyGenerator.generateKeyPair());
    }

  }
}
like image 58
sfussenegger Avatar answered Nov 12 '22 10:11

sfussenegger