0

我正在编写一个需要从系统的 /dev/urandom 接口中提取 32 个字符的密钥的 Java 程序。为此,我使用以下过程:

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";
            }
    }

(如果它不能这样做,那么它将返回一堆空字符)

由于某种原因,它什么都不返回(或抛出异常而不打印堆栈跟踪)。我知道这一点,因为我将它打印到控制台(什么都没有),当我将它提供给我的 RC4 引擎时,它会引发除以零错误。为什么命令不起作用,我怎样才能使它起作用?

感谢您的帮助。

编辑:在我发布此消息后不到两分钟,我推断它没有返回空字符,因为我将它设置为返回单词“柠檬”并且它做了同样的事情,所以它不会抛出异常。

编辑2:根据评论中的建议,我尝试从文件中直接读取结果相同,但我认为我可能做错了......

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";
            }

编辑 3:这可能对那些帮助的人有所帮助:使用上面的代码并将输出传送到 od 我发现它只是读取 0。(同样,它不会抛出异常,否则它不会全为零:)

# ./run.sh | od
0000000 000000 000000 000000 000000 000000 000000 000000 000000
*
4

4 回答 4

4

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

管道 ( |) 和重定向 ( <) 由 shell 处理,而不是操作系统。您可以通过将整个命令作为字符串参数传递给sh. 请参阅以下示例并对其进行调整以供您使用:

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);
        }
    }
}

编辑

虽然上述内容可能会帮助您运行使用重定向的 shell 命令,但在当前实例中,正确的方法是根本不使用外部进程,而是直接读取/dev/urandom,正如 @Josh Cartwright 在此评论中解释的那样

于 2013-01-09T00:38:28.563 回答
2

尽管这不是绝对必要的,但为了运行具有多个命令行参数的程序,您需要创建一个包含命令的字符串数组作为第一个参数,并且每个空格都应该是数组的一个条目,例如对于 ls -l

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

这会正确调用命令行。这是 javadoc:执行 javadoc

于 2013-01-09T00:24:06.800 回答
1

做的时候不能用linux管道运行几个commans Runtime.exec()。您应该单独运行命令或tr手动处理命令的结果。除此之外,您必须使用完整路径来命令二进制文件,例如替换tr/usr/bin/tr.

于 2013-01-09T00:26:22.357 回答
1

正如 user 已经指出的那样Miserable Variable,尝试在实际的 Unix shell 中运行您的命令;并确保 shell 作为交互式 shell ( sh -i -c 'cmd1 | cmd2') 被调用。

否则 UNIX 管道机制似乎会被阻塞,因为tr不断读取/dev/urandom但由于某种原因拒绝写入其stdout.

Terminal.app尽管以下命令运行没有任何问题,但仍执行:

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

另一种方法是明确限制读取的字节数/dev/urandom(参见下面的代码)。

/*

# 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);
        }
    }
}
于 2013-01-11T16:25:41.160 回答