0

I'm writing a Java program that needs to extract a 32 character key from the system's /dev/urandom interface. For this, I'm using the following procedure:

public String generateKey() {
            try {
                    Runtime run = Runtime.getRuntime();
                    Process pr = run.exec("tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1;echo;");
                    pr.waitFor();
                    BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
                    StringBuffer sb = new StringBuffer();
                    String line = "";
                    while ((line = buf.readLine())!= null ) {
                            sb.append(line);
                    }
                    return sb.toString();
            } catch (Exception e) {
                    e.printStackTrace();
                    return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
            }
    }

(If it can't do it then it will return a bunch of null characters)

For some reason it's returning nothing (or throwing an exception and not printing the stack trace). I know this because I print it out to the console (nothing) and when I give it to my RC4 engine it throws a divide by zero error. Why is the command not working and how could I make it work?

Thank you for the help.

Edit: Not two minutes after I posted this I have deduced it's not returning the null characters because I set it to return the word "lemons" and it did the same thing so it's not throwing an exception.

Edit 2: As per a suggestion in the comments I tried to read right from the file with the same results but I think I may be doing this wrong...

File file = new File("/dev/urandom");
            FileReader freader = null;
            StringBuffer sb = new StringBuffer();
            int x = 0;
            try {
                    freader = new FileReader(file);
                    BufferedReader reader = new BufferedReader(freader);
                    for (int i=0;(i<32||(x=reader.read())==-1);i++) {
                            sb.append((char) x);
                    }
            return sb.toString();
            } catch(Exception e) {
                    e.printStackTrace();
                    return "a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
            }

Edit 3: This may be helpful to those helping: Using the above code and piping the output to od I found it's just reading 0s. (Again, it's not throwing an exception otherwise it wouldn't be all zeros:)

# ./run.sh | od
0000000 000000 000000 000000 000000 000000 000000 000000 000000
*
6
  • 1
    Just do not catch the Exception [especially] in a minimal test-case .. no need to deal with lemon NULs. Commented Jan 9, 2013 at 0:11
  • What do you mean by "do not catch the exception"? If I remove the try/catch block the compiler will yell at me. I assume you mean something different... Commented Jan 9, 2013 at 0:14
  • Read up on Checked Exceptions - which can be such annoying critters - and the throws clause. If this is unadvisable for other means (i.e. larger context), simply wrap it as an Unchecked: try { .. } catch (Exception ex) { throw new RuntimeException(ex); }. The difference between this and plain logging is this still propagates an exception up - yay! Only suppress an exception if there is something useful to do with it (which might be "suppressing it" in cases, but not here/in a test-case). Commented Jan 9, 2013 at 0:14
  • 1
    Why must you use a shell at all? Why not just read 32-characters from /dev/urandom using the standard java file io mechanisms? Commented Jan 9, 2013 at 0:40
  • It never threw a RuntimeException so, as I thought, it's not throwing an exception in itself. I think the command isn't returning as it would in a regular bash shell or its not being read properly. Commented Jan 9, 2013 at 0:58

4 Answers 4

4

Process pr = run.exec("tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1;echo;");

The pipe (|) and redirection (<) are handled by the shell, not the OS. You can trivially solve this by passing the entire command as a string parameter to sh. See the following example and adjust it for your use:

import java.io.*;
class P1 {
    public static void main(String[] args) throws Exception {
        Runtime run = Runtime.getRuntime();
        Process pr = run.exec("sh -c \"ls </dev/null | grep java\"");
        pr.waitFor();
        BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
        String line;
        while ((line = buf.readLine())!= null ) {
            System.out.println(line);
        }
    }
}

EDIT

While the above might help you run shell commands that use redirection, in the current instance the right approach is to not use external process at all, instead reading from /dev/urandom directly, as @Josh Cartwright explains in this comment

Sign up to request clarification or add additional context in comments.

Comments

2

Though this is not strictly necessary, in order to run a program with multiple command line arguments, you need to create an array of strings consisting on the command as the first parameter and each space should be an entry of the array, for example for ls -l

String[] command = {"ls" , "-l"};
Runtime.getRuntime().exec(command);

This makes the correct call to the command line. Here is the javadoc: Exec javadoc

2 Comments

Or simple Runtime.getRuntime().exec("ls", "-l")
not necesarily. Runtime.exec("/bin/echo XXX") works perfectly.
1

As already indicated by user Miserable Variable, try to run your commands inside an actual Unix shell; and make sure the shell gets invoked as an interactive shell (sh -i -c 'cmd1 | cmd2').

Otherwise the UNIX piping mechanism seems to get blocked because tr keeps reading from /dev/urandom but for some reason refuses to write to its stdout.

Executed in Terminal.app though the following command runs without any hiccups:

sh -c 'LC_ALL=C tr -cd [:alnum:] < /dev/urandom | fold -w30 | head -n1; echo;'

Another approach would be to explicitly limit the number of bytes read from /dev/urandom (see code below).

/*

# cat GenerateKey.java
javac GenerateKey.java
java GenerateKey

# For more information see the "Java exec and Unix pipes" comment in:
# "Java exec - execute system processes with Java ProcessBuilder and Process (part 3)",
# http://alvinalexander.com/java/java-exec-processbuilder-process-3

*/

import java.io.*;
import java.util.*;

public class GenerateKey {
    public static void main(String[] args) throws Exception {
        Runtime run = Runtime.getRuntime();

        // does not work on Mac OS X 10.6.8
        //String[] cmd = { "/bin/sh", "-c", "LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1; echo;" };  

        // works
        String[] cmd = { "/bin/sh", "-i", "-c", "LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1; echo;" };  
        //String[] cmd = { "/bin/sh", "-c", "head -c 3000 < /dev/urandom | LC_ALL=C tr -cd '[:alnum:]' | head -c 30" }; 
        //String[] cmd = { "/bin/sh", "-c", "dd if=/dev/urandom bs=3000 count=1 2>/dev/null | LC_ALL=C tr -cd '[:alnum:]' | fold -w30 | head -n1" }; 
        Process pr = run.exec(cmd);

        pr.waitFor();
        BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
        String line;
        while ((line = buf.readLine())!= null ) {
            System.out.println(line);
        }
    }
}

Comments

1

You can not use linux piping to run several commans when doing Runtime.exec(). You should run commands separatelly or process the result of tr command manually. Besides this, you must use full path to command binary, e.g. replace tr with /usr/bin/tr.

1 Comment

No in that sample. It throws Caused by: java.io.IOException: java.io.IOException: error=2, No such file or directory for me if I try to use tr syntax. It works well if I replace tr with /usr/bin/tr

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.