test/jdk/sun/security/tools/keytool/KeyToolTest.java
author weijun
Fri, 15 Nov 2019 09:06:58 +0800
changeset 59104 046e4024e55a
parent 58902 197238c30630
permissions -rw-r--r--
8214024: Remove the default keytool -keyalg value Reviewed-by: mullan

/*
 * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 6251120 8231950
 * @summary Testing keytool
 *
 * Run through autotest.sh and manualtest.sh
 *
 * Testing non-PKCS11 keystores:
 *       echo | java -Dfile KeyToolTest
 *
 * Testing NSS PKCS11 keystores:
 *       # testing NSS
 *       # make sure the NSS db files are in current directory and writable
 *       echo | java -Dnss -Dnss.lib=/path/to/libsoftokn3.so KeyToolTest
 *
 * Testing Solaris Cryptography Framework PKCS11 keystores:
 *       # make sure you've already run pktool and set test12 as pin
 *       echo | java -Dsolaris KeyToolTest
 *
 * ATTENTION:
 * Exception in thread "main" java.security.ProviderException:
 *   sun.security.pkcs11.wrapper.PKCS11Exception: CKR_KEY_SIZE_RANGE
 *       at sun.security.pkcs11.P11Signature.engineSign(P11Signature.java:420)
 *       ...
 * Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_KEY_SIZE_RANGE
 *       at sun.security.pkcs11.wrapper.PKCS11.C_SignFinal(Native Method)
 *       at sun.security.pkcs11.P11Signature.engineSign(P11Signature.java:391)
 *       ...
 * been observed. Possibly a Solaris bug
 *
 * ATTENTION:
 * NSS PKCS11 config file are changed, DSA not supported now.
 *
 * @library /test/lib
 * @modules java.base/sun.security.tools.keytool
 *          java.base/sun.security.util
 *          java.base/sun.security.x509
 * @run main/othervm/timeout=600 -Dfile KeyToolTest
 */

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import sun.security.x509.*;
import java.io.*;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.security.cert.X509Certificate;
import jdk.test.lib.util.FileUtils;
import sun.security.util.ObjectIdentifier;


public class KeyToolTest {

    // The stdout and stderr outputs after a keytool run
    String out;
    String err;

    // the output of println() in KeyTool.run
    String ex;

    String lastInput = "", lastCommand = "";
    private static final boolean debug =
        System.getProperty("debug") != null;

    static final String NSS_P11_ARG =
            "-keystore NONE -storetype PKCS11 -providerName SunPKCS11-nss " +
            "-addprovider SunPKCS11 " +
            "-providerArg p11-nss.txt ";
    // Use -providerClass here, to confirm it still works for SunPKCS11.
    static final String NSS_SRC_P11_ARG =
            "-srckeystore NONE -srcstoretype PKCS11 " +
            "-srcproviderName SunPKCS11-nss " +
            "-providerClass sun.security.pkcs11.SunPKCS11 " +
            "-providerArg p11-nss.txt ";
    static final String NZZ_P11_ARG =
            "-keystore NONE -storetype PKCS11 -providerName SunPKCS11-nzz " +
            "-addprovider SunPKCS11 " +
            "-providerArg p11-nzz.txt ";
    static final String NZZ_SRC_P11_ARG =
            "-srckeystore NONE -srcstoretype PKCS11 " +
            "-srcproviderName SunPKCS11-nzz " +
            "-addprovider SunPKCS11 " +
            "-providerArg p11-nzz.txt ";
    static final String SUN_P11_ARG = "-keystore NONE -storetype PKCS11 ";
    static final String SUN_SRC_P11_ARG =
            "-srckeystore NONE -srcstoretype PKCS11 ";

    String p11Arg, srcP11Arg;

    /** Creates a new instance of KeyToolTest */
    KeyToolTest() {
        // so that there is "Warning" and not translated into other language
        Locale.setDefault(Locale.US);
    }

    /**
     * Helper, removes a file
     */
    void remove(String filename) {
        if (debug) {
            System.err.println("Removing " + filename);
        }
        try{
            FileUtils.deleteFileIfExistsWithRetry(Paths.get(filename));
        }catch(IOException e) {
            throw new RuntimeException("Error deleting " + filename, e);
        }
    }

    /**
     * Run a set of keytool command with given terminal input.
     * @param input the terminal inputs, the characters typed by human
     *        if <code>cmd</code> is running on a terminal
     * @param cmd the argument of a keytool command line
     * @throws if keytool goes wrong in some place
     */
    void test(String input, String cmd) throws Exception {
        lastInput = input;
        lastCommand = cmd;

        // "X" is appended so that we can precisely test how input is consumed
        HumanInputStream in = new HumanInputStream(input+"X");
        test(in, cmd);
        // make sure the input string is no more no less
        if(in.read() != 'X' || in.read() != -1)
            throw new Exception("Input not consumed exactly");
    }

    void test(InputStream in, String cmd) throws Exception {

        // save the original 3 streams
        if (debug) {
            System.err.println(cmd);
        } else {
            System.err.print(".");
        }
        PrintStream p1 = System.out;
        PrintStream p2 = System.err;
        InputStream i1 = System.in;

        ByteArrayOutputStream b1 = new ByteArrayOutputStream();
        ByteArrayOutputStream b2 = new ByteArrayOutputStream();

        try {
            System.setIn(in);
            System.setOut(new PrintStream(b1));
            System.setErr(new PrintStream(b2));

            // since System.in is overrided, the
            // sun.security.tools.keytool.Main.main() method will
            // never block at user input

            // use -debug so that main() will throw an Exception
            // instead of calling System.exit()
            sun.security.tools.keytool.Main.main(("-debug "+cmd).split("\\s+"));
        } finally {
            out = b1.toString();
            err = b2.toString();
            ex = out;   // now it goes to System.out
            System.setIn(i1);
            System.setOut(p1);
            System.setErr(p2);
        }
    }

    /**
     * Call this method if you expect test(input, cmd) should go OK
     */
    void testOK(String input, String cmd) throws Exception {
        try {
            // Workaround for "8057810: Make SHA256withDSA the default
            // jarsigner and keytool algorithm for DSA keys". Unfortunately
            // SunPKCS11-NSS does not support SHA256withDSA yet.
            if (cmd.contains("p11-nss.txt") && cmd.contains("-genkey")
                    && cmd.contains("DSA")) {
                cmd += " -sigalg SHA1withDSA -keysize 1024";
            }
            test(input, cmd);
        } catch(Exception e) {
            afterFail(input, cmd, "OK");
            throw e;
        }
    }

    /**
     * Call this method if you expect test(input, cmd) should fail and throw
     * an exception
     */
    void testFail(String input, String cmd) throws Exception {
        boolean ok;
        try {
            test(input, cmd);
            ok = true;
        } catch(Exception e) {
            if (e instanceof MissingResourceException) {
                ok = true;
            } else {
                ok = false;
            }
        }
        if(ok) {
            afterFail(input, cmd, "FAIL");
            throw new RuntimeException();
        }
    }

    /**
     * Call this method if you expect test(input, cmd) should go OK
     */
    void testOK(InputStream is, String cmd) throws Exception {
        try {
            test(is, cmd);
        } catch(Exception e) {
            afterFail("", cmd, "OK");
            throw e;
        }
    }

    /**
     * Call this method if you expect test(input, cmd) should fail and throw
     * an exception
     */
    void testFail(InputStream is, String cmd) throws Exception {
        boolean ok;
        try {
            test(is, cmd);
            ok = true;
        } catch(Exception e) {
            ok = false;
        }
        if(ok) {
            afterFail("", cmd, "FAIL");
            throw new RuntimeException();
        }
    }

    /**
     * Call this method if you just want to run the command and does
     * not care if it succeeds or fails.
     */
    void testAnyway(String input, String cmd) {
        try {
            test(input, cmd);
        } catch(Exception e) {
            ;
        }
    }

    /**
     * Helper method, print some output after a test does not do as expected
     */
    void afterFail(String input, String cmd, String should) {
        if (cmd.contains("p11-nss.txt")) {
            cmd = "-J-Dnss.lib=" + System.getProperty("nss.lib") + " " + cmd;
        }
        System.err.println("\nTest fails for the command ---\n" +
                "keytool " + cmd + "\nOr its debug version ---\n" +
                "keytool -debug " + cmd);

        System.err.println("The command result should be " + should +
                ", but it's not. Try run the command manually and type" +
                " these input into it: ");
        char[] inputChars = input.toCharArray();

        for (int i=0; i<inputChars.length; i++) {
            char ch = inputChars[i];
            if (ch == '\n') System.err.print("ENTER ");
            else if (ch == ' ') System.err.print("SPACE ");
            else System.err.print(ch + " ");
        }
        System.err.println("");

        System.err.println("ERR is:\n"+err);
        System.err.println("OUT is:\n"+out);
    }

    void assertTrue(boolean bool, String msg) {
        if (debug) {
            System.err.println("If not " + bool + ", " + msg);
        } else {
            System.err.print("v");
        }
        if(!bool) {
            afterFail(lastInput, lastCommand, "TRUE");
                System.err.println(msg);
            throw new RuntimeException(msg);
        }
    }

