Offline hashed keys

37 views
Skip to first unread message

bobc

unread,
Oct 26, 2012, 7:38:37 PM10/26/12
to lic...@googlegroups.com
Anyone have any sample code for validating the hashed keys for offline use?

I'm following the instructions to the letter and having no luck. Sample code would be most useful.

Chris Traina

unread,
Oct 26, 2012, 7:44:47 PM10/26/12
to lic...@googlegroups.com
I'm having the same problem.  Any language would help, but especially C#.  I followed the instructions in the API document:

  1. Combine the device ID with the secret key the same way you asked licmax to do so when configuring your product there.
  2. Apply the same one-way hash algorithm you configured your product to use at licmax to produce a message digest.
  3. Convert the message digest from binary to ASCII or HEX formatted string same as you configured the product at licmax.
  4. Encode the ASCII string accordingly as configured (eg. base64, base32... etc).
  5. Chop from the same side of the HEX or ENCODED string (you chose to combine the device ID and the secret key at licmax) the length of the license key you configured at licmax. The chopped piece is supposed to be your license key for this copy of your application
My results were wildly different from the license key I received.  I tried a variety of settings in the product instance configuration with no luck.

Any help would be GREATLY appreciated!

licmax

unread,
Oct 29, 2012, 1:20:53 PM10/29/12
to lic...@googlegroups.com

Hello all,

Sorry to see you having difficulties getting the hashed license key to work. What you guys described below is exactly how it is supposed to work. Are you guys using foreign keyboards by any chance? Sometimes the keyboard enters a key that is not in ascii and that can cause havoc.

Would it be possible to ask you to post here the result (the hashed license key) you are getting. We can do some debugging on our side to hopefully converge on the problem and solution.

licmax

unread,
Oct 29, 2012, 1:32:20 PM10/29/12
to lic...@googlegroups.com

Also, remember the side you attach the secret key to the device id is the side that the license key gets chopped from.

On Friday, October 26, 2012 4:44:47 PM UTC-7, Chris Traina wrote:

Chris Traina

unread,
Oct 29, 2012, 2:13:34 PM10/29/12
to lic...@googlegroups.com
We are not using any odd keyboards or unusual (non-ASCII) characters.  I tried it both ways (device ID + secret and secret + device ID), with the Product instance configured as follows:

Hash algorithm:  SHA-1
Character set:  Windows-1252
Key format:  ASCII
Base encoding:  Base64

With the device ID (randomly generated for our test) 449956a181444c0f85266699f8d91ed1 and the configured secret (for product instance RafTestProductHashed1), I received a license key of UuV0tEJA6I from your server.  When I perform a SHA-1 hash and Base64 encoding on the combined deviceID/secret string, I get:

Device + secret:  OGIzODY4ODVkNjdhOWU1OGVmY2VhNDhiYTliMTdlZjU0MGQwN2NjMw==

Secret + device:  ZWRjZGU2ZWYzYjVkMDk3ZTViZTBiMjc1YjI1ODA4NDYyNDI2NGFmMw==

Thanks!

Chris Traina

unread,
Oct 29, 2012, 2:20:27 PM10/29/12
to lic...@googlegroups.com

Would it be possible to post a step-by-step example of how your system calculates the license key for a bogus product (secret) and device ID?  That way, I could walk through my code and see where things break down.  Thanks!

licmax

unread,
Oct 29, 2012, 4:35:21 PM10/29/12
to lic...@googlegroups.com


On Monday, October 29, 2012 11:20:27 AM UTC-7, Chris Traina wrote:

Would it be possible to post a step-by-step example of how your system calculates the license key for a bogus product (secret) and device ID?  That way, I could walk through my code and see where things break down.  Thanks!

Hi Chris,

Thanks for the update. We are in the process of finding the code from our developers. We will post it for you soon.

licmax

unread,
Nov 5, 2012, 12:47:00 AM11/5/12
to lic...@googlegroups.com

We hope this helps:

package com.lm.lic.manager.management;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

import com.lm.lic.manager.hibernate.HashedLicMechAlgorithm;
import com.lm.lic.manager.hibernate.HashedLicMechCharSet;
import com.lm.lic.manager.hibernate.HashedLicMechConfig;
import com.lm.lic.manager.hibernate.HashedLicMechKeyBase;
import com.lm.lic.manager.hibernate.HashedLicMechKeyFormat;
import com.lm.lic.manager.hibernate.HashedLicMechSegment;
import com.lm.lic.manager.hibernate.LicensePaymentStatus;
import com.sun.xml.ws.util.Base64Util;

