linkIDMask - Encryption and Obfuscation of IDs

IDMask Logo

IDMask is a Java library for masking internal IDs (e.g. from your DB) when they need to be publicly published to hide their actual value and to prevent forging. This should make it very hard for an attacker to understand provided IDs (e.g. by witnessing a sequence, deducting how many orders you had, etc.) and prevent guessing of possible valid ones. Masking is fully reversible and also supports optional randomization for e.g. shareable links or one-time tokens. It has a wide support for various Java types including long, UUID and BigInteger. This library bases its security on strong cryptographic primitives (AES, HMAC, HKDF) to create a secure encryption schema. It was inspired by HashIds but tries to tackle most of it's shortcomings.

Download Build Status Javadocs Coverage Status Maintainability

linkFeature Overview

The code is compiled with target Java 7 to keep backwards compatibility with Android and older Java applications.

linkQuickstart

Add the dependency to your pom.xml (check latest release):

1<dependency>

2 <groupId>at.favre.lib</groupId>

3 <artifactId>id-mask</artifactId>

4 <version>{latest-version}</version>

5</dependency>

A very simple example using 64 bit integers (long):

1byte[] key = Bytes.random(16).array();

2long id = ...

3

4IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());

5

6String maskedId = idMask.mask(id);

7//example: NPSBolhMyabUBdTyanrbqT8

8long originalId = idMask.unmask(maskedId);

alternatively using UUIDs:

1UUID id = UUID.fromString("eb1c6999-5fc1-4d5f-b98a-792949c38c45");

2

3IdMask<UUID> idMask = IdMasks.forUuids(Config.builder(key).build());

4

5String maskedId = idMask.mask(id);

6//example: rK0wpnG1lwvG0xiZn5swxOYmAvxhA4A7yg

7UUID originalId = idMask.unmask(maskedId);

Examples for other java types (e.g. BigInteger, byte[] and LongTuple), see below.

linkHow-To

The following section explains in detail how to use and configure IDMask:

linkStep 1: Create a Secret Key

IDMask's security relies on the strength of the used cryptographic key. In it's rawest from, a secret key is basically just a random byte array. A provided key should be at least 16 bytes long (longer usually doesn't translate to better security). IDMask requires it to be between 12 and 64. There are multiple ways to manage secret keys, if your project already has a managed KeyStore or similar, use it. Otherwise you could just hardcode the key in your code. This, of course, only makes sense where the client doesn't have access to the source or binary (i.e. in a backend scenario). Here are some suggestion on how to create your secret key:

linkOption A: Use Random Number Generator CLI

One of the easiest ways to create a high quality key is to use this java cli: Dice. Just download the .jar (or .exe) and run:

1java -jar dice.jar 16 -e "java"

This will generate multiple 16 byte long syntactically correct java byte arrays:

1new byte[]{(byte) 0xE4, (byte) 0x8A, ...};

You could just hard code this value:

1private static final byte[] ID_MASK_KEY = new byte[]{(byte) 0xE4, (byte) 0x8A, ...};

linkOption B: Generate a Random Key within a Java Runtime

Either in the debugger, simple application or any other REPL execute the following code (IDMask must be in classpath):

1Bytes.random(16).encodeHex();

which will create a random byte array using SecureRandom and encodes it as hex string. In your code use this hex parser and the previously generated string:

1private static final byte[] ID_MASK_KEY = Bytes.parseHex("e48a....").array();

Either way, don't worry too much as the library supports changing the secret key while still supporting unmasking of older IDs.

linkStep 2: Choosing the correct Type

IDMask basically supports 2 data types:

Data types with these byte lengths can be represented as various Java types often used as identifiers:

linkOption A: 64-bit integers (long)

The most common case and the only one fitting in the '8 byte' category is an id with the type long. In Java a long is a signed integer and can represent -2^63 to 2^63 -1. IDMask can mask any valid long value. Internally it will be represented as 8 byte, two's complement.

Create a new instance by calling:

1IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());

2String masked = idMask.mask(-588461182736122L);

It is of course possible to also pass int types by casting them:

1String masked = idMask.mask((long) 1780);

