2

I am facing a problem on the system authenticate system. Our servers use the version 1.6 while clients use version 1.8, in the process of authenticate, we generate a key by "SHA1PRNG" with SecureRandom, while the following code: i.e.:

KeyGenerator keygen = KeyGenerator.getInstance("Blowfish"); 
SecureRandom foo = SecureRandom.getInstance("SHA1PRNG");
foo.setSeed("baa".getBytes());
keygen.init(foo);

The problem is that, we found that the key generated in clients is different from that in server. We have tired to print out all steps, and found that the problem is caused by the SecureRandom, i.e., after foo.setSeed("baa".getBytes()); if we call foo.nextBytes(), it will give different values.

Therefore, we would like to know whether there is any way to keep both side generate the same value? (Given that the version of Java in both clients and server can not be changed.) Or does any platform independent SecureRandom method in Java?

Background information: SERVER and CLIENT run in Unix. I have a desktop running Java 1.8, and I have tested the followings:

  1. Desktop Java 1.8 can encrypt and decrypt the key generated in CLIENT (Java 1.8)

  2. CLIENT (Java 1.8) can not encrypt or decrypt the key generated in SERVER (Java 1.6) and verse versa.

  3. CLIENT has installed Java 1.6 (only for testing) can not encrypt or decrypt the key generated in SERVER (Java 1.6). We guess it is because the /dev/random or /dev/urandom has been overwritten to Java 1.8 version. Therefore even the version of Java is the same, they have different behavior.

Zoe
  • 27,060
  • 21
  • 118
  • 148
shing_l
  • 53
  • 1
  • 8
  • 2
    The purpose of `SecureRandom` is that the produced output are non-deterministic. – KarelG May 07 '18 at 08:09
  • Eh, if you use `Random`, there is still no guarantee that same values would be produced at two different systems. – KarelG May 07 '18 at 08:11
  • 2
    @KarelG wrong - if you use the same seed then it is guranteed that the same values will be produced - `Random` is fully deterministic. – Boris the Spider May 07 '18 at 08:12
  • @BoristheSpider no. The instance creation has a factor to this generation. (just noticed your answer. Yes you have to share the instance which totally defeats the security attempt the OP tries to resolve) – KarelG May 07 '18 at 08:21
  • 1
    @KarelG I don't understand what you are trying to say - please read the documentation linked in [my answer](https://stackoverflow.com/a/50209848/2071828). But suffice it to say, two instances of `Random` created with the same seed are _requried_ to provide the same sequence. – Boris the Spider May 07 '18 at 08:22
  • 1
    You do **not** need to share the instance between client and server @KarelG. You need to share the random seed. – Boris the Spider May 07 '18 at 08:24
  • 1
    I took a look at the code [here](http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/security/provider/SecureRandom.java/?v=diff&id2=6-b14) and the implementations for SecureRandom are, in fact, different between versions 1.6 and 1.8. – Jenna Sloan May 07 '18 at 08:41
  • @jenna Sloan, yes, that is exactly what i want to point out. – shing_l May 08 '18 at 04:20

2 Answers2

3

From the documentation for SecureRandom:

Additionally, SecureRandom must produce non-deterministic output. Therefore any seed material passed to a SecureRandom object must be unpredictable, and all SecureRandom output sequences must be cryptographically strong, as described in RFC 1750: Randomness Recommendations for Security.

So, not only are you violating the requirements of SecureRandom by passing a predictable seed, but it is explicitly required that the output of SecureRandom is unpredictable.

In order to generate a predictable sequence of randomness, use Random:

If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers.

But note that: If you use the same seed every time, then the number will always be the same, so you must use the same initial seed, that is shared betwee client and server somehow. This initial seed need to be reset every time the server application is restarted.

The instance of Random must be shared between calls to the routine, otherwise the same single number will be generated each time:

public static void main(final String[] args) {
    IntStream.range(1, 10)
            .map(i -> new Random(42).nextInt())
            .forEach(System.out::println);
}