    void assertTrue(boolean bool) {
        assertTrue(bool, "well...");
    }
    /**
     * Helper method, load a keystore
     * @param file file for keystore, null or "NONE" for PKCS11
     * @pass password for the keystore
     * @type keystore type
     * @returns the KeyStore object
     * @exception Exception if anything goes wrong
     */
    KeyStore loadStore(String file, String pass, String type) throws Exception {
        KeyStore ks = KeyStore.getInstance(type);
        FileInputStream is = null;
        if (file != null && !file.equals("NONE")) {
            is = new FileInputStream(file);
        }
        ks.load(is, pass.toCharArray());
        is.close();
        return ks;
    }

    /**
     * The test suite.
     * Maybe it's better to put this outside the KeyToolTest class
     */
    void testAll() throws Exception {
        KeyStore ks;

        remove("x.jks");
        remove("x.jceks");
        remove("x.p12");
        remove("x2.jceks");
        remove("x2.jks");
        remove("x.jks.p1.cert");

        // name changes: genkeypair, importcert, exportcert
        remove("x.jks");
        remove("x.jks.p1.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -alias p1 -file x.jks.p1.cert");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.getKey("p1", "changeit".toCharArray()) != null,
            "key not DSA");
        assertTrue(new File("x.jks.p1.cert").exists(), "p1 export err");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias p1");
        // importcert, prompt for Yes/No
        testOK("y\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -alias c1 -file x.jks.p1.cert");
        // importcert, -noprompt
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -alias c2 -file x.jks.p1.cert -noprompt");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.getCertificate("c1") != null, "import c1 err");

        // v3
        byte[] encoded = ks.getCertificate("c1").getEncoded();
        X509CertImpl certImpl = new X509CertImpl(encoded);
        assertTrue(certImpl.getVersion() == 3, "Version is not 3");

        // changealias and keyclone
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        testOK("changeit\n", "-keystore x.jks -storetype JKS " +
                "-changealias -alias p1 -destalias p11");
        testOK("changeit\n", "-keystore x.jks -storetype JKS " +
                "-changealias -alias c1 -destalias c11");
        // press ENTER when prompt for p111's keypass
        testOK("changeit\n\n", "-keystore x.jks -storetype JKS " +
                "-keyclone -alias p11 -destalias p111");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(!ks.containsAlias("p1"), "there is no p1");
        assertTrue(!ks.containsAlias("c1"), "there is no c1");
        assertTrue(ks.containsAlias("p11"), "there is p11");
        assertTrue(ks.containsAlias("c11"), "there is c11");
        assertTrue(ks.containsAlias("p111"), "there is p111");

        // genSecKey
        remove("x.jceks");
        // DES, no need keysize
        testOK("changeit\nchangeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg DES -alias s1");
        // DES, keysize cannot be 128
        testFail("changeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg DES -alias s11 -keysize 128");
        // DESede. no need keysize
        testOK("changeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg DESede -alias s2");
        // AES, need keysize
        testFail("changeit\n\n", "-keystore x.jceks -storetype AES " +
                "-genseckey -keyalg Rijndael -alias s3");
        testOK("changeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg AES -alias s3 -keysize 128");
        // about keypass
        // can accept storepass
        testOK("\n", "-keystore x.jceks -storetype JCEKS -storepass changeit " +
                "-genseckey -keyalg DES -alias s4");
        // or a new one
        testOK("keypass\nkeypass\n", "-keystore x.jceks -storetype JCEKS " +
                "-storepass changeit -genseckey -keyalg DES -alias s5");
        // keypass must be valid (prompt 3 times)
        testOK("bad\n\bad\nkeypass\nkeypass\n", "-keystore x.jceks " +
                "-storetype JCEKS -storepass changeit -genseckey " +
                "-keyalg DES -alias s6");
        // keypass must be valid (prompt 3 times)
        testFail("bad\n\bad\nbad\n", "-keystore x.jceks -storetype JCEKS " +
                "-storepass changeit -genseckey -keyalg DES -alias s7");
        // keypass must be valid (prompt 3 times)
        testFail("bad\n\bad\nbad\nkeypass\n", "-keystore x.jceks " +
                "-storetype JCEKS -storepass changeit -genseckey -keyalg DES -alias s7");
        ks = loadStore("x.jceks", "changeit", "JCEKS");
        assertTrue(ks.getKey("s1", "changeit".toCharArray())
                .getAlgorithm().equalsIgnoreCase("DES"), "s1 is DES");
        assertTrue(ks.getKey("s1", "changeit".toCharArray())
                .getEncoded().length == 8,  "DES is 56");
        assertTrue(ks.getKey("s2", "changeit".toCharArray())
                .getEncoded().length == 24,  "DESede is 168");
        assertTrue(ks.getKey("s2", "changeit".toCharArray())
                .getAlgorithm().equalsIgnoreCase("DESede"), "s2 is DESede");
        assertTrue(ks.getKey("s3", "changeit".toCharArray())
                .getAlgorithm().equalsIgnoreCase("AES"), "s3 is AES");
        assertTrue(ks.getKey("s4", "changeit".toCharArray())
                .getAlgorithm().equalsIgnoreCase("DES"), "s4 is DES");
        assertTrue(ks.getKey("s5", "keypass".toCharArray())
                .getAlgorithm().equalsIgnoreCase("DES"), "s5 is DES");
        assertTrue(ks.getKey("s6", "keypass".toCharArray())
                .getAlgorithm().equalsIgnoreCase("DES"), "s6 is DES");
        assertTrue(!ks.containsAlias("s7"), "s7 not created");

        // maybe we needn't test this, one day JKS will support SecretKey
        //testFail("changeit\nchangeit\n", "-keystore x.jks -storetype JKS " +
        //        "-genseckey -keyalg AES -alias s3 -keysize 128");