Note that the generated masked id will not be shorter when using 32-bit integers.

linkOption B: Universally unique identifier (UUIDs)

A UUID is a 128 bit long identifier (with 122 bit entropy) and often used in databases because one does not have to worry about sequences or duplicates, but can just generate unique IDs by choosing one randomly. Java has first level support for UUIDs for generation, parsing and serialization.

Create a new instance by calling:

1IdMask<UUID> idMask = IdMasks.forUuids(Config.builder(key).build());

2String masked = idMask.mask(UUID.fromString("eb1c6999-5fc1-4d5f-b98a-792949c38c45"));

linkOption C: Arbitrary-Precision Integers (BigInteger)

If your IDs are typed as BigInteger you can either convert them to long (if they are bound to 64 bit) or use the specific IDMask implementation. Note that the big integer will be converted to a two's complement byte representation and are supported up to 15 byte length (i.e. up to 2^120).

1IdMask<BigInteger> idMask = IdMasks.forBigInteger(Config.builder(key).build());

2String masked = idMask.mask(BigInteger.ONE);

linkOption D: Tuple of 64-bit integers

Sometimes it makes sense to encode multiple IDs to a single serialized version. Use this if you want to combine two long IDs, making it even harder to understand individual ones within.

1IdMask<LongTuple> idMask = IdMasks.forLongTuples(Config.builder(key).build());

2String masked = idMask.mask(new LongTuple(182736128L, 33516718189976L));

linkOption E: 16 byte (128 bit) byte array

Only for advanced use cases. The most generic way to represent a 128 bit id is as a byte array. Basically you may provide any data as long as it fits in 16 bytes. Note, that this is not a general purpose encryption schema and your data might not be secure!

1IdMask<byte[]> idMask = IdMasks.for128bitNumbers(Config.builder(key).build());

2String masked = idMask.mask(new byte[] {0xE3, ....});

linkOption F: Encoding multiple 32-bit integers

Not supported directly, but still quite easy to implement - if you use only 4 byte (32-bit) integers (int), you can encoded multiple numbers.

Using the long schema you can encode up to two of those:

1int intId1 = 1;

2int intId2 = 2;

3

4IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());

5

6long encodedInts = Bytes.from(intId1, intId2).toLong();

7String masked = idMask.mask(encodedInts);

8long raw = idMask.unmask(masked);

9int[] originalIds = Bytes.from(raw).toIntArray(); // originalIds[0] == intId1; originalIds[1] == intId2

or using four 32-bit integers with the byte[] schema:

1int intId1 = 1;

2int intId2 = 2;

3int intId3 = 3;

4int intId4 = 4;

5

6IdMask<byte[]> idMask = IdMasks.for128bitNumbers(Config.builder(key).build());

7

8byte[] ids = Bytes.from(intId1, intId2, intId3, intId4).array();

9String masked = idMask.mask(ids);

10byte[] raw = idMask.unmask(masked);

11int[] originalIds = Bytes.from(raw).toIntArray(); // originalIds[0] == intId1; originalIds[1] == intId2,...

linkOption G: Strings?

