Skip to content

Commit 301af1d

Browse files
committed
8280890: Cannot use '-Djava.system.class.loader' with class loader in signed JAR
Reviewed-by: clanger Backport-of: a0f6f2409ea61ff9ed9dc2e2b46e309c751d456d
1 parent d65e05d commit 301af1d

File tree

4 files changed

+158
-44
lines changed

4 files changed

+158
-44
lines changed

src/java.base/share/classes/sun/security/tools/keytool/Main.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
import java.security.spec.ECParameterSpec;
4848
import java.text.Collator;
4949
import java.text.MessageFormat;
50-
import java.text.ParseException;
51-
import java.text.SimpleDateFormat;
5250
import java.util.*;
5351
import java.util.function.BiFunction;
5452
import java.util.jar.JarEntry;
@@ -4924,17 +4922,6 @@ private void checkWeakConstraint(String label, String sigAlg, Key key,
49244922
"Unable.to.parse.denyAfter.string.in.exception.message"));
49254923
}
49264924

4927-
SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
4928-
Date dateObj = null;
4929-
try {
4930-
dateObj = formatter.parse(denyAfterDate);
4931-
} catch (ParseException e2) {
4932-
throw new Exception(rb.getString(
4933-
"Unable.to.parse.denyAfter.string.in.exception.message"));
4934-
}
4935-
formatter = new SimpleDateFormat("yyyy-MM-dd");
4936-
denyAfterDate = formatter.format(dateObj);
4937-
49384925
weakWarnings.add(String.format(
49394926
rb.getString("whose.sigalg.usagesignedjar"), label, sigAlg,
49404927
denyAfterDate));

src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -40,9 +40,12 @@
4040
import java.security.spec.MGF1ParameterSpec;
4141
import java.security.spec.NamedParameterSpec;
4242
import java.security.spec.PSSParameterSpec;
43+
import java.time.DateTimeException;
44+
import java.time.Instant;
45+
import java.time.ZonedDateTime;
46+
import java.time.ZoneId;
4347
import java.util.ArrayList;
4448
import java.util.Arrays;
45-
import java.util.Calendar;
4649
import java.util.Date;
4750
import java.util.HashMap;
4851
import java.util.HashSet;
@@ -52,7 +55,6 @@
5255
import java.util.Set;
5356
import java.util.Collection;
5457
import java.util.StringTokenizer;
55-
import java.util.TimeZone;
5658
import java.util.concurrent.ConcurrentHashMap;
5759
import java.util.regex.Pattern;
5860
import java.util.regex.Matcher;
@@ -700,41 +702,30 @@ public void permits(ConstraintsParameters cp)
700702
* timezone.
701703
*/
702704
private static class DenyAfterConstraint extends Constraint {
703-
private Date denyAfterDate;
705+
private ZonedDateTime zdt;
706+
private Instant denyAfterDate;
704707

705708
DenyAfterConstraint(String algo, int year, int month, int day) {
706-
Calendar c;
707709

708710
algorithm = algo;
709711

710712
if (debug != null) {
711-
debug.println("DenyAfterConstraint read in as: year " +
713+
debug.println("DenyAfterConstraint read in as: year " +
712714
year + ", month = " + month + ", day = " + day);
713715
}
714716

715-
c = new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("GMT"))
716-
.setDate(year, month - 1, day).build();
717-
718-
if (year > c.getActualMaximum(Calendar.YEAR) ||
719-
year < c.getActualMinimum(Calendar.YEAR)) {
720-
throw new IllegalArgumentException(
721-
"Invalid year given in constraint: " + year);
722-
}
723-
if ((month - 1) > c.getActualMaximum(Calendar.MONTH) ||
724-
(month - 1) < c.getActualMinimum(Calendar.MONTH)) {
725-
throw new IllegalArgumentException(
726-
"Invalid month given in constraint: " + month);
727-
}
728-
if (day > c.getActualMaximum(Calendar.DAY_OF_MONTH) ||
729-
day < c.getActualMinimum(Calendar.DAY_OF_MONTH)) {
717+
try {
718+
zdt = ZonedDateTime
719+
.of(year, month, day, 0, 0, 0, 0, ZoneId.of("GMT"));
720+
denyAfterDate = zdt.toInstant();
721+
} catch (DateTimeException dte) {
730722
throw new IllegalArgumentException(
731-
"Invalid Day of Month given in constraint: " + day);
723+
"Invalid denyAfter date", dte);
732724
}
733725

734-
denyAfterDate = c.getTime();
735726
if (debug != null) {
736727
debug.println("DenyAfterConstraint date set to: " +
737-
denyAfterDate);
728+
zdt.toLocalDate());
738729
}
739730
}
740731

