mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-15 08:34:30 +02:00
8233404: System property to set the number of PBE iterations in JCEKS keystores
Reviewed-by: weijun
This commit is contained in:
parent
8e709f03bb
commit
0e5a288dfe
3 changed files with 219 additions and 1 deletions
|
@ -48,6 +48,7 @@ import javax.security.auth.DestroyFailedException;
|
|||
|
||||
import sun.security.x509.AlgorithmId;
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.util.SecurityProperties;
|
||||
|
||||
/**
|
||||
* This class implements a protection mechanism for private keys. In JCE, we
|
||||
|
@ -75,14 +76,39 @@ final class KeyProtector {
|
|||
private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1";
|
||||
|
||||
private static final int MAX_ITERATION_COUNT = 5000000;
|
||||
private static final int ITERATION_COUNT = 200000;
|
||||
private static final int MIN_ITERATION_COUNT = 10000;
|
||||
private static final int DEFAULT_ITERATION_COUNT = 200000;
|
||||
private static final int SALT_LEN = 20; // the salt length
|
||||
private static final int DIGEST_LEN = 20;
|
||||
private static final int ITERATION_COUNT;
|
||||
|
||||
// the password used for protecting/recovering keys passed through this
|
||||
// key protector
|
||||
private char[] password;
|
||||
|
||||
/**
|
||||
* {@systemProperty jdk.jceks.iterationCount} property indicating the
|
||||
* number of iterations for password-based encryption (PBE) in JCEKS
|
||||
* keystores. Values in the range 10000 to 5000000 are considered valid.
|
||||
* If the value is out of this range, or is not a number, or is
|
||||
* unspecified; a default of 200000 is used.
|
||||
*/
|
||||
static {
|
||||
int iterationCount = DEFAULT_ITERATION_COUNT;
|
||||
String ic = SecurityProperties.privilegedGetOverridable(
|
||||
"jdk.jceks.iterationCount");
|
||||
if (ic != null && !ic.isEmpty()) {
|
||||
try {
|
||||
iterationCount = Integer.parseInt(ic);
|
||||
if (iterationCount < MIN_ITERATION_COUNT ||
|
||||
iterationCount > MAX_ITERATION_COUNT) {
|
||||
iterationCount = DEFAULT_ITERATION_COUNT;
|
||||
}
|
||||
} catch (NumberFormatException e) {}
|
||||
}
|
||||
ITERATION_COUNT = iterationCount;
|
||||
}
|
||||
|
||||
KeyProtector(char[] password) {
|
||||
if (password == null) {
|
||||
throw new IllegalArgumentException("password can't be null");
|
||||
|
|
|
@ -1066,6 +1066,16 @@ jdk.xml.dsig.secureValidationPolicy=\
|
|||
jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
|
||||
java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*
|
||||
|
||||
# The iteration count used for password-based encryption (PBE) in JCEKS
|
||||
# keystores. Values in the range 10000 to 5000000 are considered valid.
|
||||
# If the value is out of this range, or is not a number, or is unspecified;
|
||||
# a default of 200000 is used.
|
||||
#
|
||||
# If the system property jdk.jceks.iterationCount is also specified, it
|
||||
# supersedes the security property value defined here.
|
||||
#
|
||||
#jdk.jceks.iterationCount = 200000
|
||||
|
||||
#
|
||||
# PKCS12 KeyStore properties
|
||||
#
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Red Hat, Inc.
|
||||
*
|
||||
* 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 8233404
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=30 IterationCount HOST 200000
|
||||
* @run main/othervm/timeout=30 IterationCount HOST 200000 1
|
||||
* @run main/othervm/timeout=30 IterationCount HOST 200000 6000000
|
||||
* @run main/othervm/timeout=30 IterationCount HOST 200000 invalid
|
||||
* @run main/othervm/timeout=30 IterationCount HOST 30000 30000
|
||||
* @run main/othervm/timeout=30 IterationCount OVERRIDE
|
||||
* @author Martin Balao (mbalao@redhat.com)
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class IterationCount {
|
||||
private static final String clientStr = "CLIENT";
|
||||
private static final String javaBinPath =
|
||||
System.getProperty("java.home", ".") + File.separator + "bin" +
|
||||
File.separator + "java";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (args[0].equals("HOST")) {
|
||||
String setValue = null;
|
||||
if (args.length > 2) {
|
||||
setValue = args[2];
|
||||
}
|
||||
testSystem(args[1], setValue);
|
||||
testSecurity(args[1], setValue);
|
||||
} else if (args[0].equals(clientStr)) {
|
||||
int expectedIterationCount = Integer.parseInt(args[1]);
|
||||
int currentIterationCount = getCurrentIterationCountValue();
|
||||
System.out.println("Expected value: " + expectedIterationCount);
|
||||
System.out.println("Current value: " + currentIterationCount);
|
||||
if (currentIterationCount != expectedIterationCount) {
|
||||
throw new Exception("Expected value different than current");
|
||||
}
|
||||
} else if (args[0].equals("OVERRIDE")) {
|
||||
testSystemOverridesSecurity();
|
||||
}
|
||||
System.out.println("TEST PASS - OK");
|
||||
}
|
||||
|
||||
private static List<String> getBasicCommand() {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add(javaBinPath);
|
||||
cmd.add("-cp");
|
||||
cmd.add(System.getProperty("test.classes", "."));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static void executeCommand(List<String> cmd, String expectedCount)
|
||||
throws Throwable {
|
||||
cmd.add(IterationCount.class.getName());
|
||||
cmd.add(clientStr);
|
||||
cmd.add(expectedCount);
|
||||
OutputAnalyzer out = ProcessTools.executeCommand(
|
||||
cmd.toArray(new String[cmd.size()]));
|
||||
out.shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
private static void testSystem(String expectedCount, String setValue)
|
||||
throws Throwable {
|
||||
System.out.println("Test setting " +
|
||||
(setValue != null ? setValue : "nothing") +
|
||||
" as a System property");
|
||||
List<String> cmd = getBasicCommand();
|
||||
if (setValue != null) {
|
||||
cmd.add("-Djdk.jceks.iterationCount=" + setValue);
|
||||
}
|
||||
executeCommand(cmd, expectedCount);
|
||||
System.out.println(".............................");
|
||||
}
|
||||
|
||||
private static void testSecurity(String expectedCount, String setValue)
|
||||
throws Throwable {
|
||||
testSecurity(expectedCount, setValue, getBasicCommand());
|
||||
}
|
||||
|
||||
private static void testSecurity(String expectedCount, String setValue,
|
||||
List<String> cmd) throws Throwable {
|
||||
System.out.println("Test setting " +
|
||||
(setValue != null ? setValue : "nothing") +
|
||||
" as a Security property");
|
||||
Path tmpDirPath = Files.createTempDirectory("tmpdir");
|
||||
try {
|
||||
if (setValue != null) {
|
||||
String javaSecurityPath = tmpDirPath +
|
||||
File.separator + "java.security";
|
||||
writeJavaSecurityProp(javaSecurityPath, setValue);
|
||||
cmd.add("-Djava.security.properties=" + javaSecurityPath);
|
||||
}
|
||||
executeCommand(cmd, expectedCount);
|
||||
System.out.println(".............................");
|
||||
} finally {
|
||||
deleteDir(tmpDirPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testSystemOverridesSecurity() throws Throwable {
|
||||
System.out.println("Test that setting a System property overrides" +
|
||||
" the Security one");
|
||||
String systemValue = Integer.toString(30000);
|
||||
System.out.println("System value: " + systemValue);
|
||||
List<String> cmd = getBasicCommand();
|
||||
cmd.add("-Djdk.jceks.iterationCount=" + systemValue);
|
||||
testSecurity(systemValue, Integer.toString(40000), cmd);
|
||||
}
|
||||
|
||||
private static void writeJavaSecurityProp(String javaSecurityPath,
|
||||
String setValue) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(
|
||||
new File(javaSecurityPath))) {
|
||||
fos.write(("jdk.jceks.iterationCount=" + setValue).getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCurrentIterationCountValue() throws Exception {
|
||||
Class<?> KeyProtectorClass =
|
||||
Class.forName("com.sun.crypto.provider.KeyProtector");
|
||||
Field iterationCountField =
|
||||
KeyProtectorClass.getDeclaredField("ITERATION_COUNT");
|
||||
iterationCountField.setAccessible(true);
|
||||
return iterationCountField.getInt(KeyProtectorClass);
|
||||
}
|
||||
|
||||
private static void deleteDir(Path directory) throws IOException {
|
||||
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file,
|
||||
BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
|
||||
throws IOException {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue