Thursday, December 1, 2011

Generating Secure Numbers or Random Numbers like PIN/OTP

Dear Reader,

I am writing a useful post here: How to generate Secure Number sequences or Random Number sequences like PIN/One time
passwords or any number sequence for Temporary passwords which are numeric. I have explained the complete tutorial by
giving first a practical example that we use in our coding practices. The complete theory is given at the below section
after coding example.

At first, I have written code to generate simple Random Number based on given length, then the secure one:


//Basic Random Generator
package com.dmodi.utils;
import org.apache.commons.lang.RandomStringUtils;
//Add commons-lang-2.4.jar
public class RandomNumberGenerator {
    public static void main(String[] args) {
        for(int i=0;i<10;i++){
            System.out.println(generateRandomNumber(6));
            System.out.println(generateRandomNumber(12));
        }

    }
    public static String generateRandomNumber(int length){
        String randomNumber="";
        randomNumber = RandomStringUtils.randomNumeric(length);
        return randomNumber;
    }
}

===========================================

//Secure Random Generator
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
//Add commons-lang-2.4.jar
import org.apache.commons.lang.RandomStringUtils;

public class OTP_Generation {
    public static void main(String[] args) {
        int otpLength=4;
        for(int i=0; i<10;i++) {
            System.out.println("OTP using SecureRandomGenerator: "+(i+1)+"==>"+generateOTP(otpLength));
        }
        System.out.println("==========================");
        for(int i=0; i<10;i++) {
            System.out.println("OTP using RandomNumberGenerator: "+(i+1)+"==>"+generateRandomOTP(otpLength));
        }
    }

    public static String generateOTP(int otpLengthNumber){
        String otp = new String();
        int otpSample=0;
        for(int i=0;i<otpLengthNumber;i++){
            otp=otp+"9";
        }

        otpSample=Integer.parseInt(otp);

        SecureRandom prng;
        try {
            prng = SecureRandom.getInstance("SHA1PRNG"); //Number Generation Algorithm
            otp = new Integer(prng.nextInt(otpSample)).toString();
            otp = (otp.length() < otpLengthNumber) ? padleft(otp, otpLengthNumber, '0') : otp;
        } catch (NoSuchAlgorithmException e) {
        }

//        If generated OTP exists in DB -regenerate OTP again
        boolean otpExists=false;
        if (otpExists) {
            generateOTP(otpLengthNumber);
        } else {
            return otp;
        }
        return otp;
    }
    private static String padleft(String s, int len, char c) { //Fill with some char or put some logic to make more secure
        s = s.trim();
        StringBuffer d = new StringBuffer(len);
        int fill = len - s.length();
        while (fill-- > 0)
            d.append(c);
        d.append(s);
        return d.toString();
    }
    public static String generateRandomOTP(int otpLengthNumber){
        String otp = RandomStringUtils.randomNumeric(otpLengthNumber);
        return otp;
    }
}

While generating random numbers in Java for Security purposes, we use java.security.SecureRandom class or
org.apache.commons.lang.RandomStringUtils class. SecureRandom class is designed to provide cryptographically secure
random numbers, however sometime it's become quite predictable if not used properly. I explained below:

SecureRandom class uses Pseudo Random Number Generator (PRNG) implementation for generating numbers. The PRNGs are
part of Cryptographic Service Providers (CSP) (here SUN for JAVA) is default CSP. On Windows env, SUN CSP uses "SHA1PRNG"
algorithm. On Unix env it is using something else, not mentioning here. At present, we use this Window's environment only.

SecureRandom sr1 = new SecureRandom();
//The following will create SUN SHA1PRNG if the highest priority CSP is SUN
SecureRandom sr2 = SecureRandom.getInstance("SHA1PRNG");

//The following will always create SUN SHA1PRNG
SecureRandom sr3 = SecureRandom.getInstance("SHA1PRNG", "SUN");

The PRNG tries to ensure that the output does not reveal any information about the seed, and that somebody observing the
output cannot predict future outputs without knowing the seed.

What is SEED?
An early computer-based PRNG, suggested by John von Neumann in 1946, is known as the middle-square method. The algorithm
is as follows: take any number, square it, remove the middle digits of the resulting number as the "random number", then
use that number as the seed for the next iteration.

For example, squaring the number "1111" yields "1234321", which can be written as "01234321", an 8-digit number being the
square of a 4-digit number. This gives "2343" as the "random" number. Repeating this procedure gives "4896" as the next
result, and so on. "Von Neumann" used 10 digit numbers, but the process was the same.
A problem with this "middle square" method is that all sequences eventually repeat themselves, some very quickly, such
as "0000". Von Neumann was aware of this, but he found the approach sufficient for his purposes, and was worried that
mathematical "fixes" would simply hide errors rather than remove them.


Note that according to Sun’s documentation, the returned java.security.SecureRandom instance is not seeded by any of these
calls. If after one of these calls, java.security.SecureRandom.nextBytes(byte[]) is called, then the PRNG is seeded using a
secure mechanism provided by the underlying operating system (starting with JRE 1.4.1 in Windows and JRE 1.4.2 in Linux and Solaris).

If java.security.SecureRandom.setSeed(long) or java.security.SecureRandom.setSeed(byte[]) is called before a call to
java.security.SecureRandom.nextBytes(byte[]), then the internal seeding mechanism is bypassed, and only the provided seed is used
to generate random numbers.

By bypassing the internal secure seeding mechanism of the SHA1PRNG, you may compromise the security of your PRNG output. If you
seed it with anything that an attacker can potentially predict (e.g. the time when the PRNG instance was created), then using
java.security.SecureRandom may not provide the level of security that you need.

Finally, regardless of how well the PRNG is seeded, it should not be used indefinitely without reseeding. There are two approaches
that can be used for longer-term security of PRNG output:

Periodically throw away the existing java.security.SecureRandom instance and create a new one. This will generate a new instance
with a new seed.
Periodically add new random material to the PRNG seed by making a call to
::::>>> java.security.SecureRandom.setSeed( java.security.SecureRandom.generateSeed(int) ).

In summary, keep the following in mind when using java.security.SecureRandom: Always specify the exact PRNG and provider that you
wish to use. If you just use the default PRNG, you may end up with different PRNGs on different installations of your application that
may need to be called differently in order to work properly. Using the following code to get a PRNG instance is appropriate:

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");

When using the SHA1PRNG, always call java.security.SecureRandom.nextBytes(byte[]) immediately after creating a new instance of the PRNG.
This will force the PRNG to seed itself securely. If for testing purposes, you need predictable output, ignoring this rule and seeding
the PRNG with hard-coded/predictable values may be appropriate. Use at least JRE 1.4.1 on Windows and at least JRE 1.4.2 on Solaris and Linux.

Earlier versions do not seed the SHA1PRNG securely. Periodically reseed your PRNG as observing a large amount of PRNG output generated
using one seed may allow the attacker to determine the seed and thus predict all future outputs.

Some of the theoretical contents are taken from few websites but are tuned as per my way of writing, however the coding example is
completely mine. Please read and leave the comment if found useful.

Another Example with Seeding:

try {
    // Create a secure random number generator
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");

    // Get 1024 random bits
    byte[] bytes = new byte[1024/8];
    sr.nextBytes(bytes);

    // Create two secure number generators with the same seed
    int seedByteCount = 10;
    byte[] seed = sr.generateSeed(seedByteCount);

    sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed);
} 
catch (NoSuchAlgorithmException e) {
}
==========================END==========================

No comments:

Post a Comment