Output:

-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035
-1170105035

Generally speaking, what you are trying to do is a Bad Idea TM. You would be much better off using an asymmertric encryption scheme rather than trying to reinvent the wheel on your own.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • 1
    Thanks for your Answer, yes, you are right, we will set different seed every time and repeat generate several times to make it "less predictable". The Posted code just a part that we found different. our aims is to make both server and client generate the same unique code for communication. Would you mind to give any suggestion? – shing_l May 07 '18 at 08:29
  • 1
    @shing_l I would still strongly recommend that you do **not** use your proposed solution. Without advasarial cryptanalysis and security analysis the solution is next to useless. **Do not roll your own security**. – Boris the Spider May 07 '18 at 08:32
  • 1
    @shing_l Please thoroughly read [this answer on a related SO site](https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own). – Boris the Spider May 07 '18 at 08:32
  • Thanks for your help and i think i need to study the asymmetric encryption. ( I tired to vote your answer up, but lack of reputation. Sorry.) Thanks a lot. I have learned an new things. – shing_l May 07 '18 at 08:45
  • 1
    @BoristheSpider This answer doesn't actually offer an alternative solution, but instead only deprecates the coding methodology of the person asking the question. – Jenna Sloan May 07 '18 at 08:53
  • 1
    @JennaSloan 1) it does offer an answer, use `Random` and share the seed. 2) often with crytography it is better to tell people why not to roll their own. – Boris the Spider May 07 '18 at 08:54
  • 1
    @JennaSloan If the coding methodology is wrong it should be so stated. How else is anybody going to learn? – user207421 May 07 '18 at 09:02
  • Your example uses `java.lang.Random` (which has different guarantees) to demonstrate that `java.security.SecureRandom`can produce the same stream of numbers given the same initial seed. Had you used `SecureRandom` with `"NativePRNG"` (as in the question), you would find that `SecureRandom` cannot be made to reliably produce the same stream of output every time, even with the same initial seed. If you use `"SHA1PRNG"` instead of `"NativePRNG"`, then you can get predictable output from it. – Christopher Schultz Aug 20 '18 at 13:56
  • @ChristopherSchultz excuse me? I'm doing the exact opposite. I'm saying exactly that it _doesn't_ produce the same sequence and I quote documentation to that effect. My code demonstrates that using `Random` you can produce the same sequence - "_In order to generate a predictable sequence of randomness, use `Random`_". Please read the question and answer more carefully. – Boris the Spider Aug 20 '18 at 14:01
-1

Don't invent your own key deviation functions. Use the functions invented for this.

You don't write what you use as input for your key deviation, but if it's a password you could use a PBE (Password based encryption) Key Spec - something like this:

SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "Blowfish");
Ebbe M. Pedersen
  • 7,250
  • 3
  • 27
  • 47
  • The OP is trying to generate a key for a symmetric cipher both client and server side using the same, predictable, mechanism - this is different to a KDF. – Boris the Spider May 07 '18 at 15:07
  • @Ebbe M. Pedersen, Thanks for your suggestion, from your answer, I have take a look on this, https://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption/992413#992413 , the OP there is doing something like our program. The SecretKey will be further used in Cipher in some other part. As I am using JDK 1.6, the factory should use "PBKDF2WithHmacSHA1", and the size of the key should be 128 rather than 256. However, I got another problem (java.security.InvalidKeyException) , it seems that we need to install something extra library (unlimited jurisdiction patch), right? – shing_l May 08 '18 at 04:30
  • @Boris the Spider, Thanks for your explanation. Um...actually I am new in encryption, so I may explain wrongly on the meaning of predictable. Our system will do the same thing in both client and server side to encrypt and decrypt some password. Just like jenna Sloan said, SecureRandom in 1.6 and 1.8 give different behavior. If the problem on SecureRandom solved, I can avoid large system changes. – shing_l May 08 '18 at 04:37