Per design this library lacks the feature to mask string based IDs. This is to discourage using it as general purpose encryption library. In most cases strings are encoded data: e.g. UUIDs string representation, hex, base64, etc. Best practice would be to decode these to a byte array (or UUID if possible) and use any of the other options provided above. (Note: technically it would be possible to convert the string to e.g. ASCII bytes and just feed it IdMask<byte[]> if it's length is equal or under 16; but this is highly discouraged).

linkStep 3: IDMask Configuration

Usually the default settings are fine for most use cases, however it may make sense to adapt to different usage scenarios with the following settings.

linkQ1: Should Ids be deterministic or random?

By default off, the masking algorithm supports randomization of generated IDs. This is achieved by creating a random number and using it as part of the encrypt scheme as well as appending it to the output of the masked id. Therefore randomized IDs are longer than their deterministic counter part. Randomization increases the obfuscation effectiveness but makes it impossible for a client to check equality. This usually makes sense with shareable links, random access tokens, or other one-time identifiers. Randomized IDs within models are probably a bad idea.

For instance these masked IDs all represent the same original id 70366123987523049:

1SUkHScdj3j9sE3B3K8KGzgc

2Hx8KpcbNQb7MAAnKPW-H3D4

3wsKW652TCEDBjim8JfOmLbg

Enable with:

1Config.builder(key)

2 .randomizedIds(true)

3 ...

linkQ2: What encoding should I choose?

The library internally converts everything to bytes, encrypts it and then requires an encoding schema to make the output printable. Per default the url-safe version of Base64 (RFC4648) is used. This is a well supported, fast and reasonable space efficient (needs ~25% more storage than the raw bytes) encoding. Note that the output size is constant using the same settings a type and does not grow or shrink depending on e.g. how big the number is.

However depending on your use case, you may want Ids that are easy to type, do not contain possible problematic words or require some maximum length. The library includes some built-in encodings which satisfy different requirements:

Encodingmay contain wordseasy to typeurl safeLength for 64 bit id (deterministic/randomized)Length for 128 bit id (deterministic/randomized)Example
Hexnoyesyes34/5050/82e5e53e09bbd37f8d8b9afdfbed776de6fe
Base32yesyesyes28/4040/66XS6GLNDNQ2NSBWJRMWM3U72FTLLA
Base32 (Safe Alphabet)no curse wordscontains upper and lowercaseyes28/4040/66jKVJx8yQPP8wkBNQhZBkJr6vKhwH
Base64yesnoyes23/3434/55SkqktDj1MVEkiPMrwg1blfA

If IDs should be as short as possible, you may look into using Ascii85/Base85 with a Java implementation here; expect around 8% better space efficiency compared to Base64.

Choose a different encoding by setting the following in the config builder:

1Config.builder(key)

2 .encoding(new ByteToTextEncoding.Base32Rfc4648())

3 ...

Implement your own encoding by using the ByteToTextEncoding interface.

linkFormatted IDs

For IDs that are better readable for humans you can use the ByteToTextEncoding.Formatter and use it to wrap any other encoding instance with:

1ByteToTextEncoding.Formatter.wrap(myEncoding);

For example with Base32 this could look like this

1SE4RT-7LNHU7X-X3TMJ-OJYNMDS-ETVQ

linkQ3: Do you need Caching?

By default a simple in-memory lru cache is enabled. This cache improves performance if recurring IDs are encoded/decoded - if this is not the case the cache should be disabled to safe memory.

This setting is responsible for disabling the cache:

1Config.builder(key)

2 .enableCache(true)

3 ...

if you want to wire your own cache framework to the id mask library you may do so by implementing the Cache interface and setting:

1Config.builder(key)

2 .cacheImpl(new MyHazelcastCache())

3 ...

linkQ4: Any other Advanced Security Features required?

You may provide your own JCA provider (like BouncyCastle) or your own cryptographically secure pseudo-random number generator (i.e. a SecureRandom implementation). The provider is used to encrypt/decrypt with AES and to calculate HMACs

Example:

1Config.builder(key)

2 .secureRandom(new SecureRandom())

3 .securityProvider(Security.getProvider("BC"))

4 ...

linkHigh Security Mode

Only applicable with 16 byte IDs (e.g. UUID, byte[], BigInteger, ...) it is optionally possible to increase the security strength of the masked id in expense for increased id lengths. By default a 8-byte MAC is appended to the ID and, if randomization is enabled, a 8-byte random nonce is prepended. In high security mode these numbers double to 16 byte, therefore high security IDs are 16 bytes longer. If you generate a massive amount of IDs (more than 2^32) or don't mind the longer output length, high security mode is recommended.

Issue with smaller MAC is increased chance of not recognizing a forgery and issue with smaller randomization nonce is higher chance of finding duplicated randomization values and recognizing equal IDs (chance of duplicate after 5,000,000,000 randomized IDs with 8 byte nonce is 50%). Increasing these numbers to 16 bytes make both those issue negligible.

linkA Full Example

Here is a fully wired example using the generic byte array IDMask:

1IdMask<byte[]> idMask = IdMasks.for128bitNumbers(

2 Config.builder(KeyManager.Factory.with(key))

3 .randomizedIds(true) //non-deterministic output

4 .enableCache(true)

5 .cacheImpl(new Cache.SimpleLruMemCache(64))

6 .encoding(new ByteToTextEncoding.Base32Rfc4648())

7 .secureRandom(new SecureRandom())

8 .securityProvider(Security.getProvider("BC"))

9 .build());

10

11String maskedId = idMask.mask(id128bit);

12//example: RAKESQ32V37ORV5JX7FAWCFVV2PITRN5KMOKYBJBLNS42WCEA3FI2FIBXLKJGMTSZY

13byte[] originalId = idMask.unmask(maskedId);

linkAdditional Features

linkUpgrade of used Secret Key

If you want to change the secret key, because e.g. it became compromised, but still want to be able to unmask IDs created with the previous key, you can use the built-in key migration scheme. Since every created id encodes the id of the used key, it is possible to choose a different key decryption.

Here is a full example: First a new instance with key1 will be created. If no key-id is passed, the library uses KeyManager.Factory.DEFAULT_KEY_ID.

1long id = 123456789L;

2byte[] key1 = Bytes.random(16).array();

3

4// create new instance with your key

5IdMask<Long> idMask1 = IdMasks.forLongIds(Config.builder(key1).build());

6String maskKey1 = idMask1.mask(id);

7// e.g.: kpKOdqrNdmyx34-VxjTg6B4

If you want to switch the active key, just generate a new one and also set the old one as legacy key. Mind to choose a different key-id:

1// if key1 is compromised create a new key

2byte[] key2 = Bytes.random(16).array();

3

4// set the new key as active key and add the old key as legacy key - us the DEFAULT_KEY_ID, is it is used if no key id is set

5IdMask<Long> idMask2 = IdMasks.forLongIds(

6 Config.builder(KeyManager.Factory.withKeyAndLegacyKeys(

7 new KeyManager.IdSecretKey(KeyManager.Factory.DEFAULT_KEY_ID+1, key2), //new key with a new key id

8 new KeyManager.IdSecretKey(KeyManager.Factory.DEFAULT_KEY_ID, key1))) //old key with the DEFAULT_KEY_ID

9 .build());

Masking the same id, with the new key will generate different output:

1// same id will create different output

2String maskKey2 = idMask2.mask(id);

3// e.g.: 3c1UMVvVK5SvNiOaT4btpiQ

Unmasking however will reveal the same underlying id, no matter if it was masked with key1 or key2.

1// the new instance can unmask the old an new key

2assert idMask2.unmask(maskKey1).equals(idMask2.unmask(maskKey2));

Be aware that changing the secret key, will destroy equality of masked IDs cached with clients or elsewhere.

linkError Handling

An IdMask instance will basically throws 2 types of unchecked exceptions:

1) IllegalArgumentException 2) IdMaskSecurityException (extends SecurityException)

