Creating secure encrypted URL’s in your application is a very common need in web development, especially when you need to integrate with 3rd parties.
A good example that I personally worked on was a points rewards site which allowed 3rd parties to embed encrypted links in their sites and emails. When their users would arrive to our site through these links, they would be rewarded in some way. These type of encrypted REST API’s are very useful for creating user/award specific one-time links.
Dependencies
We will rely on two great libraries: Apache Shiro and BouncyCastle. BouncyCastle provides some more secure encryption modes not available in Shiro. So to get started we will add these two dependencies to our project:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.64</version>
</dependency>
Encryption Algorithms
I’m by no means an expert in encryption and will include some useful links below, but here is a short description of the two methods provided. Of course you can add others depending on your specific needs and those of your partners/clients. We ran into difficulties because some 3rd parties just don’t have the technical resources to implement some types of decryption.
-
- AES-CBC mode with authentication(AE)
- Uses PKCS5 padding and a 128-bit secret key.
- Authenticated encryption means that it’s not possible to take an encrypted string, modify it, and get another valid encrypted string.
- This is set as the default method because you might find not everyone is able to implement the below mode.
- AES-GCM mode
- Similar to CBC with no padding. Has built-in authentication.
- AES-CBC mode with authentication(AE)
Useful links
- https://blog.cryptographyengineering.com/2012/05/19/how-to-choose-authenticated-encryption/
- http://meri-stuff.blogspot.com/2012/04/secure-encryption-in-java.html
- https://github.com/defuse/php-encryption
CipherService
We can now write a generic CipherService class which will contain encryption/decryption methods. These methods will accept a string, an optional secret key, and the encryption algorithm you wish to use. These methods will return Base-64 encoded strings that are suitable to include in a URL. You can find the full code here.
public class EncryptionService {
String encryptAndBase64Encode(String secretString);
String encryptAndBase64Encode(String secretString, EncryptionType type);
String encryptAndBase64Encode(String secretString, byte[] secretKey, EncryptionType type);
String base64DecodeAndDecrypt(String base64EncodedEncryptedString) throws Exception;
String base64DecodeAndDecrypt(String base64EncodedEncryptedString, EncryptionType type) throws Exception;
String base64DecodeAndDecrypt(String base64EncodedEncryptedString, byte[] secretKey, EncryptionType type) throws Exception;
}
NOTE: The decision about how and where to store your secret key is important! At the very least, don’t accidentally include it into your source code repository! I’m curious to hear some simple but secure ways you would store it.
Generating links in Wicket
Now that we have the supporting services in place, we can create a simple helper class that will create encrypted Wicket links and decrypt them:
@Service
public class Encryptor {
private @Autowired CipherService cipherService;
public <T extends WebPage> String createEncryptedUrl(Class<T> pageClass, PageParameters params) {
String secretStr = Utils.mapToString(params);
String encryptedStr = cipherService.encryptAndBase64Encode(secretStr);
Url url = RequestCycle.get().mapUrlFor(pageClass, new PageParameters().add("enc", encryptedStr));
return RequestCycle.get().getUrlRenderer().renderFullUrl(url);
}
public PageParameters decryptUrl(String encryptedStr) throws Exception {
String decryptedStr = cipherService.base64DecodeAndDecrypt(encryptedStr);
return Utils.stringToParams(decryptedStr);
}
}
You can use the createEncryptedUrl() method which will create a fully qualified URL with the HTTP GET parameters aggregated and encrypted into a single ‘enc’ parameter.
You can than use the decryptUrl() method which will take an encrypted string and return an instance of PageParameters.
Conclusion
This by no means is meant as an exhaustive solution to all of your encryption needs, but it’s a great start! The full source code, including test pages that allow you to play around with the classes, is available on GitHub.