diff --git a/src/java.base/share/classes/java/util/jar/JarVerifier.java b/src/java.base/share/classes/java/util/jar/JarVerifier.java index cce4c6618eb..c866e6e4d0e 100644 --- a/src/java.base/share/classes/java/util/jar/JarVerifier.java +++ b/src/java.base/share/classes/java/util/jar/JarVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -39,6 +39,8 @@ import sun.security.util.ManifestEntryVerifier; import sun.security.util.SignatureFileVerifier; import sun.security.util.Debug; +import static sun.security.util.SignatureFileVerifier.isInMetaInf; + /** * * @author Roland Schemers @@ -135,15 +137,14 @@ class JarVerifier { */ if (parsingMeta) { - String uname = name.toUpperCase(Locale.ENGLISH); - if ((uname.startsWith("META-INF/") || - uname.startsWith("/META-INF/"))) { + + if (isInMetaInf(name)) { if (je.isDirectory()) { mev.setEntry(null, je); return; } - + String uname = name.toUpperCase(Locale.ENGLISH); if (uname.equals(JarFile.MANIFEST_NAME) || uname.equals(JarIndex.INDEX_NAME)) { return; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 9e4a7fefd53..aa19de461ec 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2023, 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 @@ -1745,8 +1745,27 @@ public class ZipFile implements ZipConstants, Closeable { assert(signatureRelated == SignatureFileVerifier .isBlockOrSF(new String(name, off, len, UTF_8.INSTANCE) .toUpperCase(Locale.ENGLISH))); + + // Signature related files must reside directly in META-INF/ + if (signatureRelated && hasSlash(name, off + META_INF_LEN, off + len)) { + signatureRelated = false; + } return signatureRelated; } + /* + * Return true if the encoded name contains a '/' within the byte given range + * This assumes an ASCII-compatible encoding, which is ok here since + * it is already assumed in isMetaName + */ + private boolean hasSlash(byte[] name, int start, int end) { + for (int i = start; i < end; i++) { + int c = name[i]; + if (c == '/') { + return true; + } + } + return false; + } /* * If the bytes represents a non-directory name beginning diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index eb3fea340b7..0f85b538a43 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -81,6 +81,8 @@ public class SignatureFileVerifier { /** ConstraintsParameters for checking disabled algorithms */ private JarConstraintsParameters params; + private static final String META_INF = "META-INF/"; + /** * Create the named SignatureFileVerifier. * @@ -141,6 +143,18 @@ public class SignatureFileVerifier { this.sfBytes = sfBytes; } + /** + * Utility method used by JarVerifier and JarSigner + * to determine if a path is located directly in the + * META-INF/ directory + * + * @param name the path name to check + * @return true if the path resides in META-INF directly, ignoring case + */ + public static boolean isInMetaInf(String name) { + return name.regionMatches(true, 0, META_INF, 0, META_INF.length()) + && name.lastIndexOf('/') < META_INF.length(); + } /** * Utility method used by JarVerifier and JarSigner * to determine the signature file names and PKCS7 block @@ -153,7 +167,7 @@ public class SignatureFileVerifier { */ public static boolean isBlockOrSF(String s) { // Note: keep this in sync with j.u.z.ZipFile.Source#isSignatureRelated - // we currently only support DSA and RSA PKCS7 blocks + // we currently only support DSA, RSA or EC PKCS7 blocks return s.endsWith(".SF") || s.endsWith(".DSA") || s.endsWith(".RSA") @@ -191,19 +205,15 @@ public class SignatureFileVerifier { * @return true if the input file name is signature related */ public static boolean isSigningRelated(String name) { + if (!isInMetaInf(name)) { + return false; + } name = name.toUpperCase(Locale.ENGLISH); - if (!name.startsWith("META-INF/")) { - return false; - } - name = name.substring(9); - if (name.indexOf('/') != -1) { - return false; - } - if (isBlockOrSF(name) || name.equals("MANIFEST.MF")) { + if (isBlockOrSF(name) || name.equals("META-INF/MANIFEST.MF")) { return true; - } else if (name.startsWith("SIG-")) { + } else if (name.startsWith("SIG-", META_INF.length())) { // check filename extension - // see http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#Digital_Signatures + // see https://docs.oracle.com/en/java/javase/19/docs/specs/jar/jar.html#digital-signatures // for what filename extensions are legal int extIndex = name.lastIndexOf('.'); if (extIndex != -1) { diff --git a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java index 4e82872ef7a..e279c5a084b 100644 --- a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java +++ b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java @@ -63,6 +63,8 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static sun.security.util.SignatureFileVerifier.isInMetaInf; + /** * An immutable utility class to sign a jar file. *
@@ -475,8 +477,6 @@ public final class JarSigner {
}
}
- private static final String META_INF = "META-INF/";
-
// All fields in Builder are duplicated here as final. Those not
// provided but has a default value will be filled with default value.
@@ -731,7 +731,7 @@ public final class JarSigner {
enum_.hasMoreElements(); ) {
ZipEntry ze = enum_.nextElement();
- if (ze.getName().startsWith(META_INF)) {
+ if (isInMetaInf(ze.getName())) {
// Store META-INF files in vector, so they can be written
// out first
mfFiles.addElement(ze);
@@ -959,7 +959,7 @@ public final class JarSigner {
enum_.hasMoreElements(); ) {
ZipEntry ze = enum_.nextElement();
- if (!ze.getName().startsWith(META_INF)) {
+ if (!isInMetaInf(ze.getName())) {
if (handler != null) {
if (manifest.getAttributes(ze.getName()) != null) {
handler.accept("signing", ze.getName());
@@ -974,6 +974,7 @@ public final class JarSigner {
zos.close();
}
+
private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
throws IOException {
ZipEntry ze2 = new ZipEntry(ze.getName());
diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
index 9be5942b4c5..c6ebfb25e0c 100644
--- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
+++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2023, 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
@@ -835,8 +835,7 @@ public class Main {
if (!extraAttrsDetected && JUZFA.getExtraAttributes(je) != -1) {
extraAttrsDetected = true;
}
- hasSignature = hasSignature
- || SignatureFileVerifier.isBlockOrSF(name);
+ hasSignature |= signatureRelated(name) && SignatureFileVerifier.isBlockOrSF(name);
CodeSigner[] signers = je.getCodeSigners();
boolean isSigned = (signers != null);
diff --git a/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java b/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java
new file mode 100644
index 00000000000..0f55702c1f6
--- /dev/null
+++ b/test/jdk/java/util/jar/JarFile/IgnoreUnrelatedSignatureFiles.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2023, 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 8300140
+ * @summary Make sure signature related files in subdirectories of META-INF are not considered for verification
+ * @modules java.base/jdk.internal.access
+ * @modules java.base/sun.security.util
+ * @modules java.base/sun.security.tools.keytool
+ * @modules jdk.jartool/sun.security.tools.jarsigner
+ * @run main/othervm IgnoreUnrelatedSignatureFiles
+ */
+
+import jdk.internal.access.JavaUtilZipFileAccess;
+import jdk.internal.access.SharedSecrets;
+import jdk.security.jarsigner.JarSigner;
+import sun.security.tools.jarsigner.Main;
+import sun.security.util.SignatureFileVerifier;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.CodeSigner;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class IgnoreUnrelatedSignatureFiles {
+
+ private static final JavaUtilZipFileAccess JUZA = SharedSecrets.getJavaUtilZipFileAccess();
+
+ // This path resides in a subdirectory of META-INF, so it should not be considered signature related
+ public static final String SUBDIR_SF_PATH = "META-INF/subdirectory/META-INF/SIGNER.SF";
+
+
+ public static void main(String[] args) throws Exception {
+
+ // Regular signed JAR
+ Path j = createJarFile();
+ Path s = signJarFile(j, "SIGNER1", "signed");
+
+ // Singed JAR with unrelated signature files
+ Path m = moveSignatureRelated(s);
+ Path sm = signJarFile(m, "SIGNER2", "modified-signed");
+
+ // Signed JAR with custom SIG-* files
+ Path ca = createCustomAlgJar();
+ Path cas = signJarFile(ca, "SIGNER1", "custom-signed");
+
+ // 0: Sanity check that the basic signed JAR verifies
+ try (JarFile jf = new JarFile(s.toFile(), true)) {
+ Map