The first will be thrown on basic parameter validation errors which usually stems from incorrect use of the library (e.g. masked id too long or short, null reference, etc.). The second are errors thrown from the id masking decryption logic which may be security relevant. It would make sense to at least catch and log them. The IdMaskSecurityException.getReason() can be used to group detailed causes.

linkUsing in your Application

Various default implementation for value converter exist in the ext.* package. All dependencies for these converters are set to provided which means the caller needs to provide their own. This will prevent unnecessary dependencies for project that use different frameworks.

linkJackson JSON Serializer/Deserializer

The object serialization framework Jackson (most often used with JSON serialization) has a serializer/deserializer feature. A way to use it, is to extend any of the pre-implemented serializers found in IdMaskJackson and provide it with a IdMask instance:

1public final class MyIdMaskLongSerializers {

2 private MyIdMaskLongSerializers() {

3 }

4

5 @Inject

6 private IdMask<Long> idMask;

7

8 public static final class Serializer extends IdMaskJackson.LongSerializer {

9 public Serializer() {

10 super(idMask);

11 }

12 }

13

14 public static final class Deserializer extends IdMaskJackson.LongDeserializer {

15 public Deserializer() {

16 super(idMask);

17 }

18 }

19}

and then add the annotations to the id you want to mask:

1public class LongIdUser {

2 @JsonSerialize(using = MyIdMaskLongSerializers.Serializer.class)

3 @JsonDeserialize(using = MyIdMaskLongSerializers.Deserializer.class)

4 private final long id;

5...

6}