/**
 * @author Ibrahim Mustafa
 */
public class HashedLicKeyGenerator extends BasicLicKeyGenerator {
  private static Logger logger = Logger.getLogger(HashedLicKeyGenerator.class);

  /**
   *
   */
  public HashedLicKeyGenerator() {
  }

  /**
   *
   *
   * @param deviceId
   * @param num number of licenses to generate
   * @param hlmConfig
   * @param lps
   * @return
   */
  public List<String> generate(String deviceId, int num, HashedLicMechConfig hlmConfig, LicensePaymentStatus lps) {
    List<String> licenses = new ArrayList<String>(num);
    for(int i = 0; i < num; i++) {
      String lic = generate(deviceId, hlmConfig, lps);
      licenses.add(lic);
    }
    logger.info("Generated " + num + " lic keys");
    return licenses;
  }

  /**
   * For initial set of licenses that are for free, the deviceId is empty. Generate a random all-caps string
   * in its place. The generated license is going to be replaced with that built from the combination of the
   * real deviceId and the secret key the user chooses when adding a product instance.
   *
   * @param deviceId
   * @param hlmConfig
   * @param lps
   * @return
   */
  public String generate(String deviceId, HashedLicMechConfig hlmConfig, LicensePaymentStatus lps) {
    String hashedKey = hlmConfig.getMessage();
    StringBuilder sb = new StringBuilder(hlmConfig.getMessage());
    if(!StringUtils.hasText(deviceId) && lps != null && lps.isFree()) {
      deviceId = generate(hlmConfig.getLength());
    }
    if(StringUtils.hasText(deviceId)) {
      // Append the deviceId in the same place of the intended wanted segment of the hashed key.
      // If the segment is to the LAST or WHOLE of the hashed key, append the deviceId to the LAST
      // of the secret key/message. Otherwise, if segment is START, append it to the left of it.
      String segment = hlmConfig.getSegment();
      if(segment.equals(HashedLicMechSegment.START.name()))
        sb.insert(0, deviceId);
      else
        sb.append(deviceId);
      hashedKey = hashit(sb.toString(), hlmConfig);
    }

    return hashedKey;
  }

  private String hashit(String message, HashedLicMechConfig hlmConfig) {
    MessageDigest digester = null;
    String hashedKey = null;
    try {
      digester = MessageDigest.getInstance(HashedLicMechAlgorithm.getSecondName(hlmConfig.getAlgorithm()));
      MessageDigest cd = (MessageDigest)digester.clone();
      cd.update(message.getBytes(HashedLicMechCharSet.getSecondName(hlmConfig.getCharSet())));

      byte[] hashBytes = cd.digest();

      String keyFormat = hlmConfig.getKeyFormat();
      String hashText = EMPTY;
      if(HashedLicMechKeyFormat.isHex(keyFormat)) {
        hashText = toHexString(hashBytes);
      }
      else if(HashedLicMechKeyFormat.isAscii(keyFormat)) {
        if(HashedLicMechKeyBase.isBase64(hlmConfig.getBase()))
          hashText = Base64Util.encode(hashBytes);
        if(HashedLicMechKeyBase.isBase32(hlmConfig.getBase()))
          hashText = Base32.encode(hashBytes);
        else if(HashedLicMechKeyBase.isBase16(hlmConfig.getBase()))
          hashText = toHexString(hashBytes);
      }

      hashedKey = extractSegment(hlmConfig.getSegment(), hlmConfig.getLength(), hashText);
    } catch(NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch(UnsupportedEncodingException e) {
      System.out.println("The Encoding Is Not Supported");
    } catch(CloneNotSupportedException e) {
      e.printStackTrace();
    } catch(Exception e) {
      e.printStackTrace();
    }
    return hashedKey;
  }

  public String extractSegment(String segment, Integer length, String hashText) {
    if(hashText == null || hashText.length() <= length)
      return hashText;

    String subtext = hashText;
    if(segment.equals(HashedLicMechSegment.START.name())) {
      if(hashText.length() >= length)
        subtext = hashText.substring(0, length);
      else
        subtext = hashText.substring(0, hashText.length());
    }
    else if(segment.equals(HashedLicMechSegment.LAST.name())) {

      if(hashText.length() >= length && hashText.length() <= 2 * length)
        subtext = hashText.substring(length);
      else
        subtext = hashText.substring(hashText.length() - length);
    }

    return subtext;
  }
}

Reply all
Reply to author
Forward
0 new messages