If you need to store sensitive data in your system, you have to be sure that you have proper encryption in place. First of all you need to decide what kind of encryption you need βfor instance, symmetric or asymmetric. Also, you need to choose how secure it needs to be. Stronger encryption takes more time and consumes more CPU. The most important part is that you donβt need to implement the encryption algorithms yourself. Encryption is hard and a trusted library solves encryption for you.
If, for instance, we want to encrypt something like credit card details, we probably need a symmetric algorithm, because we need to be able to retrieve the original number. Say we use the Advanced Encryption Standard (AES), which is currently the standard symmetric encryption algorithm for US federal organizations. To encrypt and decrypt, there is no reason to deep-dive into into low level Java crypto. We recommend that you use a library that does the heavy lifting for you. For example, Google Tink.
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>1.3.0-rc3</version>
</dependency>
Below, thereβs a short example of how to use Authenticated Encryption with Associated Data (AEAD) with AES. This allows us to encrypt plaintext and provide associated data that should be authenticated but not encrypted.
private void run() throws GeneralSecurityException {
AeadConfig.register();
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM);
String plaintext = "I want to break free!";
String aad = "Queen";
Aead aead = keysetHandle.getPrimitive(Aead.class);
byte[] ciphertext = aead.encrypt(plaintext.getBytes(), aad.getBytes());
String encr = Base64.getEncoder().encodeToString(ciphertext);
System.out.println(encr);
byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encr), aad.getBytes());
String decr = new String(decrypted);
System.out.println(decr);
}
Password encryption
For passwords, it is safer to use one-way encryptions as we donβt need to retrieve the original passwords but just match the hashes. BCrypt and, his succor, SCrypt are the most suitable for this job. Both are cryptographic hashes (one-way functions) and computationally difficult algorithms that consume a lot of time. This is exactly what you want, because brute force attacks take ages this way.
Spring security provides excellent support for a wide variety of algorithms. Try using the SCryptPasswordEncoder
and BCryptPasswordEncoder
that Spring Security tool 5 provides for the purpose of password hashing
What is a strong encryption algorithm today, may be a weak algorithm a year from now. Therefore, encryption needs to be reviewed regularly to make sure you use the right algorithm for the job. Use vetted security libraries for these tasks and keep your libraries up to date.
This was just 1 of 10 Java security best practices. Take a look at the full 10 and the easy printable one-pager available
Top comments (3)
Please mind the terminology. You wrote about password encryption, while actually talking about hashing.
There is a big difference, especially concerning secure password storage.
Using encryption you could always reverse the process and retrieve the clear text password, while a hashed password can't be reversed, only validated against the actual password.
"For passwords, it is safer to use asymmetric encryptions as we donβt need to retrieve the original passwords..."
Well asymmetric encryption let's you retrieve the password just like symmetric encryption, you only need more keys.
It has nothing to do with hashing.
Thanks for pointing this out. By mistake I used the term asymmetric instead of one-way encryption.
Will change it right away.
Two things concerning password encryption:
Always make the effort parameters a configuration setting. You want to change those values in the future. For BCrypt it is just a weight parameter, which at this point should be something like 11 or 12. The weight is exponential. You might want to benchmark it. Something between 0.25 and 0.50 seconds is a nice performance without annoying legitimate users too much. For SCrypt there are two parameters, CPU and memory cost, which would be 214 and 8 respectively.
BCrypt and SCrypt are vastly different algorithms with vastly different characteristics. BCrypt is battle-tested, it has been around a long time, and still holds up. It is also properly understood by experts. SCrypt is much newer, theoretically it should be better, when configured correctly. As it is much newer, it has not received the same amount of scrutiny by experts. Additionally, performance of SCrypt in Java is quite bad compared to what can be achieved when using special CPU instructions.
It might be best to use BCrypt in Java. Spring's JavaDoc for Scrypt also refers to this interesting article about BCrypt vs. SCrypt.
Or maybe make use of
DelegatingPasswordEncoder
and even make that part of your system configurable.