44import com .google .common .annotations .VisibleForTesting ;
55import com .google .gson .JsonElement ;
66import com .google .gson .JsonPrimitive ;
7+ import org .apache .commons .codec .binary .Hex ;
78import org .apache .http .annotation .ThreadSafe ;
89import org .slf4j .Logger ;
910import org .slf4j .LoggerFactory ;
1011
12+ import javax .crypto .Mac ;
13+ import javax .crypto .spec .SecretKeySpec ;
1114import java .io .Closeable ;
1215import java .io .IOException ;
16+ import java .io .UnsupportedEncodingException ;
1317import java .net .URL ;
18+ import java .security .InvalidKeyException ;
19+ import java .security .NoSuchAlgorithmException ;
1420import java .util .HashMap ;
1521import java .util .Map ;
1622import java .util .concurrent .Future ;
2632@ ThreadSafe
2733public class LDClient implements Closeable {
2834 private static final Logger logger = LoggerFactory .getLogger (LDClient .class );
35+ private static final String HMAC_ALGORITHM = "HmacSHA256" ;
36+ protected static final String CLIENT_VERSION = getClientVersion ();
37+
2938 private final LDConfig config ;
39+ private final String apiKey ;
3040 private final FeatureRequestor requestor ;
3141 private final EventProcessor eventProcessor ;
3242 private UpdateProcessor updateProcessor ;
33- protected static final String CLIENT_VERSION = getClientVersion ();
3443
3544 /**
3645 * Creates a new client instance that connects to LaunchDarkly with the default configuration. In most
@@ -51,6 +60,7 @@ public LDClient(String apiKey) {
5160 */
5261 public LDClient (String apiKey , LDConfig config ) {
5362 this .config = config ;
63+ this .apiKey = apiKey ;
5464 this .requestor = createFeatureRequestor (apiKey , config );
5565 this .eventProcessor = createEventProcessor (apiKey , config );
5666
@@ -368,6 +378,25 @@ public boolean isOffline() {
368378 return config .offline ;
369379 }
370380
381+ /**
382+ * For more info: <a href=https://github.com/launchdarkly/js-client#secure-mode>https://github.com/launchdarkly/js-client#secure-mode</a>
383+ * @param user The User to be hashed along with the api key
384+ * @return the hash, or null if the hash could not be calculated.
385+ */
386+ public String secureModeHash (LDUser user ) {
387+ if (user == null || user .getKeyAsString ().isEmpty ()) {
388+ return null ;
389+ }
390+ try {
391+ Mac mac = Mac .getInstance (HMAC_ALGORITHM );
392+ mac .init (new SecretKeySpec (apiKey .getBytes (), HMAC_ALGORITHM ));
393+ return Hex .encodeHexString (mac .doFinal (user .getKeyAsString ().getBytes ("UTF8" )));
394+ } catch (InvalidKeyException | UnsupportedEncodingException | NoSuchAlgorithmException e ) {
395+ logger .error ("Could not generate secure mode hash" , e );
396+ }
397+ return null ;
398+ }
399+
371400 private static String getClientVersion () {
372401 Class clazz = LDConfig .class ;
373402 String className = clazz .getSimpleName () + ".class" ;
0 commit comments