linkJAX-RS 2 Parameter Converter

The Java API for RESTful Web Services , JAX-RS 2 provides the possibility to define so called ParamConverter<T> which can be used to transparently convert between java types and string representations for query, path, header, cookie and header parameter.

The IdMaskParamConverters class contains various default implementations for the most used types. To make it work, you have to define a ParamConverterProvider and return the appropriate converter like so:

1@Provider

2public static class MyParamConverterProvider implements ParamConverterProvider {

3

4 @Inject

5 private IdMask<Long> idMask;

6

7 @Override

8 public <T> ParamConverter<T> getConverter(Class<T> aClass, Type type, Annotation[] annotations) {

9

10 if (aClass.equals(MaskedLongId.class)) {

11 return (ParamConverter<T>) new IdMaskMaskedLongIdParamConverter(idMask);

12 } else if (aClass.equals(Long.class)) {

13 return (ParamConverter<T>) new IdMaskLongIdParamConverter(idMask);

14 }

15 ...

16 }

17}

Note that maybe you don't want to convert ALL long type values, so there is a simple wrapper class MaskedLongId which can be used for easier type mapping instead of just Long.

linkDownload

The artifacts are deployed to jcenter and Maven Central.

linkMaven

Add dependency to your pom.xml:

1<dependency>

2 <groupId>at.favre.lib</groupId>

3 <artifactId>id-mask</artifactId>

4 <version>{latest-version}</version>

5</dependency>

linkGradle

Add to your build.gradle module dependencies:

1implementation group: 'at.favre.lib', name: 'id-mask', version: '{latest-version}'

linkLocal Jar

Grab jar from latest release.

linkDescription

linkWhy?

IDMask can be used in an environment, where you want to protect the knowledge of the value of your IDs. Usually a very easy workaround would be to add another column in your database and randomly create UUIDs and use this instead of your e.g. numeric IDs. However sometimes this is not feasible (e.g. having millions of rows) or you cannot change the DB schema. Additionally IDMask can make IDs appear random, a feature which cannot be satisfied with the above approach.

linkWhen to use IDMask

linkWhen not to use IDMask

linkPerformance

IDMask requires a non-trivial amount of work to encrypt IDs. The 8-byte-schema only needs to encrypt a single AES block (which should be hardware accelerated with most CPUs). The 16-byte schema is more expensive, since it requires encryption of an AES block, one HKDF expand and a HMAC calculation. According to the JMH benchmark, you can expect multiple hundreds encryption/decryption per ms. Compared to the performance HashIds, which is faster by a factor of about 1000, IDMask seems extremely slow, but in the grant scheme of things it probably doesn't make a difference if masking of a single id costs 2µs or 0.002µs - there will be no performance bottleneck either way.

linkJMH Benchmark

Here is an benchmark done on a i7-7700k:

1Benchmark Mode Cnt Score Error Units

2IdMaskAndHashIdsBenchmark.benchmarkHashIdEncode avgt 3 2,372 ± 0,282 ns/op

3IdMaskAndHashIdsBenchmark.benchmarkHashIdEncodeDecode avgt 3 3,396 ± 0,965 ns/op

4IdMaskAndHashIdsBenchmark.benchmarkIdMask16Byte avgt 3 7530,858 ± 761,871 ns/op

5IdMaskAndHashIdsBenchmark.benchmarkIdMask8Byte avgt 3 2863,481 ± 183,189 ns/op

6IdMaskAndHashIdsBenchmark.benchmarkMaskAndUnmask16Byte avgt 3 15054,198 ± 2030,344 ns/op

7IdMaskAndHashIdsBenchmark.benchmarkMaskAndUnmask8Byte avgt 3 4707,021 ± 164,998 ns/op

linkEncryption Schema

Two slightly different encryption schemes are used to optimize for performance and output size for the specific case. For each of these schemes two variation exist for deterministic and randomized encryption. As reference here are some test-vectors.

