Generating QR Codes With Secure Hashes Using Java

Intro

I have been testing out new functionality for “checking-in” to a location using QR codes. To make sure the user is at the specified location and is scanning my QR code (and not a “fake” code created by someone else), I needed to add a way of “signing” each code with a value that only I – the provider of the QR code – could know. This would also make it simple enough to be able to implement the same mechanism in the app used to scan the codes to verify the validity on the client side.

I ended up with a solution where I would have a QR code containing a JSON object with a Name and a Key – a hashed and salted name string. The string will be read by the client app used to scan the code and hashed using the same algorithm with the same secret salt value, and compared to the value in the QR code on the client side.

The data structure inside a generated QR code would be like this:

{"Name","MyString","Key","HashedMyStringWithSecretSalt"}

When it comes to the implementation, I decided to do the generation or codes in Java and, later, implement this as a standalone microservice. Here, I must admit that I was surprised by how simple it was using a specialized library. More about that below.

So, let’s have a look at how this can be implemented in your solution – step by step.

Generating QR Codes

First, I needed a library that can handle QR codes, and I decided to use Zebra Crossing (“ZXing”) library because of its simplicity and popularity (i.e. community around it).

All you need to get started is to add the following dependencies to your pom.xml (assuming you are using Maven to build your project):

This library provides quite an extensive functionality both for generating and reading codes. This was more than enough for my use case where I just needed to generate a QR code with a simple JSON object:

publicbyte[]qrCodeGenerator(Stringid)throwsIOException,WriterException,InvalidKeySpecException,NoSuchAlgorithmException{StringfilePath="QRCode.png";Stringcharset="UTF-8";MaphintMap=newHashMap();hintMap.put(EncodeHintType.ERROR_CORRECTION,ErrorCorrectionLevel.L);Map<String,String>qrCodeDataMap=Map.of("Name",id,"Key",keyProvider.generateVerificationKey(id)// see next section for ´generateVerificationKey´ method);StringjsonString=newJSONObject(qrCodeDataMap).toString();createQRCode(jsonString,filePath,charset,hintMap,500,500);BufferedImageimage=ImageIO.read(newFile(filePath));ByteArrayOutputStreambaos=newByteArrayOutputStream();ImageIO.write(image,"png",baos);byte[]imageData=baos.toByteArray();returnimageData;}privatevoidcreateQRCode(StringqrCodeData,StringfilePath,Stringcharset,MaphintMap,intqrCodeHeight,intqrCodeWidth)throwsWriterException,IOException{BitMatrixmatrix=newMultiFormatWriter().encode(newString(qrCodeData.getBytes(charset),charset),BarcodeFormat.QR_CODE,qrCodeWidth,qrCodeHeight,hintMap);MatrixToImageWriter.writeToPath(matrix,filePath.substring(filePath.lastIndexOf('.')+1),FileSystems.getDefault().getPath(filePath));}

Note also fun little thing – the conversion of Java hashmaps to a JSON object using JSONObject. Sometimes it is much easier to build up data structure the way you want it, and then serialize to JSON:

If you are looking for a more simplified interface, you might also check out QRGen that claims to simplify QR code generation API for Java even further and is built on top ZXing. However, ZXing was absolutely fine in my case.

Hashing Strings

Now, I needed to be able to hash a string in a quick and secure manner. For this, I decided to use the method suggested by OWASP for Java. To implement this method you will need to start with updating your pom.xml:

What Now?

By now you should be able to generate QR codes with a hashed string. In the next post, I will be sharing code on how to embed and generate PDF files with this information with Java, followed by a post where it all will be put together into a MicroProfile microservice. Stay tuned!