提交 6d43dca5 编写于 作者: V vinnie

8008296: keytool utility doesn't support '-importpassword' command

Reviewed-by: weijun
上级 17f3c537
...@@ -72,6 +72,8 @@ import sun.security.provider.certpath.CertStoreHelper; ...@@ -72,6 +72,8 @@ import sun.security.provider.certpath.CertStoreHelper;
import sun.security.util.Password; import sun.security.util.Password;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import sun.security.pkcs.PKCS9Attribute; import sun.security.pkcs.PKCS9Attribute;
import sun.security.tools.KeyStoreUtil; import sun.security.tools.KeyStoreUtil;
...@@ -190,6 +192,10 @@ public final class Main { ...@@ -190,6 +192,10 @@ public final class Main {
KEYPASS, KEYSTORE, STOREPASS, STORETYPE, KEYPASS, KEYSTORE, STOREPASS, STORETYPE,
PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG,
PROVIDERPATH, V), PROVIDERPATH, V),
IMPORTPASS("Imports.a.password",
ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE,
STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS,
PROVIDERARG, PROVIDERPATH, V, PROTECTED),
IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore", IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore",
SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE, SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE,
DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS, DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS,
...@@ -409,6 +415,8 @@ public final class Main { ...@@ -409,6 +415,8 @@ public final class Main {
command = GENKEYPAIR; command = GENKEYPAIR;
} else if (collator.compare(flags, "-import") == 0) { } else if (collator.compare(flags, "-import") == 0) {
command = IMPORTCERT; command = IMPORTCERT;
} else if (collator.compare(flags, "-importpassword") == 0) {
command = IMPORTPASS;
} }
/* /*
* Help * Help
...@@ -727,6 +735,7 @@ public final class Main { ...@@ -727,6 +735,7 @@ public final class Main {
command != GENSECKEY && command != GENSECKEY &&
command != IDENTITYDB && command != IDENTITYDB &&
command != IMPORTCERT && command != IMPORTCERT &&
command != IMPORTPASS &&
command != IMPORTKEYSTORE && command != IMPORTKEYSTORE &&
command != PRINTCRL) { command != PRINTCRL) {
throw new Exception(rb.getString throw new Exception(rb.getString
...@@ -808,6 +817,7 @@ public final class Main { ...@@ -808,6 +817,7 @@ public final class Main {
command == GENKEYPAIR || command == GENKEYPAIR ||
command == GENSECKEY || command == GENSECKEY ||
command == IMPORTCERT || command == IMPORTCERT ||
command == IMPORTPASS ||
command == IMPORTKEYSTORE || command == IMPORTKEYSTORE ||
command == KEYCLONE || command == KEYCLONE ||
command == CHANGEALIAS || command == CHANGEALIAS ||
...@@ -958,6 +968,13 @@ public final class Main { ...@@ -958,6 +968,13 @@ public final class Main {
} }
doGenSecretKey(alias, keyAlgName, keysize); doGenSecretKey(alias, keyAlgName, keysize);
kssave = true; kssave = true;
} else if (command == IMPORTPASS) {
if (keyAlgName == null) {
keyAlgName = "PBE";
}
// password is stored as a secret key
doGenSecretKey(alias, keyAlgName, keysize);
kssave = true;
} else if (command == IDENTITYDB) { } else if (command == IDENTITYDB) {
if (filename != null) { if (filename != null) {
try (InputStream inStream = new FileInputStream(filename)) { try (InputStream inStream = new FileInputStream(filename)) {
...@@ -1419,6 +1436,43 @@ public final class Main { ...@@ -1419,6 +1436,43 @@ public final class Main {
} }
return null; // PKCS11, MSCAPI, or -protected return null; // PKCS11, MSCAPI, or -protected
} }
/*
* Prompt the user for the password credential to be stored.
*/
private char[] promptForCredential() throws Exception {
// Handle password supplied via stdin
if (System.console() == null) {
char[] importPass = Password.readPassword(System.in);
passwords.add(importPass);
return importPass;
}
int count;
for (count = 0; count < 3; count++) {
System.err.print(
rb.getString("Enter.the.password.to.be.stored."));
System.err.flush();
char[] entered = Password.readPassword(System.in);
passwords.add(entered);
System.err.print(rb.getString("Re.enter.password."));
char[] passAgain = Password.readPassword(System.in);
passwords.add(passAgain);
if (!Arrays.equals(entered, passAgain)) {
System.err.println(rb.getString("They.don.t.match.Try.again"));
continue;
}
return entered;
}
if (count == 3) {
throw new Exception(rb.getString
("Too.many.failures.key.not.added.to.keystore"));
}
return null;
}
/** /**
* Creates a new secret key. * Creates a new secret key.
*/ */
...@@ -1436,24 +1490,63 @@ public final class Main { ...@@ -1436,24 +1490,63 @@ public final class Main {
throw new Exception(form.format(source)); throw new Exception(form.format(source));
} }
// Use the keystore's default PBE algorithm for entry protection
boolean useDefaultPBEAlgorithm = true;
SecretKey secKey = null; SecretKey secKey = null;
KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);
if (keysize != -1) { if (keyAlgName.toUpperCase().startsWith("PBE")) {
keygen.init(keysize); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
} else if ("DES".equalsIgnoreCase(keyAlgName)) {
keygen.init(56); // User is prompted for PBE credential
} else if ("DESede".equalsIgnoreCase(keyAlgName)) { secKey =
keygen.init(168); factory.generateSecret(new PBEKeySpec(promptForCredential()));
// Check whether a specific PBE algorithm was specified
if (!"PBE".equalsIgnoreCase(keyAlgName)) {
useDefaultPBEAlgorithm = false;
}
if (verbose) {
MessageFormat form = new MessageFormat(rb.getString(
"Generated.keyAlgName.secret.key"));
Object[] source =
{useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()};
System.err.println(form.format(source));
}
} else { } else {
throw new Exception(rb.getString KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName);
("Please.provide.keysize.for.secret.key.generation")); if (keysize == -1) {
if ("DES".equalsIgnoreCase(keyAlgName)) {
keysize = 56;
} else if ("DESede".equalsIgnoreCase(keyAlgName)) {
keysize = 168;
} else {
throw new Exception(rb.getString
("Please.provide.keysize.for.secret.key.generation"));
}
}
keygen.init(keysize);
secKey = keygen.generateKey();
if (verbose) {
MessageFormat form = new MessageFormat(rb.getString
("Generated.keysize.bit.keyAlgName.secret.key"));
Object[] source = {new Integer(keysize),
secKey.getAlgorithm()};
System.err.println(form.format(source));
}
} }
secKey = keygen.generateKey();
if (keyPass == null) { if (keyPass == null) {
keyPass = promptForKeyPass(alias, null, storePass); keyPass = promptForKeyPass(alias, null, storePass);
} }
keyStore.setKeyEntry(alias, secKey, keyPass, null);
if (useDefaultPBEAlgorithm) {
keyStore.setKeyEntry(alias, secKey, keyPass, null);
} else {
keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey),
new KeyStore.PasswordProtection(keyPass, keyAlgName, null));
}
} }
/** /**
......
/* /*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -65,10 +65,16 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -65,10 +65,16 @@ public class Resources extends java.util.ListResourceBundle {
{"Generates.certificate.from.a.certificate.request", {"Generates.certificate.from.a.certificate.request",
"Generates certificate from a certificate request"}, //-gencert "Generates certificate from a certificate request"}, //-gencert
{"Generates.CRL", "Generates CRL"}, //-gencrl {"Generates.CRL", "Generates CRL"}, //-gencrl
{"Generated.keyAlgName.secret.key",
"Generated {0} secret key"}, //-genseckey
{"Generated.keysize.bit.keyAlgName.secret.key",
"Generated {0}-bit {1} secret key"}, //-genseckey
{"Imports.entries.from.a.JDK.1.1.x.style.identity.database", {"Imports.entries.from.a.JDK.1.1.x.style.identity.database",
"Imports entries from a JDK 1.1.x-style identity database"}, //-identitydb "Imports entries from a JDK 1.1.x-style identity database"}, //-identitydb
{"Imports.a.certificate.or.a.certificate.chain", {"Imports.a.certificate.or.a.certificate.chain",
"Imports a certificate or a certificate chain"}, //-importcert "Imports a certificate or a certificate chain"}, //-importcert
{"Imports.a.password",
"Imports a password"}, //-importpass
{"Imports.one.or.all.entries.from.another.keystore", {"Imports.one.or.all.entries.from.another.keystore",
"Imports one or all entries from another keystore"}, //-importkeystore "Imports one or all entries from another keystore"}, //-importkeystore
{"Clones.a.key.entry", {"Clones.a.key.entry",
...@@ -220,6 +226,8 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -220,6 +226,8 @@ public class Resources extends java.util.ListResourceBundle {
{"Must.specify.alias", "Must specify alias"}, {"Must.specify.alias", "Must specify alias"},
{"Keystore.password.must.be.at.least.6.characters", {"Keystore.password.must.be.at.least.6.characters",
"Keystore password must be at least 6 characters"}, "Keystore password must be at least 6 characters"},
{"Enter.the.password.to.be.stored.",
"Enter the password to be stored: "},
{"Enter.keystore.password.", "Enter keystore password: "}, {"Enter.keystore.password.", "Enter keystore password: "},
{"Enter.source.keystore.password.", "Enter source keystore password: "}, {"Enter.source.keystore.password.", "Enter source keystore password: "},
{"Enter.destination.keystore.password.", "Enter destination keystore password: "}, {"Enter.destination.keystore.password.", "Enter destination keystore password: "},
...@@ -328,6 +336,7 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -328,6 +336,7 @@ public class Resources extends java.util.ListResourceBundle {
{"New.prompt.", "New {0}: "}, {"New.prompt.", "New {0}: "},
{"Passwords.must.differ", "Passwords must differ"}, {"Passwords.must.differ", "Passwords must differ"},
{"Re.enter.new.prompt.", "Re-enter new {0}: "}, {"Re.enter.new.prompt.", "Re-enter new {0}: "},
{"Re.enter.passpword.", "Re-enter password: "},
{"Re.enter.new.password.", "Re-enter new password: "}, {"Re.enter.new.password.", "Re-enter new password: "},
{"They.don.t.match.Try.again", "They don't match. Try again"}, {"They.don.t.match.Try.again", "They don't match. Try again"},
{"Enter.prompt.alias.name.", "Enter {0} alias name: "}, {"Enter.prompt.alias.name.", "Enter {0} alias name: "},
......
/*
* Copyright (c) 2013, 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 8008296
* @summary Store and retrieve user passwords using PKCS#12 keystore
*/
import java.io.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;
/*
* Store and retrieve passwords protected by a selection of PBE algorithms,
* using a PKCS#12 keystore.
*/
public class StorePasswords {
private static final String[] PBE_ALGORITHMS = new String[] {
"default PBE algorithm",
"PBEWithMD5AndDES",
"PBEWithSHA1AndDESede",
"PBEWithSHA1AndRC2_40",
"PBEWithSHA1AndRC2_128",
"PBEWithSHA1AndRC4_40",
"PBEWithSHA1AndRC4_128",
"PBEWithHmacSHA1AndAES_128",
"PBEWithHmacSHA224AndAES_128",
"PBEWithHmacSHA256AndAES_128",
"PBEWithHmacSHA384AndAES_128",
"PBEWithHmacSHA512AndAES_128",
"PBEWithHmacSHA1AndAES_256",
"PBEWithHmacSHA224AndAES_256",
"PBEWithHmacSHA256AndAES_256",
"PBEWithHmacSHA384AndAES_256",
"PBEWithHmacSHA512AndAES_256"
};
private static final String KEYSTORE = "mykeystore.p12";
private static final char[] KEYSTORE_PWD = "changeit".toCharArray();
private static final char[] ENTRY_PWD = "protectit".toCharArray();
private static final char[] USER_PWD = "hello1".toCharArray();
public static void main(String[] args) throws Exception {
new File(KEYSTORE).delete();
int storeCount = store();
int recoverCount = recover();
if (recoverCount != storeCount) {
throw new Exception("Stored " + storeCount + " user passwords, " +
"recovered " + recoverCount + " user passwords");
}
System.out.println("\nStored " + storeCount + " user passwords, " +
"recovered " + recoverCount + " user passwords");
}
private static int store() throws Exception {
int count = 0;
// Load an empty PKCS#12 keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
System.out.println("\nLoading PKCS#12 keystore...");
keystore.load(null, null);
// Derive a PBE key from the password
PBEKeySpec keySpec = new PBEKeySpec(USER_PWD);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE");
SecretKey key = factory.generateSecret(keySpec);
PBEParameterSpec specWithEightByteSalt =
new PBEParameterSpec("NaClNaCl".getBytes(), 1024);
// Store the user password in a keystore entry (for each algorithm)
for (String algorithm : PBE_ALGORITHMS) {
try {
System.out.println("Storing user password '" +
new String(USER_PWD) + "' (protected by " + algorithm +
")");
if (algorithm.equals("default PBE algorithm")) {
keystore.setKeyEntry(
"this entry is protected by " + algorithm, key,
ENTRY_PWD, null);
} else {
keystore.setEntry(
"this entry is protected by " + algorithm,
new KeyStore.SecretKeyEntry(key),
new KeyStore.PasswordProtection(ENTRY_PWD, algorithm,
null));
}
count++;
} catch (KeyStoreException e) {
Throwable inner = e.getCause();
if (inner instanceof UnrecoverableKeyException) {
Throwable inner2 = inner.getCause();
if (inner2 instanceof InvalidAlgorithmParameterException) {
System.out.println("...re-trying due to: " +
inner2.getMessage());
// Some PBE algorithms demand an 8-byte salt
keystore.setEntry(
"this entry is protected by " + algorithm,
new KeyStore.SecretKeyEntry(key),
new KeyStore.PasswordProtection(ENTRY_PWD,
algorithm, specWithEightByteSalt));
count++;
} else if (inner2 instanceof InvalidKeyException) {
System.out.println("...skipping due to: " +
inner2.getMessage());
// Unsupported crypto keysize
continue;
}
} else {
throw e;
}
}
}
// Store the PKCS#12 keystore
System.out.println("Storing PKCS#12 keystore to: " + KEYSTORE);
keystore.store(new FileOutputStream(KEYSTORE), KEYSTORE_PWD);
return count;
}
private static int recover() throws Exception {
int count = 0;
// Load the PKCS#12 keystore
KeyStore keystore = KeyStore.getInstance("PKCS12");
System.out.println("\nLoading PKCS#12 keystore from: " + KEYSTORE);
keystore.load(new FileInputStream(KEYSTORE), KEYSTORE_PWD);
SecretKey key;
SecretKeyFactory factory;
PBEKeySpec keySpec;
// Retrieve each user password from the keystore
for (String algorithm : PBE_ALGORITHMS) {
key = (SecretKey) keystore.getKey("this entry is protected by " +
algorithm, ENTRY_PWD);
if (key != null) {
count++;
factory = SecretKeyFactory.getInstance(key.getAlgorithm());
keySpec =
(PBEKeySpec) factory.getKeySpec(key, PBEKeySpec.class);
char[] pwd = keySpec.getPassword();
System.out.println("Recovered user password '" +
new String(pwd) + "' (protected by " + algorithm + ")");
if (!Arrays.equals(USER_PWD, pwd)) {
throw new Exception("Failed to recover the user password " +
"protected by " + algorithm);
}
}
}
return count;
}
}
#
# Copyright (c) 2013, 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 8008296
# @summary confirm that keytool correctly imports user passwords
#
# @run shell StorePasswordsByShell.sh
# set a few environment variables so that the shell-script can run stand-alone
# in the source directory
if [ "${TESTSRC}" = "" ] ; then
TESTSRC="."
fi
if [ "${TESTCLASSES}" = "" ] ; then
TESTCLASSES="."
fi
if [ "${TESTJAVA}" = "" ] ; then
echo "TESTJAVA not set. Test cannot execute."
echo "FAILED!!!"
exit 1
fi
# set platform-dependent variables
OS=`uname -s`
case "$OS" in
SunOS )
PATHSEP=":"
FILESEP="/"
;;
Linux )
PATHSEP=":"
FILESEP="/"
;;
Darwin )
PATHSEP=":"
FILESEP="/"
;;
CYGWIN* )
PATHSEP=";"
FILESEP="/"
;;
Windows* )
PATHSEP=";"
FILESEP="\\"
;;
* )
echo "Unrecognized system!"
exit 1;
;;
esac
PBE_ALGORITHMS="\
default-PBE-algorithm \
PBEWithMD5AndDES \
PBEWithSHA1AndDESede \
PBEWithSHA1AndRC2_40 \
PBEWithSHA1AndRC2_128
PBEWithSHA1AndRC4_40 \
PBEWithSHA1AndRC4_128 \
PBEWithHmacSHA1AndAES_128 \
PBEWithHmacSHA224AndAES_128 \
PBEWithHmacSHA256AndAES_128 \
PBEWithHmacSHA384AndAES_128 \
PBEWithHmacSHA512AndAES_128 \
PBEWithHmacSHA1AndAES_256 \
PBEWithHmacSHA224AndAES_256 \
PBEWithHmacSHA256AndAES_256 \
PBEWithHmacSHA384AndAES_256 \
PBEWithHmacSHA512AndAES_256"
USER_PWD="hello1\n"
ALIAS_PREFIX="this entry is protected by "
COUNTER=0
# cleanup
rm mykeystore.p12 > /dev/null 2>&1
echo
for i in $PBE_ALGORITHMS; do
if [ $i = "default-PBE-algorithm" ]; then
KEYALG=""
else
KEYALG="-keyalg ${i}"
fi
if [ $COUNTER -lt 5 ]; then
IMPORTPASSWORD="-importpassword"
else
IMPORTPASSWORD="-importpass"
fi
echo "Storing user password (protected by ${i})"
echo "${USER_PWD}" | \
${TESTJAVA}${FILESEP}bin${FILESEP}keytool ${IMPORTPASSWORD} \
-storetype pkcs12 -keystore mykeystore.p12 -storepass changeit \
-alias "${ALIAS_PREFIX}${i}" ${KEYALG} > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo Error
else
echo OK
COUNTER=`expr ${COUNTER} + 1`
fi
done
echo
COUNTER2=`${TESTJAVA}${FILESEP}bin${FILESEP}keytool -list -storetype pkcs12 \
-keystore mykeystore.p12 -storepass changeit | grep -c "${ALIAS_PREFIX}"`
RESULT="stored ${COUNTER} user passwords, detected ${COUNTER2} user passwords"
if [ $COUNTER -ne $COUNTER2 -o $COUNTER -lt 11 ]; then
echo "ERROR: $RESULT"
exit 1
else
echo "OK: $RESULT"
exit 0
fi
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册