link8 Byte Encryption Schema

This schema uses the following cryptographic primitives:

Using the a full 16 byte AES block, we create a message containing of the 8 byte id (ie. the plaintext) and an 8 byte reference value. Then we encrypt it with AES/ECB (since we encrypt only a single block, a block mode using an IV like CBC wouldn't make a difference):

1message_d = ( refValue_1a | id )

2maskedId_d = ciphertext_d = AES_ECB( message_d )

When decrypting, we compare the reference value, and if it has changed we discard the id, since either the key is incorrect, or this was a forgery attempt:

1AES_ECB( maskedId_d ) = refValue_1b | id

2refValue_1a == refValue_1b

linkDeterministic

In the deterministic mode the reference value is just a 8 byte long array of zeros.

linkRandomized

In the randomized mode the reference value is a random 8 byte long array. Because the decryption requires knowledge of this value it will be prepended to the cipher text:

1ciphertext_r = AES_ECB( refValue_rnd | id )

2maskedId_r = refValue_rnd | ciphertext_r

linkVersion Byte

Both modes have a version byte prepended which will be xor-ed with the first byte of the cipher text for simple obfuscation:

1obfuscated_version_byte = version_byte ^ ciphertext[0]

Finally the message looks like this:

1maskeId_msg_d = obfuscated_version_byte | maskedId_d

and

1maskeId_msg_r = obfuscated_version_byte | maskedId_r

for randomized encryption.

linkSummary

This schema has the advantage of having forgery protection without the need for a dedicated MAC generation and also keeps the output reasonable small with 16 + 1 byte.

link16 Byte Encryption Schema

This schema uses the following cryptographic primitives:

The basic scheme works as follows:

First create the required keys and nonce:

1okm = hkdf.expand(key, entropy, 64);

2key_s = okm[0-16];

3iv_s = okm[16-32];

4mac_key_s = okm[32-64];

5

6key ......... provided secret key

7entropy ..... 8 byte value. For randomized-ids it is a random value, otherwise zero bytes

Then encrypt the id:

1ciphertext = AES_CBC( iv_s , id ^ entropy)

2mac = HMAC(ciphertext)

3maskedId_msg= ciphertext | mac[0-8]

4

5id .......... id to mask (aka plaintext)

optionally if randomized IDs are enabled, also append entropy to the output:

1maskedId_msg_r = entropy | maskedId_msg

Finally append the version byte (see explanation in 8 byte schema). Use either the randomized or deterministic version:

1maskeId_msg_r = obfuscated_version_byte | maskedId_msg_r

2maskeId_msg_d = obfuscated_version_byte | maskedId_msg

linkIDMask vs HashIds

One of the reasons this library was created, was that the author was not happy how HashIds solved the issue of obfuscating/encryption of IDs. These are the main criticism:

linkWeak, home-brew cryptography

HashIds encryption schema is basically: Have an alphabet. Shuffle it with Yates Algorithm and a salt provided by the user. Use this simple encoding schema to encode 53 bit integers. There is a well known cryptanalysis and to be fair, the author of HashIds does not directly claim any security - BUT a library called HashId which is used to obfuscate IDs for security purpose which does not claim any actual security does not make sense to me.

linkArbitrary limitation inherited rom the original javascript implementation

The integer values in HashId must be positive and have an upper bound of 53-bit (instead of 64-bit). If IDs are created in a sequence in the DB, this limitation is probably irrelevant, but if you work with random 64-bit values, this might break your code. IDMask does not apply any restriction on Java's long; the value may be positive as well as negative in the full range of 2^64. Only BigInteger is restricted to 15 byte (or 2^120).

In summary, here is simple comparison table of the main points:

IDMaskHashId
Supported Typeslong, UUID, BigInteger, byte[], LongTuplelong, long[]
Type Limitationslong: none, BigInteger: max 15 byte, byte[]: max 16 byteonly positive and max 2^53
Randomized IDsoptionalno
Output Lengthfixed lengthvariable length
EncryptionAES + HMACencoding + shuffled alphabet
Performance2-7 µs/op0.003 µs/op
Collision possiblenono
CachingBuilt-Inno
EncodingsHex, Base32, Base64, Custom...customizable alphabet
Forgery ProtectionHMAC (8-16 bytes)no

linkSecurity Relevant Information

linkOWASP Dependency Check

This project uses the OWASP Dependency-Check which is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities against a NIST database. The build will fail if any issue is found.

linkDigital Signatures

linkSigned Jar

The provided JARs in the Github release page are signed with my private key:

1CN=Patrick Favre-Bulle, OU=Private, O=PF Github Open Source, L=Vienna, ST=Vienna, C=AT

2Validity: Thu Sep 07 16:40:57 SGT 2017 to: Fri Feb 10 16:40:57 SGT 2034

3SHA1: 06:DE:F2:C5:F7:BC:0C:11:ED:35:E2:0F:B1:9F:78:99:0F:BE:43:C4

4SHA256: 2B:65:33:B0:1C:0D:2A:69:4E:2D:53:8F:29:D5:6C:D6:87:AF:06:42:1F:1A:EE:B3:3C:E0:6D:0B:65:A1:AA:88

Use the jarsigner tool (found in your $JAVA_HOME/bin folder) folder to verify.

linkSigned Commits

All tags and commits by me are signed with git with my private key:

1GPG key ID: 4FDF85343912A3AB

2Fingerprint: 2FB392FB05158589B767960C4FDF85343912A3AB

linkBuild

linkJar Sign

If you want to jar sign you need to provide a file keystore.jks in the root folder with the correct credentials set in environment variables ( OPENSOURCE_PROJECTS_KS_PW and OPENSOURCE_PROJECTS_KEY_PW); alias is set as pfopensource.

If you want to skip jar signing just change the skip configuration in the pom.xml jar sign plugin to true:

1true

linkBuild with Maven

Use the Maven wrapper to create a jar including all dependencies

1mvnw clean install

linkCheckstyle Config File

This project uses my common-parent which centralized a lot of the plugin versions aswell as providing the checkstyle config rules. Specifically they are maintained in checkstyle-config. Locally the files will be copied after you mvnw install into your target folder and is called target/checkstyle-checker.xml. So if you use a plugin for your IDE, use this file as your local configuration.

linkTech Stack

linkFurther Reading

linkMain Article

linkDiscussions

linkSimilar Libraries

linkCredits

linkLicense

Copyright 2019 Patrick Favre-Bulle

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

1http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Star
IDMask - Encryption and Obfuscation of IDsFeature OverviewQuickstartHow-ToStep 1: Create a Secret KeyOption A: Use Random Number Generator CLIOption B: Generate a Random Key within a Java RuntimeStep 2: Choosing the correct TypeOption A: 64-bit integers (long)Option B: Universally unique identifier (UUIDs)Option C: Arbitrary-Precision Integers (BigInteger)Option D: Tuple of 64-bit integersOption E: 16 byte (128 bit) byte arrayOption F: Encoding multiple 32-bit integersOption G: Strings?Step 3: IDMask ConfigurationQ1: Should Ids be deterministic or random?Q2: What encoding should I choose?Formatted IDsQ3: Do you need Caching?Q4: Any other Advanced Security Features required?High Security ModeA Full ExampleAdditional FeaturesUpgrade of used Secret KeyError HandlingUsing in your ApplicationJackson JSON Serializer/DeserializerJAX-RS 2 Parameter ConverterDownloadMavenGradleLocal JarDescriptionWhy?When to use IDMaskWhen not to use IDMaskPerformanceJMH BenchmarkEncryption Schema8 Byte Encryption SchemaDeterministicRandomizedVersion ByteSummary16 Byte Encryption SchemaIDMask vs HashIdsWeak, home-brew cryptographyArbitrary limitation inherited rom the original javascript implementationSecurity Relevant InformationOWASP Dependency CheckDigital SignaturesSigned JarSigned CommitsBuildJar SignBuild with MavenCheckstyle Config FileTech StackFurther ReadingMain ArticleDiscussionsSimilar LibrariesCreditsLicense

Home

OpenSourcechevron_right



Patrick Favre-Bulle 2020