Skip to content

Commit

Permalink
8280890: Cannot use '-Djava.system.class.loader' with class loader in…
Browse files Browse the repository at this point in the history
… signed JAR

Reviewed-by: weijun, hchao
  • Loading branch information
seanjmullan committed Feb 7, 2022
1 parent 22a1a32 commit a0f6f24
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 44 deletions.
13 changes: 0 additions & 13 deletions src/java.base/share/classes/sun/security/tools/keytool/Main.java
Expand Up @@ -47,8 +47,6 @@
import java.security.spec.ECParameterSpec;
import java.text.Collator;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BiFunction;
import java.util.jar.JarEntry;
Expand Down Expand Up @@ -4926,17 +4924,6 @@ private void checkWeakConstraint(String label, String sigAlg, Key key,
"Unable.to.parse.denyAfter.string.in.exception.message"));
}

SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
Date dateObj = null;
try {
dateObj = formatter.parse(denyAfterDate);
} catch (ParseException e2) {
throw new Exception(rb.getString(
"Unable.to.parse.denyAfter.string.in.exception.message"));
}
formatter = new SimpleDateFormat("yyyy-MM-dd");
denyAfterDate = formatter.format(dateObj);

weakWarnings.add(String.format(
rb.getString("whose.sigalg.usagesignedjar"), label, sigAlg,
denyAfterDate));
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2022, 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
Expand Down Expand Up @@ -39,9 +39,12 @@
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -51,7 +54,6 @@
import java.util.Set;
import java.util.Collection;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

Expand Down Expand Up @@ -686,41 +688,30 @@ public void permits(ConstraintsParameters cp)
* timezone.
*/
private static class DenyAfterConstraint extends Constraint {
private Date denyAfterDate;
private ZonedDateTime zdt;
private Instant denyAfterDate;

DenyAfterConstraint(String algo, int year, int month, int day) {
Calendar c;

algorithm = algo;

if (debug != null) {
debug.println("DenyAfterConstraint read in as: year " +
debug.println("DenyAfterConstraint read in as: year " +
year + ", month = " + month + ", day = " + day);
}

c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
.setDate(year, month - 1, day).build();

if (year > c.getActualMaximum(Calendar.YEAR) ||
year < c.getActualMinimum(Calendar.YEAR)) {
throw new IllegalArgumentException(
"Invalid year given in constraint: " + year);
}
if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
(month - 1) < c.getActualMinimum(Calendar.MONTH)) {
throw new IllegalArgumentException(
"Invalid month given in constraint: " + month);
}
if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
try {
zdt = ZonedDateTime
.of(year, month, day, 0, 0, 0, 0, ZoneId.of("GMT"));
denyAfterDate = zdt.toInstant();
} catch (DateTimeException dte) {
throw new IllegalArgumentException(
"Invalid Day of Month given in constraint: " + day);
"Invalid denyAfter date", dte);
}

denyAfterDate = c.getTime();
if (debug != null) {
debug.println("DenyAfterConstraint date set to: " +
denyAfterDate);
zdt.toLocalDate());
}
}

Expand All @@ -735,23 +726,22 @@ private static class DenyAfterConstraint extends Constraint {
@Override
public void permits(ConstraintsParameters cp)
throws CertPathValidatorException {
Date currentDate;
String errmsg;
Instant currentDate;

if (cp.getDate() != null) {
currentDate = cp.getDate();
currentDate = cp.getDate().toInstant();
} else {
currentDate = new Date();
currentDate = Instant.now();
}

if (!denyAfterDate.after(currentDate)) {
if (!denyAfterDate.isAfter(currentDate)) {
if (next(cp)) {
return;
}
throw new CertPathValidatorException(
"denyAfter constraint check failed: " + algorithm +
" used with Constraint date: " +
denyAfterDate + "; params date: " +
zdt.toLocalDate() + "; params date: " +
currentDate + cp.extendedExceptionMsg(),
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
}
Expand All @@ -770,7 +760,7 @@ public boolean permits(Key key) {
debug.println("DenyAfterConstraints.permits(): " + algorithm);
}

return denyAfterDate.after(new Date());
return denyAfterDate.isAfter(Instant.now());
}
}

Expand Down
43 changes: 43 additions & 0 deletions test/jdk/java/security/SignedJar/CustomClassLoader.java
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2022, 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.
*/

import java.io.IOException;
import java.io.InputStream;

public class CustomClassLoader extends ClassLoader {

public CustomClassLoader(ClassLoader parent) {
super(parent);
}

@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
try (InputStream is = getClass().getClassLoader()
.getResourceAsStream(name + ".class")) {
byte[] buf = is.readAllBytes();
return defineClass(name, buf, 0, buf.length);
} catch (IOException e) {
throw new ClassNotFoundException(e.getMessage());
}
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2022, 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 8280890
* @library /test/lib
* @build SignedJarWithCustomClassLoader CustomClassLoader
* @run main/othervm SignedJarWithCustomClassLoader
* @summary Make sure java.system.class.loader property can be used when custom
* class loader is inside signed jar
*/

import java.nio.file.Path;
import java.nio.file.Paths;

import jdk.test.lib.SecurityTools;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.JarUtils;

public class SignedJarWithCustomClassLoader {

public static void main(String[] args) throws Exception {

// compile the Main program
String main = """
public class Main {
public static void main(String[] args) {}
}
""";
String testClasses = System.getProperty("test.classes", "");
ClassFileInstaller.writeClassToDisk("Main",
InMemoryJavaCompiler.compile("Main", main),
testClasses);

// create the jar file
Path classes = Paths.get(testClasses);
JarUtils.createJarFile(Paths.get("test.jar"), classes,
classes.resolve("CustomClassLoader.class"),
classes.resolve("Main.class"));

// create signer's keypair
SecurityTools.keytool("-genkeypair -keyalg RSA -keystore ks " +
"-storepass changeit -dname CN=test -alias test")
.shouldHaveExitValue(0);

// sign jar
SecurityTools.jarsigner("-keystore ks -storepass changeit " +
"-signedjar signed.jar test.jar test")
.shouldHaveExitValue(0);

// run app with system class loader set to custom classloader
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-cp", "signed.jar",
"-Djava.system.class.loader=CustomClassLoader", "Main");
ProcessTools.executeProcess(pb)
.shouldHaveExitValue(0);

// sign jar again, but this time with SHA-1 which is disabled
SecurityTools.jarsigner("-keystore ks -storepass changeit " +
"-digestalg SHA-1 -sigalg SHA1withRSA " +
"-signedjar signed.jar test.jar test")
.shouldHaveExitValue(0);

// run app again, should still succeed even though SHA-1 is disabled
pb = ProcessTools.createJavaProcessBuilder(
"-cp", "signed.jar",
"-Djava.system.class.loader=CustomClassLoader", "Main");
ProcessTools.executeProcess(pb)
.shouldHaveExitValue(0);
}
}

1 comment on commit a0f6f24

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.