@@ -749,23 +740,22 @@ private static class DenyAfterConstraint extends Constraint {
749740
@Override
750741
public void permits(ConstraintsParameters cp)
751742
throws CertPathValidatorException {
752-
Date currentDate;
753-
String errmsg;
743+
Instant currentDate;
754744

755745
if (cp.getDate() != null) {
756-
currentDate = cp.getDate();
746+
currentDate = cp.getDate().toInstant();
757747
} else {
758-
currentDate = new Date();
748+
currentDate = Instant.now();
759749
}
760750

761-
if (!denyAfterDate.after(currentDate)) {
751+
if (!denyAfterDate.isAfter(currentDate)) {
762752
if (next(cp)) {
763753
return;
764754
}
765755
throw new CertPathValidatorException(
766756
"denyAfter constraint check failed: " + algorithm +
767757
" used with Constraint date: " +
768-
denyAfterDate + "; params date: " +
758+
zdt.toLocalDate() + "; params date: " +
769759
currentDate + cp.extendedExceptionMsg(),
770760
null, null, -1, BasicReason.ALGORITHM_CONSTRAINED);
771761
}
@@ -784,7 +774,7 @@ public boolean permits(Key key) {
784774
debug.println("DenyAfterConstraints.permits(): " + algorithm);
785775
}
786776

787-
return denyAfterDate.after(new Date());
777+
return denyAfterDate.isAfter(Instant.now());
788778
}
789779
}
790780

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
27+
public class CustomClassLoader extends ClassLoader {
28+
29+
public CustomClassLoader(ClassLoader parent) {
30+
super(parent);
31+
}
32+
33+
@Override
34+
public Class<?> findClass(String name) throws ClassNotFoundException {
35+
try (InputStream is = getClass().getClassLoader()
36+
.getResourceAsStream(name + ".class")) {
37+
byte[] buf = is.readAllBytes();
38+
return defineClass(name, buf, 0, buf.length);
39+
} catch (IOException e) {
40+
throw new ClassNotFoundException(e.getMessage());
41+
}
42+
}
43+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8280890
27+
* @library /test/lib
28+
* @build SignedJarWithCustomClassLoader CustomClassLoader
29+
* @run main/othervm SignedJarWithCustomClassLoader
30+
* @summary Make sure java.system.class.loader property can be used when custom
31+
* class loader is inside signed jar
32+
*/
33+
34+
import java.nio.file.Path;
35+
import java.nio.file.Paths;
36+
37+
import jdk.test.lib.SecurityTools;
38+
import jdk.test.lib.compiler.InMemoryJavaCompiler;
39+
import jdk.test.lib.helpers.ClassFileInstaller;
40+
import jdk.test.lib.process.ProcessTools;
41+
import jdk.test.lib.util.JarUtils;
42+
43+
public class SignedJarWithCustomClassLoader {
44+
45+
public static void main(String[] args) throws Exception {
46+
47+
// compile the Main program
48+
String main = """
49+
public class Main {
50+
public static void main(String[] args) {}
51+
}
52+
""";
53+
String testClasses = System.getProperty("test.classes", "");
54+
ClassFileInstaller.writeClassToDisk("Main",
55+
InMemoryJavaCompiler.compile("Main", main),
56+
testClasses);
57+
58+
// create the jar file
59+
Path classes = Paths.get(testClasses);
60+
JarUtils.createJarFile(Paths.get("test.jar"), classes,
61+
classes.resolve("CustomClassLoader.class"),
62+
classes.resolve("Main.class"));
63+
64+
// create signer's keypair
65+
SecurityTools.keytool("-genkeypair -keyalg RSA -keystore ks " +
66+
"-storepass changeit -dname CN=test -alias test")
67+
.shouldHaveExitValue(0);
68+
69+
// sign jar
70+
SecurityTools.jarsigner("-keystore ks -storepass changeit " +
71+
"-signedjar signed.jar test.jar test")
72+
.shouldHaveExitValue(0);
73+
74+
// run app with system class loader set to custom classloader
75+
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
76+
"-cp", "signed.jar",
77+
"-Djava.system.class.loader=CustomClassLoader", "Main");
78+
ProcessTools.executeProcess(pb)
79+
.shouldHaveExitValue(0);
80+
81+
// sign jar again, but this time with SHA-1 which is disabled
82+
SecurityTools.jarsigner("-keystore ks -storepass changeit " +
83+
"-digestalg SHA-1 -sigalg SHA1withRSA " +
84+
"-signedjar signed.jar test.jar test")
85+
.shouldHaveExitValue(0);
86+
87+
// run app again, should still succeed even though SHA-1 is disabled
88+
pb = ProcessTools.createJavaProcessBuilder(
89+
"-cp", "signed.jar",
90+
"-Djava.system.class.loader=CustomClassLoader", "Main");
91+
ProcessTools.executeProcess(pb)
92+
.shouldHaveExitValue(0);
93+
}
94+
}

0 commit comments

Comments
 (0)