How to programmatically pass input to Java keytool?

1.6k Views Asked by At

I'm trying to fix a problem in the open-source project jcabi-ssl-maven-plugin.

The problem occurs in the method com.jcabi.ssl.maven.plugin.Keytool#genkey:

@Loggable(Loggable.DEBUG)
public void genkey() throws IOException {
    final Process proc = this.proc(
        "-genkeypair",
        "-alias",
        "localhost",
        "-keyalg",
        "RSA",
        "-keysize",
        "2048",
        "-keypass",
        this.password
    ).start(); // Here we launch the keytool
    final PrintWriter writer = new PrintWriter(
        new OutputStreamWriter(proc.getOutputStream())
    );
    writer.print("localhost\n"); // These and other writer.print(...) statements 
    writer.print("ACME Co.\n"); // write answers to questions of keytool
    writer.print("software developers\n");
    writer.print("San Francisco\n");
    writer.print("California\n");
    writer.print("US\n");
    writer.print("yes\n");
    writer.close();
    new VerboseProcess(proc).stdout();
    Logger.info(
        this,
        "Keystore created in '%s' (%s)",
        this.keystore,
        FileUtils.byteCountToDisplaySize(this.keystore.length())
    );
}

The purpose of this code is to

  1. launch the Java keytool program and
  2. answer its questions on the command line.

First step involves programmatic call like "C:\Program Files\Java\jdk1.8.0\jre\bin\keytool" -genkeypair -alias localhost -keyalg RSA -keysize 2048 -keypass some-password -storetype jks -noprompt -storepass some-password -keystore C:\Users\DP118M\AppData\Local\Temp\junit4133348108660376680\keystore.jks.

Then, keytool asks you several questions (see below, translated from German):

What is your name?
  [Unknown]:  A
What is the name of your organizational unit?
  [Unknown]:  A
What is the name of your organisation?
  [Unknown]:  A
What is the name of your city or county?
  [Unknown]:  A
What is the name of your state?
  [Unknown]:  A
What is the country code of your organizational unit?
  [Unknown]:  A
Is CN=A, OU=A, O=A, L=A, ST=A, C=A correct?
  [No]:  Yes

Instead of typing in the answers (A above) manually, the code above uses writer.print(...); statements.

But they obviously don't work because when I run a test, which involves the use of keytool (e. g. com.jcabi.ssl.maven.plugin.CacertsTest#importsCertificatesFromKeystore), I get following error:

java.lang.IllegalArgumentException: Non-zero exit code 1: Keytool Error: java.lang.RuntimeException: Too many repeated attempts. Program will be terminated.
\n
    at com.jcabi.log.VerboseProcess.stdout(VerboseProcess.java:220)
    at com.jcabi.log.VerboseProcess.stdout(VerboseProcess.java:158)
    at com.jcabi.ssl.maven.plugin.Keytool.genkey(Keytool.java:116)
    at com.jcabi.ssl.maven.plugin.Keystore.activate(Keystore.java:121)
    at com.jcabi.ssl.maven.plugin.CacertsTest.importsCertificatesFromKeystore(CacertsTest.java:64)

I assume that the root cause of this problem is that the writer.print(...); statements fail to deliver their texts to the keytool.

How can I fix this (make sure that in the com.jcabi.ssl.maven.plugin.Keytool#genkey method the messages written by writer.print(...); are correctly delivered to keytool) ?

Update 1 (28.12.2014 MSK):

I think I found the cause. I'm running my test on Windows, so calls like writer.print("localhost\n"); need to be replaced by writer.print("localhost" + System.getProperty("line.separator"));.

This is easy.

But there is another part. The developers of this code obviously worked on an English speaking version of keytool, so at the end it expects a yes (see last question in the example output keytool above).

Therefore there is the statement

writer.print("yes\n");

But I'm using a German Windows version, so in my case the correct response is not yes, but Ja.

If I change the code like this, it works (the test does not fail):

final String newLine = System.getProperty("line.separator");
writer.print("localhost" + newLine);
writer.print("ACME Co." + newLine);
writer.print("software developers" + newLine);
writer.print("San Francisco" + newLine);
writer.print("California" + newLine);
writer.print("US" + newLine);
writer.print("Ja" + newLine);

I appreciate hints, on how I can make this part of code work on operating systems with any language (how can I avoid hardcoding yes and Ja).

0

There are 0 best solutions below