test/jdk/sun/security/tools/jarsigner/LineBrokenMultiByteCharacter.java
changeset 47273 f60a42d4b8cd
child 49111 1b33025ae610
equal deleted inserted replaced
47272:e0d686cdf608 47273:f60a42d4b8cd
       
     1 /*
       
     2  * Copyright (c) 2017, 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 6695402
       
    27  * @summary verify signatures of jars containing classes with names
       
    28  *          with multi-byte unicode characters broken across lines
       
    29  * @library /test/lib
       
    30  */
       
    31 
       
    32 import java.io.IOException;
       
    33 import java.io.InputStream;
       
    34 import java.nio.file.Files;
       
    35 import java.nio.file.Paths;
       
    36 import java.util.jar.JarFile;
       
    37 import java.util.jar.Attributes.Name;
       
    38 import java.util.jar.JarEntry;
       
    39 
       
    40 import static java.nio.charset.StandardCharsets.UTF_8;
       
    41 
       
    42 import jdk.test.lib.SecurityTools;
       
    43 import jdk.test.lib.util.JarUtils;
       
    44 
       
    45 public class LineBrokenMultiByteCharacter {
       
    46 
       
    47     /**
       
    48      * this name will break across lines in MANIFEST.MF at the
       
    49      * middle of a two-byte utf-8 encoded character due to its e acute letter
       
    50      * at its exact position.
       
    51      *
       
    52      * because no file with such a name exists {@link JarUtils} will add the
       
    53      * name itself as contents to the jar entry which would have contained a
       
    54      * compiled class in the original bug. For this test, the contents of the
       
    55      * files contained in the jar file is not important as long as they get
       
    56      * signed.
       
    57      *
       
    58      * @see #verifyClassNameLineBroken(JarFile, String)
       
    59      */
       
    60     static final String testClassName =
       
    61             "LineBrokenMultiByteCharacterA1234567890B1234567890C123456789D12\u00E9xyz.class";
       
    62 
       
    63     static final String anotherName =
       
    64             "LineBrokenMultiByteCharacterA1234567890B1234567890C123456789D1234567890.class";
       
    65 
       
    66     static final String alias = "a";
       
    67     static final String keystoreFileName = "test.jks";
       
    68     static final String manifestFileName = "MANIFEST.MF";
       
    69 
       
    70     public static void main(String[] args) throws Exception {
       
    71         prepare();
       
    72 
       
    73         testSignJar("test.jar");
       
    74         testSignJarNoManifest("test-no-manifest.jar");
       
    75         testSignJarUpdate("test-update.jar", "test-updated.jar");
       
    76     }
       
    77 
       
    78     static void prepare() throws Exception {
       
    79         SecurityTools.keytool("-keystore", keystoreFileName, "-genkeypair",
       
    80                 "-storepass", "changeit", "-keypass", "changeit", "-storetype",
       
    81                 "JKS", "-alias", alias, "-dname", "CN=X", "-validity", "366")
       
    82             .shouldHaveExitValue(0);
       
    83 
       
    84         Files.write(Paths.get(manifestFileName), (Name.
       
    85                 MANIFEST_VERSION.toString() + ": 1.0\r\n").getBytes(UTF_8));
       
    86     }
       
    87 
       
    88     static void testSignJar(String jarFileName) throws Exception {
       
    89         JarUtils.createJar(jarFileName, manifestFileName, testClassName);
       
    90         verifyJarSignature(jarFileName);
       
    91     }
       
    92 
       
    93     static void testSignJarNoManifest(String jarFileName) throws Exception {
       
    94         JarUtils.createJar(jarFileName, testClassName);
       
    95         verifyJarSignature(jarFileName);
       
    96     }
       
    97 
       
    98     static void testSignJarUpdate(
       
    99             String initialFileName, String updatedFileName) throws Exception {
       
   100         JarUtils.createJar(initialFileName, manifestFileName, anotherName);
       
   101         SecurityTools.jarsigner("-keystore", keystoreFileName, "-storetype",
       
   102                 "JKS", "-storepass", "changeit", "-debug", initialFileName,
       
   103                 alias).shouldHaveExitValue(0);
       
   104         JarUtils.updateJar(initialFileName, updatedFileName, testClassName);
       
   105         verifyJarSignature(updatedFileName);
       
   106     }
       
   107 
       
   108     static void verifyJarSignature(String jarFileName) throws Exception {
       
   109         // actually sign the jar
       
   110         SecurityTools.jarsigner("-keystore", keystoreFileName, "-storetype",
       
   111                 "JKS", "-storepass", "changeit", "-debug", jarFileName, alias)
       
   112             .shouldHaveExitValue(0);
       
   113 
       
   114         try (
       
   115             JarFile jar = new JarFile(jarFileName);
       
   116         ) {
       
   117             verifyClassNameLineBroken(jar, testClassName);
       
   118             verifyCodeSigners(jar, jar.getJarEntry(testClassName));
       
   119         }
       
   120     }
       
   121 
       
   122     /**
       
   123      * it would be too easy to miss the actual test case by just renaming an
       
   124      * identifier so that the multi-byte encoded character would not any longer
       
   125      * be broken across a line break.
       
   126      *
       
   127      * this check here verifies that the actual test case is tested based on
       
   128      * the manifest and not based on the signature file because at the moment,
       
   129      * the signature file does not even contain the desired entry at all.
       
   130      *
       
   131      * this relies on {@link java.util.jar.Manifest} breaking lines unaware
       
   132      * of bytes that belong to the same multi-byte utf characters.
       
   133      */
       
   134     static void verifyClassNameLineBroken(JarFile jar, String className)
       
   135             throws IOException {
       
   136         byte[] eAcute = "\u00E9".getBytes(UTF_8);
       
   137         byte[] eAcuteBroken =
       
   138                 new byte[] {eAcute[0], '\r', '\n', ' ', eAcute[1]};
       
   139 
       
   140         if (jar.getManifest().getAttributes(className) == null) {
       
   141             throw new AssertionError(className + " not found in manifest");
       
   142         }
       
   143 
       
   144         JarEntry manifestEntry = jar.getJarEntry(JarFile.MANIFEST_NAME);
       
   145         try (
       
   146             InputStream manifestIs = jar.getInputStream(manifestEntry);
       
   147         ) {
       
   148             int bytesMatched = 0;
       
   149             for (int b = manifestIs.read(); b > -1; b = manifestIs.read()) {
       
   150                 if ((byte) b == eAcuteBroken[bytesMatched]) {
       
   151                     bytesMatched++;
       
   152                     if (bytesMatched == eAcuteBroken.length) {
       
   153                         break;
       
   154                     }
       
   155                 } else {
       
   156                     bytesMatched = 0;
       
   157                 }
       
   158             }
       
   159             if (bytesMatched < eAcuteBroken.length) {
       
   160                 throw new AssertionError("self-test failed: multi-byte "
       
   161                         + "utf-8 character not broken across lines");
       
   162             }
       
   163         }
       
   164     }
       
   165 
       
   166     static void verifyCodeSigners(JarFile jar, JarEntry jarEntry)
       
   167             throws IOException {
       
   168         // codeSigners is initialized only after the entry has been read
       
   169         try (
       
   170             InputStream inputStream = jar.getInputStream(jarEntry);
       
   171         ) {
       
   172             inputStream.readAllBytes();
       
   173         }
       
   174 
       
   175         // a check for the presence of code signers is sufficient to check
       
   176         // bug JDK-6695402. no need to also verify the actual code signers
       
   177         // attributes here.
       
   178         if (jarEntry.getCodeSigners() == null
       
   179                 || jarEntry.getCodeSigners().length == 0) {
       
   180             throw new AssertionError(
       
   181                     "no signing certificate found for " + jarEntry.getName());
       
   182         }
       
   183     }
       
   184 
       
   185 }