        // importKeyStore
        remove("x.jks");
        remove("x.jceks");
        // create 2 entries...
        testOK("changeit\nchangeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genkeypair -keyalg DSA -alias p1 -dname CN=Olala");
        testOK("", "-keystore x.jceks -storetype JCEKS -storepass changeit " +
                "-importcert -alias c1 -file x.jks.p1.cert -noprompt");
        ks = loadStore("x.jceks", "changeit", "JCEKS");
        assertTrue(ks.size() == 2, "2 entries in JCEKS");
        // import, shouldn't mention destalias/srckeypass/destkeypass
        // if srcalias is no given
        testFail("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -destalias pp");
        testFail("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srckeypass changeit");
        testFail("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -destkeypass changeit");
        // normal import
        testOK("changeit\nchangeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 2, "2 entries in JKS");
        // import again, type yes to overwrite old entries
        testOK("changeit\nchangeit\ny\ny\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS");
        ks = loadStore("x.jks", "changeit", "JKS");
        // import again, specify -nopromt
        testOK("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -noprompt");
        assertTrue(err.indexOf("Warning") != -1, "noprompt will warn");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 2, "2 entries in JKS");
        // import again, type into new aliases when prompted
        testOK("changeit\nchangeit\n\ns1\n\ns2\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 4, "4 entries in JKS");

        // importkeystore single
        // normal
        remove("x.jks");
        testOK("changeit\nchangeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias p1");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 1, "1 entries in JKS");
        // overwrite
        testOK("changeit\nchangeit\ny\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias p1");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 1, "1 entries in JKS");
        // noprompt
        testOK("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias p1 -noprompt");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 1, "1 entries in JKS");
        // rename
        testOK("changeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias p1 -destalias p2");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 2, "2 entries in JKS");
        // another rename
        testOK("changeit\nchangeit\n\nnewalias\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias p1");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 3, "3 entries in JKS");

        // importkeystore single, different keypass
        remove("x.jks");
        // generate entry with different keypass
        testOK("changeit\nkeypass\nkeypass\n", "-keystore x.jceks " +
                "-storetype JCEKS -genkeypair -keyalg DSA -alias p2 -dname CN=Olala");
        // prompt
        testOK("changeit\nchangeit\nchangeit\nkeypass\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias p2");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 1, "1 entries in JKS");
        // diff destkeypass
        testOK("changeit\nchangeit\nkeypass\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias p2 -destalias p3 -destkeypass keypass2");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.size() == 2, "2 entries in JKS");
        assertTrue(ks.getKey("p2", "keypass".toCharArray()) != null,
                "p2 has old password");
        assertTrue(ks.getKey("p3", "keypass2".toCharArray()) != null,
                "p3 has new password");

        // importkeystore single, cert
        remove("x.jks");
        // normal
        testOK("changeit\nchangeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias c1");
        // in fact srcstorepass can be ignored
        testOK("changeit\n\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias c1 -destalias c2");
        assertTrue(err.indexOf("WARNING") != -1, "But will warn");
        // 2nd import, press y to overwrite ...
        testOK("changeit\n\ny\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias c1 -destalias c2");
        // ... or rename
        testOK("changeit\n\n\nc3\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS " +
                "-srcalias c1 -destalias c2");
        ks = loadStore("x.jks", "changeit", "JKS");
        // c1, c2, c3
        assertTrue(ks.size() == 3, "3 entries in JKS");

        // importkeystore, secretkey
        remove("x.jks");
        // create SecretKeyEntry
        testOK("changeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg DES -alias s1");
        // create SecretKeyEntry
        testOK("changeit\n\n", "-keystore x.jceks -storetype JCEKS " +
                "-genseckey -keyalg DES -alias s2");
        // remove the keypass!=storepass one
        testOK("changeit\n", "-keystore x.jceks -storetype JCEKS " +
                "-delete -alias p2");
        ks = loadStore("x.jceks", "changeit", "JCEKS");
        // p1, c1, s1, s2
        assertTrue(ks.size() == 4, "4 entries in JCEKS");
        // normal
        testOK("changeit\nchangeit\nchangeit\n", "-importkeystore " +
                "-srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS -srcalias s1");
        assertTrue(err.indexOf("not imported") != -1, "Not imported");
        assertTrue(err.indexOf("Cannot store non-PrivateKeys") != -1,
                "Not imported");

        // Importing a JCEKS keystore to a JKS one. Will warn
        // for the 2 SecretKey entries

        remove("x.jks");
        // Two "no" answers to bypass warnings
        // normal
        testOK("\n\n", "-srcstorepass changeit -deststorepass changeit " +
                "-importkeystore -srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS");
        assertTrue(err.indexOf("s1 not") != -1, "s1 not");
        assertTrue(err.indexOf("s2 not") != -1, "s2 not");
        assertTrue(err.indexOf("c1 success") != -1, "c1 success");
        assertTrue(err.indexOf("p1 success") != -1, "p1 success");
        remove("x.jks");
        // One "yes" to stop
        // normal
        testOK("yes\n", "-srcstorepass changeit -deststorepass changeit " +
                "-importkeystore -srckeystore x.jceks -srcstoretype JCEKS " +
                "-destkeystore x.jks -deststoretype JKS");
        // maybe c1 or p1 has been imported before s1 or s2 is touched,
        // anyway we know yesNo is only asked once.

        // pkcs12
        remove("x.jks");
        // JKS prompt for keypass
        testFail("changeit\nchangeit\n", "-keystore x.jks -storetype JKS " +
                "-genkeypair -alias p1 -dname CN=olala");
        remove("x.jks");
        // just type ENTER means keypass=storepass
        testOK("changeit\nchangeit\n\n", "-keystore x.jks -storetype JKS " +
                "-genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        remove("x.p12");
        // PKCS12 only need storepass
        testOK("", "-keystore x.p12 -storetype PKCS12 -storepass changeit " +
                "-genkeypair -keyalg DSA -alias p0 -dname CN=olala");
        testOK("changeit\n", "-keystore x.p12 -storetype PKCS12 " +
                "-genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        // when specify keypass, make sure keypass==storepass...
        testOK("changeit\n", "-keystore x.p12 -keypass changeit " +
                "-storetype PKCS12 -genkeypair -keyalg DSA -alias p3 -dname CN=olala");
        assertTrue(err.indexOf("Warning") == -1,
                "PKCS12 silent when keypass == storepass");
        // otherwise, print a warning
        testOK("changeit\n", "-keystore x.p12 -keypass another" +
                " -storetype PKCS12 -genkeypair -keyalg DSA -alias p2 -dname CN=olala");
        assertTrue(err.indexOf("Warning") != -1,
                "PKCS12 warning when keypass != storepass");
        // no -keypasswd for PKCS12
        testFail("", "-keystore x.p12 -storepass changeit -storetype PKCS12" +
                " -keypasswd -new changeit -alias p3");
        testOK("", "-keystore x.p12 -storepass changeit -storetype PKCS12 " +
                "-changealias -alias p3 -destalias p33");
        testOK("", "-keystore x.p12 -storepass changeit -storetype PKCS12 " +
                "-keyclone -alias p33 -destalias p3");

        // pkcs12
        remove("x.p12");
        // PKCS12 only need storepass
        testOK("", "-keystore x.p12 -storetype PKCS12 -storepass changeit " +
                "-genkeypair -keyalg DSA -alias p0 -dname CN=olala");
        testOK("", "-storepass changeit -keystore x.p12 -storetype PKCS12 " +
                "-genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        // when specify keypass, make sure keypass==storepass...
        testOK("", "-storepass changeit -keystore x.p12 -keypass changeit " +
                "-storetype PKCS12 -genkeypair -keyalg DSA -alias p3 -dname CN=olala");
        assertTrue(err.indexOf("Warning") == -1,
                "PKCS12 silent when keypass == storepass");
        // otherwise, print a warning
        testOK("", "-storepass changeit -keystore x.p12 -keypass another " +
                "-storetype PKCS12 -genkeypair -keyalg DSA -alias p2 -dname CN=olala");
        assertTrue(err.indexOf("Warning") != -1,
                "PKCS12 warning when keypass != storepass");

        remove("x.jks");
        remove("x.jceks");
        remove("x.p12");
        remove("x2.jceks");
        remove("x2.jks");
        remove("x.jks.p1.cert");
    }

    void testPKCS11() throws Exception {
        KeyStore ks;
        // pkcs11, the password maybe different and maybe PKCS11 not supported

        // in case last test is not executed successfully
        testAnyway("", p11Arg + "-storepass test12 -delete -alias p1");
        testAnyway("", p11Arg + "-storepass test12 -delete -alias p2");
        testAnyway("", p11Arg + "-storepass test12 -delete -alias p3");
        testAnyway("", p11Arg + "-storepass test12 -delete -alias nss");

        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 0 entries") != -1,
                "*** MAKE SURE YOU HAVE NO ENTRIES IN YOUR PKCS11 KEYSTORE " +
                        "BEFORE THIS TEST ***");

        testOK("", p11Arg +
                "-storepass test12 -genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        testOK("test12\n", p11Arg + "-genkeypair -keyalg DSA -alias p2 -dname CN=olala2");
        // cannot provide keypass for PKCS11
        testFail("test12\n", p11Arg +
                "-keypass test12 -genkeypair -keyalg DSA -alias p3 -dname CN=olala3");
        // cannot provide keypass for PKCS11
        testFail("test12\n", p11Arg +
                "-keypass nonsense -genkeypair -keyalg DSA -alias p3 -dname CN=olala3");

        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 2 entries") != -1,
                "2 entries in p11");

        testOK("test12\n", p11Arg + "-alias p1 -changealias -destalias p3");
        testOK("", p11Arg + "-storepass test12 -list -alias p3");
        testFail("", p11Arg + "-storepass test12 -list -alias p1");

        testOK("test12\n", p11Arg + "-alias p3 -keyclone -destalias p1");
        // in PKCS11, keyclone will delete old
        testFail("", p11Arg + "-storepass test12 -list -alias p3");
        testOK("", p11Arg + "-storepass test12 -list -alias p1");

        // cannot change password for PKCS11
        testFail("test12\n", p11Arg + "-alias p1 -keypasswd -new another");

        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 2 entries") != -1,
                "2 entries in p11");

        testOK("", p11Arg + "-storepass test12 -delete -alias p1");
        testOK("", p11Arg + "-storepass test12 -delete -alias p2");

        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 0 entries") != -1,
                "*** MAKE SURE YOU HAVE NO ENTRIES IN YOUR PKCS11 KEYSTORE" +
                        " BEFORE THIS TEST ***");
    }

    void testPKCS11ImportKeyStore() throws Exception {

        KeyStore ks;
        testOK("", p11Arg +
                "-storepass test12 -genkeypair -keyalg DSA -alias p1 -dname CN=olala");
        testOK("test12\n", p11Arg + "-genkeypair -keyalg DSA -alias p2 -dname CN=olala2");
        // test importkeystore for pkcs11

        remove("x.jks");
        // pkcs11 -> jks
        testOK("changeit\nchangeit\ntest12\n", srcP11Arg +
                ("-importkeystore -destkeystore x.jks -deststoretype JKS " +
                "-srcalias p1"));
        assertTrue(err.indexOf("not imported") != -1,
                "cannot import key without destkeypass");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(!ks.containsAlias("p1"), "p1 is not imported");

        testOK("changeit\ntest12\n", srcP11Arg +
                ("-importkeystore -destkeystore x.jks -deststoretype JKS " +
                "-srcalias p1 -destkeypass changeit"));
        testOK("changeit\ntest12\n", srcP11Arg +
                ("-importkeystore -destkeystore x.jks -deststoretype JKS " +
                "-srcalias p2 -destkeypass changeit"));
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.containsAlias("p1"), "p1 is imported");
        assertTrue(ks.containsAlias("p2"), "p2 is imported");
        // jks -> pkcs11
        testOK("", p11Arg + "-storepass test12 -delete -alias p1");
        testOK("", p11Arg + "-storepass test12 -delete -alias p2");
        testOK("test12\nchangeit\n", p11Arg +
                "-importkeystore -srckeystore x.jks -srcstoretype JKS");
        testOK("", p11Arg + "-storepass test12 -list -alias p1");
        testOK("", p11Arg + "-storepass test12 -list -alias p2");
        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 2 entries") != -1,
                "2 entries in p11");
        // clean up
        testOK("", p11Arg + "-storepass test12 -delete -alias p1");
        testOK("", p11Arg + "-storepass test12 -delete -alias p2");
        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 0 entries") != -1,
                "empty p11");

        remove("x.jks");
    }

    // Selected sqeTest
    void sqeTest() throws Exception {
        FileOutputStream fos = new FileOutputStream("badkeystore");
        for (int i=0; i<100; i++) {
            fos.write(i);
        }
        fos.close();

        sqeCsrTest();
        sqePrintcertTest();
        sqeDeleteTest();
        sqeExportTest();
        sqeGenkeyTest();
        sqeImportTest();
        sqeKeyclonetest();
        sqeKeypasswdTest();
        sqeListTest();
        sqeSelfCertTest();
        sqeStorepassTest();

        remove("badkeystore");
    }

    // Import: cacert, prompt, trusted, non-trusted, bad chain, not match
    void sqeImportTest() throws Exception {
        KeyStore ks;
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -file x.jks.p1.cert");
        /* deleted */ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert -noprompt");
        /* deleted */ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -delete -alias mykey");
        testOK("yes\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(ks.containsAlias("mykey"), "imported");
        /* deleted */ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -delete -alias mykey");
        testOK("\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(!ks.containsAlias("mykey"), "imported");
        testOK("no\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert");
        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(!ks.containsAlias("mykey"), "imported");
        testFail("no\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file nonexist");
        testFail("no\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks");
        remove("x.jks");
    }
    // keyclone: exist. nonexist err, cert err, dest exist, misc
    void sqeKeyclonetest() throws Exception {
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        // new pass
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -new newpass -keyclone -dest p0");
        // new pass
        testOK("\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -keyclone -dest p1");
        testOK("\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keyclone -dest p2");
        testFail("\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keyclone -dest p2");
        testFail("\n", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keyclone -dest p3 -alias noexist");
        // no cert
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -file x.jks.p1.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert -noprompt");
        // new pass
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -new newpass -keyclone -dest p0");
        remove("x.jks");
    }
    // keypasswd: exist, short, nonexist err, cert err, misc
    void sqeKeypasswdTest() throws Exception {
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -keypasswd -new newpass");
        /*change back*/ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass newpass -keypasswd -new changeit");
        testOK("newpass\nnewpass\n", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass changeit -keypasswd");
        /*change back*/ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass newpass -keypasswd -new changeit");
        testOK("new\nnew\nnewpass\nnewpass\n", "-keystore x.jks " +
                "-storetype JKS -storepass changeit -keypass changeit -keypasswd");
        /*change back*/ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass newpass -keypasswd -new changeit");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypasswd -new newpass");
        /*change back*/ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass newpass -keypasswd -new changeit");
        testOK("changeit\n", "-keystore x.jks -storetype JKS " +
                "-keypasswd -new newpass");
        /*change back*/ testOK("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypass newpass -keypasswd -new changeit");
        testFail("", "-keystore x.jks -storetype JKS -storepass badpass " +
                "-keypass changeit -keypasswd -new newpass");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass bad -keypasswd -new newpass");
        // no cert
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -file x.jks.p1.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert -noprompt");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -keypasswd -new newpass");
        // diff pass
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass keypass -genkeypair -keyalg DSA -dname CN=olala");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypasswd -new newpass");
        testOK("keypass\n", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -keypasswd -new newpass");
        // i hate those misc test
        remove("x.jks");
    }
    // list: -f -alias, exist, nonexist err;
    // otherwise, check all shows, -rfc shows more, and misc
    void sqeListTest() throws Exception {
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit -list");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-list -alias mykey");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-list -alias notexist");
        testFail("", "-keystore x.jks -storetype JKS -storepass badpass " +
                "-list -alias mykey");
        // keypass ignore
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass badpass -list -alias mykey");
        testOK("\n", "-keystore x.jks -storetype JKS -list");
        assertTrue(err.indexOf("WARNING") != -1, "no storepass");
        testOK("changeit\n", "-keystore x.jks -storetype JKS -list");
        assertTrue(err.indexOf("WARNING") == -1, "has storepass");
        testFail("badpass\n", "-keystore x.jks -storetype JKS -list");
        // misc
        testFail("", "-keystore aa\\bb//cc -storepass changeit -list");
        testFail("", "-keystore nonexisting -storepass changeit -list");
        testFail("", "-keystore badkeystore -storepass changeit -list");
        remove("x.jks");
    }
    // selfcert: exist, non-exist err, cert err, sig, dname, wrong keypass, misc
    void sqeSelfCertTest() throws Exception {
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit -selfcert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -selfcert");
        // not exist
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -selfcert -alias nonexisting");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -selfcert -dname CN=NewName");
        // sig not compatible
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -selfcert -sigalg MD5withRSA");
        // bad pass
        testFail("", "-keystore x.jks -storetype JKS -storepass wrong " +
                "-keypass changeit -selfcert");
        // bad pass
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass wrong -selfcert");
        //misc
        testFail("", "-keystore nonexist -storepass changeit " +
                "-keypass changeit -selfcert");
        testFail("", "-keystore aa//dd\\gg -storepass changeit " +
                "-keypass changeit -selfcert");
        // diff pass
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass keypass -genkeypair -keyalg DSA -dname CN=olala");
        testFail("", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -selfcert");
        testOK("keypass\n", "-keystore x.jks -storetype JKS " +
                "-storepass changeit -selfcert");

        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -file x.jks.p1.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert -noprompt");
        // certentry cannot do selfcert
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-selfcert");
        remove("x.jks");
    }
    // storepass: bad old, short new, misc
    void sqeStorepassTest() throws Exception {
        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        // all in arg
        testOK("", "-storepasswd -keystore x.jks -storetype JKS " +
                "-storepass changeit -new newstore");
        /* Change back */ testOK("", "-storepasswd -keystore x.jks" +
                " -storetype JKS -storepass newstore -new changeit");
        // all not in arg, new twice
        testOK("changeit\nnewstore\nnewstore\n", "-storepasswd " +
                "-keystore x.jks -storetype JKS");
        /* Change back */ testOK("", "-storepasswd -keystore x.jks " +
                "-storetype JKS -storepass newstore -new changeit");
        // new in arg
        testOK("changeit\n", "-storepasswd -keystore x.jks " +
                "-storetype JKS -new newstore");
        /* Change back */ testOK("", "-storepasswd -keystore x.jks " +
                "-storetype JKS -storepass newstore -new changeit");
        // old in arg
        testOK("newstore\nnewstore\n", "-storepasswd -keystore x.jks " +
                "-storetype JKS -storepass changeit");
        /* Change back */ testOK("", "-storepasswd -keystore x.jks " +
                "-storetype JKS -storepass newstore -new changeit");
        // old in arg
        testOK("new\nnew\nnewstore\nnewstore\n", "-storepasswd " +
                "-keystore x.jks -storetype JKS -storepass changeit");
        /* Change back */ testOK("", "-storepasswd -keystore x.jks " +
                "-storetype JKS -storepass newstore -new changeit");
        // bad old
        testFail("", "-storepasswd -keystore x.jks -storetype JKS " +
                "-storepass badold -new newstore");
        // short new
        testFail("", "-storepasswd -keystore x.jks -storetype JKS " +
                "-storepass changeit -new new");
        // misc
        // non exist
        testFail("", "-storepasswd -keystore nonexist " +
                "-storepass changeit -new newstore");
        // bad file
        testFail("", "-storepasswd -keystore badkeystore " +
                "-storepass changeit -new newstore");
        // bad file
        testFail("", "-storepasswd -keystore aa\\bb//cc//dd " +
                "-storepass changeit -new newstore");
        remove("x.jks");
    }

    void sqeGenkeyTest() throws Exception {

        remove("x.jks");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -alias newentry");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -alias newentry");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg DSA " +
                "-alias n1");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA " +
                "-alias n2");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala " +
                "-keyalg NoSuchAlg -alias n3");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -keysize 56 " +
                "-alias n4");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -keysize 999 " +
                "-alias n5");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -keysize 512 " +
                "-alias n6");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -keysize 1024 " +
                "-alias n7");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala " +
                "-sigalg NoSuchAlg -alias n8");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA " +
                "-sigalg MD2withRSA -alias n9");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA " +
                "-sigalg MD5withRSA -alias n10");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA " +
                "-sigalg SHA1withRSA -alias n11");
        testFail("", "-keystore aa\\bb//cc\\dd -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA " +
                "-sigalg NoSuchAlg -alias n12");
        testFail("", "-keystore badkeystore -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala " +
                "-alias n14");
        testFail("", "-keystore x.jks -storetype JKS -storepass badpass " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -alias n16");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CNN=olala -alias n17");
        remove("x.jks");
    }

    void sqeExportTest() throws Exception {
        remove("x.jks");
        // nonexist
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-export -file mykey.cert -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-export -file mykey.cert -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-import -file mykey.cert -noprompt -alias c1");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-export -file mykey.cert2 -alias c1");
        testFail("", "-keystore aa\\bb//cc\\dd -storepass changeit " +
                "-export -file mykey.cert2 -alias c1");
        testFail("", "-keystore nonexistkeystore -storepass changeit " +
                "-export -file mykey.cert2 -alias c1");
        testFail("", "-keystore badkeystore -storepass changeit " +
                "-export -file mykey.cert2 -alias c1");
        testFail("", "-keystore x.jks -storetype JKS -storepass badpass " +
                "-export -file mykey.cert2 -alias c1");
        remove("mykey.cert");
        remove("mykey.cert2");
        remove("x.jks");
    }

    void sqeDeleteTest() throws Exception {
        remove("x.jks");
        // nonexist
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        // keystore name illegal
        testFail("", "-keystore aa\\bb//cc\\dd -storepass changeit " +
                "-delete -alias mykey");
        // keystore not exist
        testFail("", "-keystore nonexistkeystore -storepass changeit " +
                "-delete -alias mykey");
        // keystore invalid
        testFail("", "-keystore badkeystore -storepass changeit " +
                "-delete -alias mykey");
        // wrong pass
        testFail("", "-keystore x.jks -storetype JKS -storepass xxxxxxxx " +
                "-delete -alias mykey");
        remove("x.jks");
    }

    void sqeCsrTest() throws Exception {
        remove("x.jks");
        remove("x.jks.p1.cert");
        remove("csr1");
        // PrivateKeyEntry can do certreq
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala -keysize 1024");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -sigalg SHA1withDSA");
        // unmatched sigalg
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -sigalg MD5withRSA");
        // misc test
        // bad storepass
        testFail("", "-keystore x.jks -storetype JKS -storepass badstorepass " +
                "-certreq -file csr1");
        // storepass from terminal
        testOK("changeit\n", "-keystore x.jks -storetype JKS " +
                "-certreq -file csr1");
        // must provide storepass
        testFail("\n", "-keystore x.jks -storetype JKS " +
                "-certreq -file csr1");
        // bad keypass
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass badkeypass -certreq -file csr1");
        // bad filepath
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file aa\\bb//cc\\dd");
        // non-existing keystore
        testFail("", "-keystore noexistks -storepass changeit " +
                "-certreq -file csr1");
        // Try the RSA private key
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=olala -keyalg RSA");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1");
        // unmatched sigalg
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -sigalg SHA1withDSA");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -sigalg MD5withRSA");
        // TrustedCertificateEntry cannot do certreq
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-exportcert -file x.jks.p1.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-delete -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-importcert -file x.jks.p1.cert -noprompt");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1 -alias mykey");
        testFail("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-certreq -file csr1");
        remove("x.jks");
        remove("x.jks.p1.cert");
        remove("csr1");
    }

    void sqePrintcertTest() throws Exception {
        remove("x.jks");
        remove("mykey.cert");
        remove("myweakkey.cert");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -keyalg DSA -dname CN=olala");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-export -file mykey.cert -alias mykey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -genkeypair -dname CN=weak -keyalg rsa " +
                "-keysize 512 -sigalg MD5withRSA -alias myweakkey");
        testOK("", "-keystore x.jks -storetype JKS -storepass changeit " +
                "-export -file myweakkey.cert -alias myweakkey");
        testFail("", "-printcert -file badkeystore");
        testFail("", "-printcert -file a/b/c/d");
        testOK("", "-printcert -file mykey.cert");
        testOK("", "-printcert -file myweakkey.cert");
        FileInputStream fin = new FileInputStream("mykey.cert");
        testOK(fin, "-printcert");
        fin.close();
        remove("x.jks");
        remove("mykey.cert");
        remove("myweakkey.cert");
    }

    // 8074935: jdk8 keytool doesn't validate pem files for RFC 1421 correctness
    static void checkPem(String file) throws Exception {
        boolean maybeLast = false;
        for (String s: Files.readAllLines(Paths.get(file))) {
            if (s.isEmpty()) continue;
            if (s.startsWith("---")) continue;
            if (maybeLast) {
                throw new Exception("Last line already seen");
            }
            if (s.length() > 64) {
                throw new Exception(s);
            }
            if (s.length() < 64) {
                maybeLast = true;
            }
        }
    }

    void v3extTest(String keyAlg) throws Exception {
        KeyStore ks;
        remove("x.jks");
        String simple = "-keystore x.jks -storetype JKS -storepass changeit " +
                "-keypass changeit -noprompt -keyalg " + keyAlg + " ";
        String pre = simple + "-genkeypair -keyalg DSA -dname CN=Olala -alias ";

        // Version and SKID
        testOK("", pre + "o1");

        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(((X509Certificate)ks.getCertificate("o1")).getVersion() == 3);
        assertTrue(((X509CertImpl)ks.getCertificate("o1"))
                .getSubjectKeyIdentifierExtension() != null);

        // BC
        testOK("", pre + "b1 -ext BC:critical");
        testOK("", pre + "b2 -ext BC");
        testOK("", pre + "b3 -ext bc");
        testOK("", pre + "b4 -ext BasicConstraints");
        testOK("", pre + "b5 -ext basicconstraints");
        testOK("", pre + "b6 -ext BC=ca:true,pathlen:12");
        testOK("", pre + "b7 -ext BC=ca:false");
        testOK("", pre + "b8 -ext BC:critical=ca:false");
        testOK("", pre + "b9 -ext BC=12");

        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(((X509CertImpl)ks.getCertificate("b1"))
                .getBasicConstraintsExtension().isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("b2"))
                .getBasicConstraintsExtension().isCritical());
        assertTrue(((X509CertImpl)ks.getCertificate("b8"))
                .getBasicConstraintsExtension().isCritical());
        assertTrue(((X509Certificate)ks.getCertificate("b1"))
                .getBasicConstraints() == Integer.MAX_VALUE);
        assertTrue(((X509Certificate)ks.getCertificate("b2"))
                .getBasicConstraints() == Integer.MAX_VALUE);
        assertTrue(((X509Certificate)ks.getCertificate("b3"))
                .getBasicConstraints() == Integer.MAX_VALUE);
        assertTrue(((X509Certificate)ks.getCertificate("b4"))
                .getBasicConstraints() == Integer.MAX_VALUE);
        assertTrue(((X509Certificate)ks.getCertificate("b5"))
                .getBasicConstraints() == Integer.MAX_VALUE);
        assertTrue(((X509Certificate)ks.getCertificate("b6"))
                .getBasicConstraints() == 12);
        assertTrue(((X509Certificate)ks.getCertificate("b7"))
                .getBasicConstraints() == -1);
        assertTrue(((X509Certificate)ks.getCertificate("b9"))
                .getBasicConstraints() == 12);

        // KU
        testOK("", pre + "ku1 -ext KeyUsage:critical=digitalsignature");
        testOK("", pre + "ku2 -ext KU=digitalSignature");
        testOK("", pre + "ku3 -ext KU=ds");
        testOK("", pre + "ku4 -ext KU=dig");
        // ambigous value
        testFail("", pre + "ku5 -ext KU=d");
        // cRLSign cannot be cs
        testFail("", pre + "ku6 -ext KU=cs");
        testOK("", pre + "ku11 -ext KU=nr");
        // ke means keyAgreement and keyCertSign...
        testFail("", pre + "ku12 -ext KU=ke");
        testOK("", pre + "ku12 -ext KU=keyE");
        testOK("", pre + "ku12a -ext KU=kE"); // kE is only keyEncipherment
        // de also means decipherOnly
        testOK("", pre + "ku13a -ext KU=de"); // de is decipherOnly
        testOK("", pre + "ku13b -ext KU=dE"); // dE is dataEncipherment
        testOK("", pre + "ku13 -ext KU=dataE");
        testOK("", pre + "ku14 -ext KU=ka");
        testOK("", pre + "ku15 -ext KU=kcs");
        testOK("", pre + "ku16 -ext KU=crls");
        testOK("", pre + "ku17 -ext KU=eo");
        testOK("", pre + "ku18 -ext KU=do");
        testOK("", pre + "ku19 -ext KU=cc");

        testOK("", pre + "ku017 -ext KU=ds,cc,eo");
        testOK("", pre + "ku135 -ext KU=nr,dataEncipherment,keyCertSign");
        testOK("", pre + "ku246 -ext KU=keyEnc,cRL,keyA");
        testOK("", pre + "ku1234 -ext KU=ka,da,keyE,nonR");

        ks = loadStore("x.jks", "changeit", "JKS");
        class CheckKU {
            void check(KeyStore ks, String alias, int... pos) throws Exception {
                System.err.print("x");
                boolean[] bs = ((X509Certificate)ks.getCertificate(alias))
                        .getKeyUsage();
                bs = Arrays.copyOf(bs, 9);
                for (int i=0; i<bs.length; i++) {
                    boolean found = false;
                    for (int p: pos) {
                        if (p == i) found = true;
                    }
                    if (!found ^ bs[i]) {
                        // OK
                    } else {
                        throw new RuntimeException("KU not match at " + i +
                                ": " + found + " vs " + bs[i]);
                    }
                }
            }
        }
        CheckKU c = new CheckKU();
        assertTrue(((X509CertImpl)ks.getCertificate("ku1"))
                .getExtension(PKIXExtensions.KeyUsage_Id).isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("ku2"))
                .getExtension(PKIXExtensions.KeyUsage_Id).isCritical());
        c.check(ks, "ku1", 0);
        c.check(ks, "ku2", 0);
        c.check(ks, "ku3", 0);
        c.check(ks, "ku4", 0);
        c.check(ks, "ku11", 1);
        c.check(ks, "ku12", 2);
        c.check(ks, "ku13", 3);
        c.check(ks, "ku14", 4);
        c.check(ks, "ku15", 5);
        c.check(ks, "ku16", 6);
        c.check(ks, "ku17", 7);
        c.check(ks, "ku18", 8);
        c.check(ks, "ku19", 1);
        c.check(ks, "ku11", 1);
        c.check(ks, "ku11", 1);
        c.check(ks, "ku11", 1);
        c.check(ks, "ku017", 0, 1, 7);
        c.check(ks, "ku135", 1, 3, 5);
        c.check(ks, "ku246", 6, 2, 4);
        c.check(ks, "ku1234", 1, 2, 3, 4);

        // EKU
        testOK("", pre + "eku1 -ext EKU:critical=sa");
        testOK("", pre + "eku2 -ext ExtendedKeyUsage=ca");
        testOK("", pre + "eku3 -ext EKU=cs");
        testOK("", pre + "eku4 -ext EKU=ep");
        testOK("", pre + "eku8 -ext EKU=ts");
        testFail("", pre + "eku9 -ext EKU=os");
        testOK("", pre + "eku9 -ext EKU=ocsps");
        testOK("", pre + "eku10 -ext EKU=any");
        testOK("", pre + "eku11 -ext EKU=1.2.3.4,1.3.5.7,ep");
        testFail("", pre + "eku12 -ext EKU=c");
        testFail("", pre + "eku12 -ext EKU=nothing");

        ks = loadStore("x.jks", "changeit", "JKS");
        class CheckEKU {
            void check(KeyStore ks, String alias, String... pos) throws Exception {
                System.err.print("x");
                List<String> bs = ((X509Certificate)ks.getCertificate(alias))
                        .getExtendedKeyUsage();
                int found = 0;
                for (String p: pos) {
                    if (bs.contains(p)) {
                        found++;
                    } else {
                        throw new RuntimeException("EKU: not included " + p);
                    }
                }
                if (found != bs.size()) {
                    throw new RuntimeException("EKU: more items than expected");
                }
            }
        }
        CheckEKU cx = new CheckEKU();
        assertTrue(((X509CertImpl)ks.getCertificate("eku1"))
                .getExtension(PKIXExtensions.ExtendedKeyUsage_Id).isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("eku2"))
                .getExtension(PKIXExtensions.ExtendedKeyUsage_Id).isCritical());
        cx.check(ks, "eku1", "1.3.6.1.5.5.7.3.1");
        cx.check(ks, "eku2", "1.3.6.1.5.5.7.3.2");
        cx.check(ks, "eku3", "1.3.6.1.5.5.7.3.3");
        cx.check(ks, "eku4", "1.3.6.1.5.5.7.3.4");
        cx.check(ks, "eku8", "1.3.6.1.5.5.7.3.8");
        cx.check(ks, "eku9", "1.3.6.1.5.5.7.3.9");
        cx.check(ks, "eku10", "2.5.29.37.0");
        cx.check(ks, "eku11", "1.3.6.1.5.5.7.3.4", "1.2.3.4", "1.3.5.7");

        // SAN
        testOK("", pre+"san1 -ext san:critical=email:me@me.org");
        testOK("", pre+"san2 -ext san=uri:http://me.org");
        testOK("", pre+"san3 -ext san=dns:me.org");
        testOK("", pre+"san4 -ext san=ip:192.168.0.1");
        testOK("", pre+"san5 -ext san=oid:1.2.3.4");
        testOK("", pre+"san6 -ext san=dns:1abc.com"); //begin with digit
        testOK("", pre+"san235 -ext san=uri:http://me.org,dns:me.org,oid:1.2.3.4");

        ks = loadStore("x.jks", "changeit", "JKS");
        class CheckSAN {
            // Please sort items with name type
            void check(KeyStore ks, String alias, int type, Object... items)
                    throws Exception {
                int pos = 0;
                System.err.print("x");
                Object[] names = null;
                if (type == 0) names = ((X509Certificate)ks.getCertificate(alias))
                        .getSubjectAlternativeNames().toArray();
                else names = ((X509Certificate)ks.getCertificate(alias))
                        .getIssuerAlternativeNames().toArray();
                Arrays.sort(names, new Comparator() {
                    public int compare(Object o1, Object o2) {
                        int i1 = (Integer)((List)o1).get(0);
                        int i2 = (Integer)((List)o2).get(0);
                        return i1 - i2;
                    }
                });
                for (Object o: names) {
                    List l = (List)o;
                    for (Object o2: l) {
                        if (!items[pos++].equals(o2)) {
                            throw new RuntimeException("Not equals at " + pos
                                    + ": " + items[pos-1] + " vs " + o2);
                        }
                    }
                }
                if (pos != items.length) {
                    throw new RuntimeException("Extra items, pos is " + pos);
                }
            }
        }
        CheckSAN csan = new CheckSAN();
        assertTrue(((X509CertImpl)ks.getCertificate("san1"))
                .getSubjectAlternativeNameExtension().isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("san2"))
                .getSubjectAlternativeNameExtension().isCritical());
        csan.check(ks, "san1", 0, 1, "me@me.org");
        csan.check(ks, "san2", 0, 6, "http://me.org");
        csan.check(ks, "san3", 0, 2, "me.org");
        csan.check(ks, "san4", 0, 7, "192.168.0.1");
        csan.check(ks, "san5", 0, 8, "1.2.3.4");
        csan.check(ks, "san235", 0, 2, "me.org", 6, "http://me.org", 8, "1.2.3.4");

        // IAN
        testOK("", pre+"ian1 -ext ian:critical=email:me@me.org");
        testOK("", pre+"ian2 -ext ian=uri:http://me.org");
        testOK("", pre+"ian3 -ext ian=dns:me.org");
        testOK("", pre+"ian4 -ext ian=ip:192.168.0.1");
        testOK("", pre+"ian5 -ext ian=oid:1.2.3.4");
        testOK("", pre+"ian235 -ext ian=uri:http://me.org,dns:me.org,oid:1.2.3.4");

        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(((X509CertImpl)ks.getCertificate("ian1"))
                .getIssuerAlternativeNameExtension().isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("ian2"))
                .getIssuerAlternativeNameExtension().isCritical());
        csan.check(ks, "ian1", 1, 1, "me@me.org");
        csan.check(ks, "ian2", 1, 6, "http://me.org");
        csan.check(ks, "ian3", 1, 2, "me.org");
        csan.check(ks, "ian4", 1, 7, "192.168.0.1");
        csan.check(ks, "ian5", 1, 8, "1.2.3.4");
        csan.check(ks, "ian235", 1, 2, "me.org", 6, "http://me.org", 8, "1.2.3.4");

        // SIA
        testOK("", pre+"sia1 -ext sia=care:uri:ldap://ca.com/cn=CA");
        testOK("", pre+"sia2 -ext sia=ts:email:ts@ca.com");
        testFail("SIA never critical", pre +
                "sia3 -ext sia:critical=ts:email:ts@ca.com");

        ks = loadStore("x.jks", "changeit", "JKS");
        class CheckSia {
            void check(KeyStore ks, String alias, int type, Object... items)
                    throws Exception {
                int pos = 0;
                System.err.print("x");
                AccessDescription[] ads = null;
                if (type == 0) {
                    SubjectInfoAccessExtension siae = (SubjectInfoAccessExtension)
                            ((X509CertImpl)ks.getCertificate(alias))
                            .getExtension(PKIXExtensions.SubjectInfoAccess_Id);
                    ads = siae.getAccessDescriptions()
                            .toArray(new AccessDescription[0]);
                } else {
                    AuthorityInfoAccessExtension aiae =
                            (AuthorityInfoAccessExtension)
                            ((X509CertImpl)ks.getCertificate(alias))
                            .getExtension(PKIXExtensions.AuthInfoAccess_Id);
                    ads = aiae.getAccessDescriptions()
                            .toArray(new AccessDescription[0]);
                }
                Arrays.sort(ads, new Comparator<AccessDescription>() {
                    @Override
                    public int compare(AccessDescription o1,
                                       AccessDescription o2) {
                        return o1.getAccessMethod().toString()
                                .compareTo(o2.getAccessMethod().toString());
                    }
                });
                for (AccessDescription ad: ads) {
                    if (!ad.getAccessMethod().equals(items[pos++]) ||
                            !new Integer(ad.getAccessLocation().getType())
                                    .equals(items[pos++])) {
                        throw new RuntimeException("Not same type at " + pos);
                    }
                    String name = null;
                    switch (ad.getAccessLocation().getType()) {
                        case 1:
                            name = ((RFC822Name)ad.getAccessLocation()
                                    .getName()).getName();
                            break;
                        case 6:
                            name = ((URIName)ad.getAccessLocation()
                                    .getName()).getURI().toString();
                            break;
                        default:
                            throw new RuntimeException("Not implemented: " + ad);
                    }
                    if (!name.equals(items[pos++])) {
                        throw new Exception("Name not same for " + ad +
                                " at pos " + pos);
                    }
                }
            }
        }
        CheckSia csia = new CheckSia();
        assertTrue(!((X509CertImpl)ks.getCertificate("sia1"))
                .getExtension(PKIXExtensions.SubjectInfoAccess_Id).isCritical());
        csia.check(ks, "sia1", 0,
                AccessDescription.Ad_CAREPOSITORY_Id, 6, "ldap://ca.com/cn=CA");
        csia.check(ks, "sia2",
                0, AccessDescription.Ad_TIMESTAMPING_Id, 1, "ts@ca.com");

        // AIA
        testOK("", pre+"aia1 -ext aia=cai:uri:ldap://ca.com/cn=CA");
        testOK("", pre+"aia2 -ext aia=ocsp:email:ocsp@ca.com");
        testFail("AIA never critical", pre +
                "aia3 -ext aia:critical=ts:email:ts@ca.com");

        ks = loadStore("x.jks", "changeit", "JKS");
        assertTrue(!((X509CertImpl)ks.getCertificate("aia1"))
                .getExtension(PKIXExtensions.AuthInfoAccess_Id).isCritical());
        csia.check(ks, "aia1", 1,
                AccessDescription.Ad_CAISSUERS_Id, 6, "ldap://ca.com/cn=CA");
        csia.check(ks, "aia2", 1,
                AccessDescription.Ad_OCSP_Id, 1, "ocsp@ca.com");

        // OID
        testOK("", pre+"oid1 -ext 1.2.3:critical=0102");
        testOK("", pre+"oid2 -ext 1.2.3");
        testOK("", pre+"oid12 -ext 1.2.3 -ext 1.2.4=01:02:03");

        ks = loadStore("x.jks", "changeit", "JKS");
        class CheckOid {
            void check(KeyStore ks, String alias, String oid, byte[] value)
                    throws Exception {
                int pos = 0;
                System.err.print("x");
                Extension ex = ((X509CertImpl)ks.getCertificate(alias))
                        .getExtension(new ObjectIdentifier(oid));
                if (!Arrays.equals(value, ex.getValue())) {
                    throw new RuntimeException("Not same content in " +
                            alias + " for " + oid);
                }
            }
        }
        CheckOid coid = new CheckOid();
        assertTrue(((X509CertImpl)ks.getCertificate("oid1"))
                .getExtension(new ObjectIdentifier("1.2.3")).isCritical());
        assertTrue(!((X509CertImpl)ks.getCertificate("oid2"))
                .getExtension(new ObjectIdentifier("1.2.3")).isCritical());
        coid.check(ks, "oid1", "1.2.3", new byte[]{1,2});
        coid.check(ks, "oid2", "1.2.3", new byte[]{});
        coid.check(ks, "oid12", "1.2.3", new byte[]{});
        coid.check(ks, "oid12", "1.2.4", new byte[]{1,2,3});

        // honored
        testOK("", pre+"ca");
        testOK("", pre+"a");
        // request: BC,KU,1.2.3,1.2.4,1.2.5
        testOK("", simple+"-alias a -certreq " +
                "-ext BC=1 -ext KU=crl " +
                "-ext 1.2.3=01 -ext 1.2.4:critical=0102 -ext 1.2.5=010203 " +
                "-rfc -file test.req");
        // printcertreq
        testOK("", "-printcertreq -file test.req");
        checkPem("test.req");
        // issue: deny KU, change criticality of 1.2.3 and 1.2.4,
        // change content of BC, add 2.3.4
        testOK("", simple+"-gencert -alias ca -infile test.req -ext " +
                "honored=all,-KU,1.2.3:critical,1.2.4:non-critical " +
                "-ext BC=2 -ext 2.3.4=01020304 " +
                "-debug -rfc -outfile test.cert");
        checkPem("test.cert");
        testOK("", simple+"-importcert -file test.cert -alias a");
        ks = loadStore("x.jks", "changeit", "JKS");
        X509CertImpl a = (X509CertImpl)ks.getCertificate("a");
        assertTrue(a.getAuthorityKeyIdentifierExtension() != null);
        assertTrue(a.getSubjectKeyIdentifierExtension() != null);
        assertTrue(a.getKeyUsage() == null);
        assertTrue(a.getExtension(new ObjectIdentifier("1.2.3")).isCritical());
        assertTrue(!a.getExtension(new ObjectIdentifier("1.2.4")).isCritical());
        assertTrue(!a.getExtension(new ObjectIdentifier("1.2.5")).isCritical());
        assertTrue(a.getExtensionValue("1.2.3").length == 3);
        assertTrue(a.getExtensionValue("1.2.4").length == 4);
        assertTrue(a.getExtensionValue("1.2.5").length == 5);
        assertTrue(a.getBasicConstraints() == 2);
        assertTrue(!a.getExtension(new ObjectIdentifier("2.3.4")).isCritical());
        assertTrue(a.getExtensionValue("2.3.4").length == 6);

        // 8073181: keytool -ext honored not working correctly
        testOK("", simple+"-gencert -alias ca -infile test.req -ext " +
                "honored=1.2.3,KU,1.2.4:critical " +
                "-debug -rfc -outfile test2.cert");
        testOK("", simple+"-importcert -file test2.cert -alias b");
        ks = loadStore("x.jks", "changeit", "JKS");
        X509CertImpl b = (X509CertImpl)ks.getCertificate("b");
        assertTrue(!b.getExtension(new ObjectIdentifier("1.2.3")).isCritical());
        assertTrue(b.getExtension(new ObjectIdentifier("1.2.4")).isCritical());

        // 8073182: keytool may generate duplicate extensions
        testOK("", pre+"dup -ext bc=2 -ext 2.5.29.19=30030101FF -ext bc=3");
        ks = loadStore("x.jks", "changeit", "JKS");
        X509CertImpl dup = (X509CertImpl)ks.getCertificate("dup");
        assertTrue(dup.getBasicConstraints() == 3);

        remove("x.jks");
        remove("test.req");
        remove("test.cert");
    }

    void i18nTest() throws Exception {
        //   1.  keytool -help
        remove("x.jks");
        testOK("", "-help");

        //   2. keytool -genkey -keyalg DSA -v -keysize 512 Enter "a" for the keystore
        // password. Check error (password too short). Enter "password" for
        // the keystore password. Hit 'return' for "first and last name",
        // "organizational unit", "City", "State", and "Country Code".
        // Type "yes" when they ask you if everything is correct.
        // Type 'return' for new key password.
        testOK("a\npassword\npassword\nMe\nHere\nNow\nPlace\nPlace\nUS\nyes\n\n",
                "-genkey -keyalg DSA -v -keysize 512 -keystore x.jks -storetype JKS");
        //   3. keytool -list -v -storepass password
        testOK("", "-list -v -storepass password -keystore x.jks -storetype JKS");
        //   4. keytool -list -v Type "a" for the keystore password.
        // Check error (wrong keystore password).
        testFail("a\n", "-list -v -keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("password was incorrect") != -1);
        //   5. keytool - -keyalg DSA -v -keysize 512 Enter "password" as the password.
        // Check error (alias 'mykey' already exists).
        testFail("password\n", "-genkey -keyalg DSA -v -keysize 512" +
                " -keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("alias <mykey> already exists") != -1);
        //   6. keytool -genkey -keyalg DSA -v -keysize 512 -alias mykey2 -storepass password
        // Hit 'return' for "first and last name", "organizational unit", "City",
        // "State", and "Country Code". Type "yes" when they ask you if
        // everything is correct. Type 'return' for new key password.
        testOK("\n\n\n\n\n\nyes\n\n", "-genkey -keyalg DSA -v -keysize 512 -alias mykey2" +
                " -storepass password -keystore x.jks -storetype JKS");
        //   7. keytool -list -v Type 'password' for the store password.
        testOK("password\n", "-list -v -keystore x.jks -storetype JKS");
        //   8. keytool -keypasswd -v -alias mykey2 -storepass password
        // Type "a" for the new key password. Type "aaaaaa" for the new key
        // password. Type "bbbbbb" when re-entering the new key password.
        // Type "a" for the new key password. Check Error (too many failures).
        testFail("a\naaaaaa\nbbbbbb\na\n", "-keypasswd -v -alias mykey2" +
                " -storepass password -keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("Too many failures - try later") != -1);
        //   9. keytool -keypasswd -v -alias mykey2 -storepass password
        // Type "aaaaaa" for the new key password. Type "aaaaaa"
        // when re-entering the new key password.
        testOK("aaaaaa\naaaaaa\n", "-keypasswd -v -alias mykey2 " +
                "-storepass password -keystore x.jks -storetype JKS");
        //  10. keytool -selfcert -v -alias mykey -storepass password
        testOK("", "-selfcert -v -alias mykey -storepass password " +
                "-keystore x.jks -storetype JKS");
        //  11. keytool -list -v -storepass password
        testOK("", "-list -v -storepass password -keystore x.jks -storetype JKS");
        //  12. keytool -export -v -alias mykey -file cert -storepass password
        remove("cert");
        testOK("", "-export -v -alias mykey -file cert -storepass password " +
                "-keystore x.jks -storetype JKS");
        //  13. keytool -import -v -file cert -storepass password
        // Check error (Certificate reply and cert are the same)
        testFail("", "-import -v -file cert -storepass password" +
                " -keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("Certificate reply and certificate" +
                " in keystore are identical") != -1);
        //  14. keytool -printcert -file cert
        testOK("", "-printcert -file cert -keystore x.jks -storetype JKS");
        remove("cert");
        //  15. keytool -list -storepass password -addprovider SUN
        testOK("", "-list -storepass password" +
                " -addprovider SUN" +
                " -keystore x.jks -storetype JKS");

        //Error tests

        //   1. keytool -storepasswd -storepass password -new abc
        // Check error (password too short)
        testFail("", "-storepasswd -storepass password -new abc");
        assertTrue(ex.indexOf("New password must be at least 6 characters") != -1);
        // Changed, no NONE needed now
        //   2. keytool -list -storetype PKCS11 Check error (-keystore must be NONE)
        //testFail("", "-list -storetype PKCS11");
        //assertTrue(err.indexOf("keystore must be NONE") != -1);
        //   3. keytool -storepasswd -storetype PKCS11 -keystore NONE
        // Check error (unsupported operation)
        testFail("", "-storepasswd -storetype PKCS11 -keystore NONE");
        assertTrue(ex.indexOf("UnsupportedOperationException") != -1);
        //   4. keytool -keypasswd -storetype PKCS11 -keystore NONE
        // Check error (unsupported operation)
        testFail("", "-keypasswd -storetype PKCS11 -keystore NONE");
        assertTrue(ex.indexOf("UnsupportedOperationException") != -1);
        //   5. keytool -list -protected -storepass password
        // Check error (password can not be specified with -protected)
        testFail("", "-list -protected -storepass password " +
                "-keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("if -protected is specified, then") != -1);
        //   6. keytool -keypasswd -protected -keypass password
        // Check error (password can not be specified with -protected)
        testFail("", "-keypasswd -protected -keypass password " +
                "-keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("if -protected is specified, then") != -1);
        //   7. keytool -keypasswd -protected -new password
        // Check error (password can not be specified with -protected)
        testFail("", "-keypasswd -protected -new password " +
                "-keystore x.jks -storetype JKS");
        assertTrue(ex.indexOf("if -protected is specified, then") != -1);
        remove("x.jks");
    }

    void i18nPKCS11Test() throws Exception {
        //PKCS#11 tests

        //   1. sccs edit cert8.db key3.db
        //Runtime.getRuntime().exec("/usr/bin/sccs edit cert8.db key3.db");
        testOK("", p11Arg + ("-storepass test12 -genkey -alias genkey" +
                " -dname cn=genkey -keysize 512 -keyalg rsa"));
        testOK("", p11Arg + "-storepass test12 -list");
        testOK("", p11Arg + "-storepass test12 -list -alias genkey");
        testOK("", p11Arg +
                "-storepass test12 -certreq -alias genkey -file genkey.certreq");
        testOK("", p11Arg +
                "-storepass test12 -export -alias genkey -file genkey.cert");
        testOK("", "-printcert -file genkey.cert");
        testOK("", p11Arg +
                "-storepass test12 -selfcert -alias genkey -dname cn=selfCert");
        testOK("", p11Arg +
                "-storepass test12 -list -alias genkey -v");
        assertTrue(out.indexOf("Owner: CN=selfCert") != -1);
        //(check that cert subject DN is [cn=selfCert])
        testOK("", p11Arg + "-storepass test12 -delete -alias genkey");
        testOK("", p11Arg + "-storepass test12 -list");
        assertTrue(out.indexOf("Your keystore contains 0 entries") != -1);
        //(check for empty database listing)
        //Runtime.getRuntime().exec("/usr/bin/sccs unedit cert8.db key3.db");
        remove("genkey.cert");
        remove("genkey.certreq");
        //  12. sccs unedit cert8.db key3.db
    }

    // tesing new option -srcProviderName
    void sszzTest() throws Exception {
        testAnyway("", NSS_P11_ARG+"-delete -alias nss -storepass test12");
        testAnyway("", NZZ_P11_ARG+"-delete -alias nss -storepass test12");
        testOK("", NSS_P11_ARG+"-genkeypair -keyalg DSA -dname CN=NSS " +
                "-alias nss -storepass test12");
        testOK("", NSS_SRC_P11_ARG + NZZ_P11_ARG +
                "-importkeystore -srcstorepass test12 -deststorepass test12");
        testAnyway("", NSS_P11_ARG+"-delete -alias nss -storepass test12");
        testAnyway("", NZZ_P11_ARG+"-delete -alias nss -storepass test12");
    }

    public static void main(String[] args) throws Exception {
        Locale reservedLocale = Locale.getDefault();
        try {
            // first test if HumanInputStream really acts like a human being
            HumanInputStream.test();
            KeyToolTest t = new KeyToolTest();

            if (System.getProperty("file") != null) {
                t.sqeTest();
                t.testAll();
                t.i18nTest();
                t.v3extTest("RSA");
                t.v3extTest("DSA");
                boolean testEC = true;
                try {
                    KeyPairGenerator.getInstance("EC");
                } catch (NoSuchAlgorithmException nae) {
                    testEC = false;
                }
                if (testEC) t.v3extTest("EC");
            }

            if (System.getProperty("nss") != null) {
                t.srcP11Arg = NSS_SRC_P11_ARG;
                t.p11Arg = NSS_P11_ARG;

                t.testPKCS11();

                // FAIL:
                // 1. we still don't have srcprovidername yet
                // 2. cannot store privatekey into NSS keystore
                //    java.security.KeyStoreException: sun.security.pkcs11
                //      .wrapper.PKCS11Exception: CKR_TEMPLATE_INCOMPLETE.
                //t.testPKCS11ImportKeyStore();

                t.i18nPKCS11Test();
                //FAIL: currently PKCS11-NSS does not support
                // 2 NSS KeyStores to be loaded at the same time
                //t.sszzTest();
            }

            if (System.getProperty("solaris") != null) {
                // For Solaris Cryptography Framework
                t.srcP11Arg = SUN_SRC_P11_ARG;
                t.p11Arg = SUN_P11_ARG;
                t.testPKCS11();
                t.testPKCS11ImportKeyStore();
                t.i18nPKCS11Test();
            }

            System.out.println("Test pass!!!");
        } finally {
            // restore the reserved locale
            Locale.setDefault(reservedLocale);
        }
    }
}

class TestException extends Exception {
    public TestException(String e) {
        super(e);
    }
}

/**
 * HumanInputStream tries to act like a human sitting in front of a computer
 * terminal typing on the keyboard while the keytool program is running.
 *
 * keytool has called InputStream.read() and BufferedReader.readLine() in
 * various places. a call to B.readLine() will try to buffer as much input as
 * possible. Thus, a trivial InputStream will find it impossible to feed
 * anything to I.read() after a B.readLine() call.
 *
 * This is why i create HumanInputStream, which will only send a single line
 * to B.readLine(), no more, no less, and the next I.read() can have a chance
 * to read the exact character right after "\n".
 *
 * I don't know why HumanInputStream works.
 */
class HumanInputStream extends InputStream {
    byte[] src;
    int pos;
    int length;
    boolean inLine;
    int stopIt;

    public HumanInputStream(String input) {
        src = input.getBytes();
        pos = 0;
        length = src.length;
        stopIt = 0;
        inLine = false;
    }

    // the trick: when called through read(byte[], int, int),
    // return -1 twice after "\n"

    @Override public int read() throws IOException {
        int re;
        if(pos < length) {
            re = src[pos];
            if(inLine) {
                if(stopIt > 0) {
                    stopIt--;
                    re = -1;
                } else {
                    if(re == '\n') {
                        stopIt = 2;
                    }
                    pos++;
                }
            } else {
                pos++;
            }
        } else {
            re = -1;//throw new IOException("NO MORE TO READ");
        }
        //if (re < 32) System.err.printf("[%02d]", re);
        //else System.err.printf("[%c]", (char)re);
        return re;
    }
    @Override public int read(byte[] buffer, int offset, int len) {
        inLine = true;
        try {
            int re = super.read(buffer, offset, len);
            return re;
        } catch(Exception e) {
            throw new RuntimeException("HumanInputStream error");
        } finally {
            inLine = false;
        }
    }
    @Override public int available() {
        if(pos < length) return 1;
        return 0;
    }

    // test part
    static void assertTrue(boolean bool) {
        if(!bool)
            throw new RuntimeException();
    }

    public static void test() throws Exception {

        class Tester {
            HumanInputStream is;
            BufferedReader reader;
            Tester(String s) {
                is = new HumanInputStream(s);
                reader = new BufferedReader(new InputStreamReader(is));
            }

            // three kinds of test method
            // 1. read byte by byte from InputStream
            void testStreamReadOnce(int expection) throws Exception {
                assertTrue(is.read() == expection);
            }
            void testStreamReadMany(String expection) throws Exception {
                char[] keys = expection.toCharArray();
                for(int i=0; i<keys.length; i++) {
                    assertTrue(is.read() == keys[i]);
                }
            }
            // 2. read a line with a newly created Reader
            void testReaderReadline(String expection) throws Exception {
                String s = new BufferedReader(new InputStreamReader(is)).readLine();
                if(s == null) assertTrue(expection == null);
                else assertTrue(s.equals(expection));
            }
            // 3. read a line with the old Reader
            void testReaderReadline2(String expection) throws Exception  {
                String s = reader.readLine();
                if(s == null) assertTrue(expection == null);
                else assertTrue(s.equals(expection));
            }
        }

        Tester test;

        test = new Tester("111\n222\n\n444\n\n");
        test.testReaderReadline("111");
        test.testReaderReadline("222");
        test.testReaderReadline("");
        test.testReaderReadline("444");
        test.testReaderReadline("");
        test.testReaderReadline(null);

        test = new Tester("111\n222\n\n444\n\n");
        test.testReaderReadline2("111");
        test.testReaderReadline2("222");
        test.testReaderReadline2("");
        test.testReaderReadline2("444");
        test.testReaderReadline2("");
        test.testReaderReadline2(null);

        test = new Tester("111\n222\n\n444\n\n");
        test.testReaderReadline2("111");
        test.testReaderReadline("222");
        test.testReaderReadline2("");
        test.testReaderReadline2("444");
        test.testReaderReadline("");
        test.testReaderReadline2(null);

        test = new Tester("1\n2");
        test.testStreamReadMany("1\n2");
        test.testStreamReadOnce(-1);

        test = new Tester("12\n234");
        test.testStreamReadOnce('1');
        test.testReaderReadline("2");
        test.testStreamReadOnce('2');
        test.testReaderReadline2("34");
        test.testReaderReadline2(null);

        test = new Tester("changeit\n");
        test.testStreamReadMany("changeit\n");
        test.testReaderReadline(null);

        test = new Tester("changeit\nName\nCountry\nYes\n");
        test.testStreamReadMany("changeit\n");
        test.testReaderReadline("Name");
        test.testReaderReadline("Country");
        test.testReaderReadline("Yes");
        test.testReaderReadline(null);

        test = new Tester("Me\nHere\n");
        test.testReaderReadline2("Me");
        test.testReaderReadline2("Here");
    }
}