Java Delays Caused by Random Number Generation
If you are using Java Encryption on Linux, very hard to detect weird performance issues might happen. This article is about to clear that out.
Random Number Generation (RNG) plays the key role in encryption in general. Whether it is used for key generation, initialization vector or for generating general random values etc. Quality of RNG values is based on entropy preventing to guess next random data. In computer systems it is actually quite hard to achieve quality entropy just by writing some code to generate RNG so many OS systems comes with implemented entropy generators based on various hardware inputs from oscillators, to mouse movements and keyboard inputs.
On Linux, such random data goes into /dev/random (blocking) and /dev/urandom (unblocking). The difference is that /dev/random is filled very slowly, and it blocks. That means, when reading bytes from /dev/random, the response might take up to several minutes to return which is bad if the server use /dev/random to encrypt HTTP response data to the web client.
Simply try the following
head -n 1000 /dev/urandom
...and
head -n 1000 /dev/random
First one will return immediately, while the second will take a lot of time to respond if one of the RNG daemons are not already installed (explained below).
To check how much random data is available inside /dev/random, we can use this...
cat /proc/sys/kernel/random/entropy_avail
The result might surprise you. Only few bytes will be available. And after repeated calls, one might notice how slowly this number grows. As /dev/random is blocking, the response will return only when entropy is big enough - which might take a lot of time.
One of the solutions for that is to install haveged (based on Havege) or rng-tools. Those are Linux services helping to solve the /dev/random slowness issues by populating /dev/random reasonably fast. This issue is non-existent on some Linux versions, as one of mentioned services are installed automatically upon OS install.
Simple check is to verify entropy_avail size value and rng-tools or haveged service is installed on the system. If not, installation is simple as shown below.
sudo apt install rng-tools
or
sudo yum install rng-tools
sudo systemctl start rngd
apt-get install haveged
or
sudo yum install -y haveged
sudo systemctl start haveged
After service is installed, check back entropy status with command below. Entropy values should be much higher.
cat /proc/sys/kernel/random/entropy_avail
Note about Docker
Haveged or rng-tools services might not work when Linux runs in containers such as Docker. The solution is to map /dev/random from the host system like in this example.
docker run -v /dev/random:/dev/random
or
docker run -v /dev/urandom:/dev/random
Note about security differences
Difference between /dev/urandom and /dev/random in entropy quality exists, still for general use /dev/urandom is good enough to be used, especially in server software when high request/response throughput is required.
Why do we write about Linux Java RNG generation in the first place?
Green Screen Terminal Services for IBM i is written in Java, so if web terminal or web admin UI does not work, besides network and firewall setup, encryption also might be the reason.
In Java, SecureRandom class is used to generate random bytes required for encryption. Various techniques exist how SecureRandom gets the random data. However, SecureRandom will most probably read values from /dev/random or /dev/urandom when Java runs on Linux.
From where SecureRandom will read the data depends on several rules. If you do not have access to the Java source, use one of mentioned rng-tools, haveged or Rule 1 or Rule 2 based on your requirements. When you have Java source and can adjust code use Rule 3, or Rule 4.
Rule 1.
Java JVM java.security file contains variable securerandom.source indicating SecureRandom class from where to read the data. The default value is /dev/random.
This can be changed to /dev/urandom, (actually to /dev/./urandom due to Java bug).
Rule 2.
System property java.security.egd which will override the securerandom.source setting. Simply call your Java program with -Djava.security.egd=file:/dev/./urandom
Rule 3.
In Java code if SecureRandom.getInstanceStrong() is used, most likely /dev/random will be used except changed with Rule 1 or Rule 2.
Instead, use SecureRandom secureRandom = new SecureRandom(). However, this is undetermined and still might use /dev/random based on Rule 1 or Rule 2 settings.
Another way to force Java to use /dev/urandom is to use SecureRandom.getInstance("NativePRNGNonBlocking")
The following is a list of PRNGs for the SUN provider:
- SHA1PRNG (Initial seeding is currently done via a combination of system attributes and the java.security entropy gathering device)
- NativePRNG (nextBytes() uses /dev/urandom, generateSeed() uses /dev/random)
- NativePRNGBlocking (nextBytes() and generateSeed() use /dev/random)
- NativePRNGNonBlocking (nextBytes() and generateSeed() use /dev/urandom)
- NativePRNGBlocking and NativePRNGNonBlocking are available in JRE 8+.
Rule 4.
Use Bouncycastle library and its own SecureRandom implementation.
For generating Security Keys RSA, ECDSA etc.
SecureRandom.getInstance("DEFAULT", BouncyCastleProvider.PROVIDER_NAME);
For IV and Nonce
SecureRandom.getInstance("NonceAndIV", BouncyCastleProvider.PROVIDER_NAME);
Conclusion
As one can see, it is more complicated on Linux. The best solution is to simply install one of rng-tools or haveged and sleep peacefully that Java application on Unix/Linux will not block if SecureRandom is used.