src/share/classes/sun/security/tools/jarsigner/Main.java

Print this page




  40 import java.security.*;
  41 import java.lang.reflect.Constructor;
  42 
  43 import com.sun.jarsigner.ContentSigner;
  44 import com.sun.jarsigner.ContentSignerParameters;
  45 import java.net.SocketTimeoutException;
  46 import java.net.URL;
  47 import java.net.URLClassLoader;
  48 import java.security.cert.CertPath;
  49 import java.security.cert.CertPathValidator;
  50 import java.security.cert.CertificateExpiredException;
  51 import java.security.cert.CertificateFactory;
  52 import java.security.cert.CertificateNotYetValidException;
  53 import java.security.cert.PKIXParameters;
  54 import java.security.cert.TrustAnchor;
  55 import java.util.Map.Entry;
  56 import sun.security.tools.KeyStoreUtil;
  57 import sun.security.tools.PathList;
  58 import sun.security.x509.*;
  59 import sun.security.util.*;
  60 import sun.misc.BASE64Encoder;
  61 
  62 
  63 /**
  64  * <p>The jarsigner utility.
  65  *
  66  * The exit codes for the main method are:
  67  *
  68  * 0: success
  69  * 1: any error that the jar cannot be signed or verified, including:
  70  *      keystore loading error
  71  *      TSP communication error
  72  *      jarsigner command line error...
  73  * otherwise: error codes from -strict
  74  *
  75  * @author Roland Schemers
  76  * @author Jan Luehe
  77  */
  78 
  79 public class Main {
  80 


1103                                "1.0");
1104                 String javaVendor = System.getProperty("java.vendor");
1105                 String jdkVersion = System.getProperty("java.version");
1106                 mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
1107                                + ")");
1108                 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1109                 mfCreated = true;
1110             }
1111 
1112             /*
1113              * For each entry in jar
1114              * (except for signature-related META-INF entries),
1115              * do the following:
1116              *
1117              * - if entry is not contained in manifest, add it to manifest;
1118              * - if entry is contained in manifest, calculate its hash and
1119              *   compare it with the one in the manifest; if they are
1120              *   different, replace the hash in the manifest with the newly
1121              *   generated one. (This may invalidate existing signatures!)
1122              */
1123             BASE64Encoder encoder = new JarBASE64Encoder();
1124             Vector<ZipEntry> mfFiles = new Vector<>();
1125 
1126             boolean wasSigned = false;
1127 
1128             for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1129                         enum_.hasMoreElements();) {
1130                 ZipEntry ze = enum_.nextElement();
1131 
1132                 if (ze.getName().startsWith(META_INF)) {
1133                     // Store META-INF files in vector, so they can be written
1134                     // out first
1135                     mfFiles.addElement(ze);
1136 
1137                     if (SignatureFileVerifier.isBlockOrSF(
1138                             ze.getName().toUpperCase(Locale.ENGLISH))) {
1139                         wasSigned = true;
1140                     }
1141 
1142                     if (signatureRelated(ze.getName())) {
1143                         // ignore signature-related and manifest files
1144                         continue;
1145                     }
1146                 }
1147 
1148                 if (manifest.getAttributes(ze.getName()) != null) {
1149                     // jar entry is contained in manifest, check and
1150                     // possibly update its digest attributes
1151                     if (updateDigests(ze, zipFile, digests, encoder,
1152                                       manifest) == true) {
1153                         mfModified = true;
1154                     }
1155                 } else if (!ze.isDirectory()) {
1156                     // Add entry to manifest
1157                     Attributes attrs = getDigestAttributes(ze, zipFile,
1158                                                            digests,
1159                                                            encoder);
1160                     mfEntries.put(ze.getName(), attrs);
1161                     mfModified = true;
1162                 }
1163             }
1164 
1165             // Recalculate the manifest raw bytes if necessary
1166             if (mfModified) {
1167                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1168                 manifest.write(baos);
1169                 if (wasSigned) {
1170                     byte[] newBytes = baos.toByteArray();
1171                     if (mfRawBytes != null
1172                             && oldAttr.equals(manifest.getMainAttributes())) {
1173 
1174                         /*
1175                          * Note:
1176                          *
1177                          * The Attributes object is based on HashMap and can handle
1178                          * continuation columns. Therefore, even if the contents are
1179                          * not changed (in a Map view), the bytes that it write()


1938         ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
1939         if (ze == null) {
1940             // Check all entries for matching name
1941             Enumeration<? extends ZipEntry> enum_ = zf.entries();
1942             while (enum_.hasMoreElements() && ze == null) {
1943                 ze = enum_.nextElement();
1944                 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
1945                     (ze.getName())) {
1946                     ze = null;
1947                 }
1948             }
1949         }
1950         return ze;
1951     }
1952 
1953     /*
1954      * Computes the digests of a zip entry, and returns them as an array
1955      * of base64-encoded strings.
1956      */
1957     private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
1958                                              MessageDigest[] digests,
1959                                              BASE64Encoder encoder)
1960         throws IOException {
1961 
1962         int n, i;
1963         InputStream is = null;
1964         try {
1965             is = zf.getInputStream(ze);
1966             long left = ze.getSize();
1967             while((left > 0)
1968                 && (n = is.read(buffer, 0, buffer.length)) != -1) {
1969                 for (i=0; i<digests.length; i++) {
1970                     digests[i].update(buffer, 0, n);
1971                 }
1972                 left -= n;
1973             }
1974         } finally {
1975             if (is != null) {
1976                 is.close();
1977             }
1978         }
1979 
1980         // complete the digests
1981         String[] base64Digests = new String[digests.length];
1982         for (i=0; i<digests.length; i++) {
1983             base64Digests[i] = encoder.encode(digests[i].digest());
1984         }
1985         return base64Digests;
1986     }
1987 
1988     /*
1989      * Computes the digests of a zip entry, and returns them as a list of
1990      * attributes
1991      */
1992     private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
1993                                            MessageDigest[] digests,
1994                                            BASE64Encoder encoder)
1995         throws IOException {
1996 
1997         String[] base64Digests = getDigests(ze, zf, digests, encoder);
1998         Attributes attrs = new Attributes();
1999 
2000         for (int i=0; i<digests.length; i++) {
2001             attrs.putValue(digests[i].getAlgorithm()+"-Digest",
2002                            base64Digests[i]);
2003         }
2004         return attrs;
2005     }
2006 
2007     /*
2008      * Updates the digest attributes of a manifest entry, by adding or
2009      * replacing digest values.
2010      * A digest value is added if the manifest entry does not contain a digest
2011      * for that particular algorithm.
2012      * A digest value is replaced if it is obsolete.
2013      *
2014      * Returns true if the manifest entry has been changed, and false
2015      * otherwise.
2016      */
2017     private boolean updateDigests(ZipEntry ze, ZipFile zf,
2018                                   MessageDigest[] digests,
2019                                   BASE64Encoder encoder,
2020                                   Manifest mf) throws IOException {
2021         boolean update = false;
2022 
2023         Attributes attrs = mf.getAttributes(ze.getName());
2024         String[] base64Digests = getDigests(ze, zf, digests, encoder);
2025 
2026         for (int i=0; i<digests.length; i++) {
2027             // The entry name to be written into attrs
2028             String name = null;
2029             try {
2030                 // Find if the digest already exists
2031                 AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
2032                 for (Object key: attrs.keySet()) {
2033                     if (key instanceof Attributes.Name) {
2034                         String n = ((Attributes.Name)key).toString();
2035                         if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
2036                             String tmp = n.substring(0, n.length() - 7);
2037                             if (AlgorithmId.get(tmp).equals(aid)) {
2038                                 name = n;
2039                                 break;
2040                             }
2041                         }
2042                     }
2043                 }
2044             } catch (NoSuchAlgorithmException nsae) {


2077         cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
2078         cpString = PathList.appendPath(signerClassPath, cpString);
2079         URL[] urls = PathList.pathToURLs(cpString);
2080         ClassLoader appClassLoader = new URLClassLoader(urls);
2081 
2082         // attempt to find signer
2083         Class<?> signerClass = appClassLoader.loadClass(signerClassName);
2084 
2085         // Check that it implements ContentSigner
2086         Object signer = signerClass.newInstance();
2087         if (!(signer instanceof ContentSigner)) {
2088             MessageFormat form = new MessageFormat(
2089                 rb.getString("signerClass.is.not.a.signing.mechanism"));
2090             Object[] source = {signerClass.getName()};
2091             throw new IllegalArgumentException(form.format(source));
2092         }
2093         return (ContentSigner)signer;
2094     }
2095 }
2096 
2097 /**
2098  * This is a BASE64Encoder that does not insert a default newline at the end of
2099  * every output line. This is necessary because java.util.jar does its own
2100  * line management (see Manifest.make72Safe()). Inserting additional new lines
2101  * can cause line-wrapping problems (see CR 6219522).
2102  */
2103 class JarBASE64Encoder extends BASE64Encoder {
2104     /**
2105      * Encode the suffix that ends every output line.
2106      */
2107     protected void encodeLineSuffix(OutputStream aStream) throws IOException { }
2108 }
2109 
2110 class SignatureFile {
2111 
2112     /** SignatureFile */
2113     Manifest sf;
2114 
2115     /** .SF base name */
2116     String baseName;
2117 
2118     public SignatureFile(MessageDigest digests[],
2119                          Manifest mf,
2120                          ManifestDigester md,
2121                          String baseName,
2122                          boolean signManifest)
2123 
2124     {
2125         this.baseName = baseName;
2126 
2127         String version = System.getProperty("java.version");
2128         String javaVendor = System.getProperty("java.vendor");
2129 
2130         sf = new Manifest();
2131         Attributes mattr = sf.getMainAttributes();
2132         BASE64Encoder encoder = new JarBASE64Encoder();
2133 
2134         mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
2135         mattr.putValue("Created-By", version + " (" + javaVendor + ")");
2136 
2137         if (signManifest) {
2138             // sign the whole manifest
2139             for (int i=0; i < digests.length; i++) {
2140                 mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
2141                                encoder.encode(md.manifestDigest(digests[i])));
2142             }
2143         }
2144 
2145         // create digest of the manifest main attributes
2146         ManifestDigester.Entry mde =
2147                 md.get(ManifestDigester.MF_MAIN_ATTRS, false);
2148         if (mde != null) {
2149             for (int i=0; i < digests.length; i++) {
2150                 mattr.putValue(digests[i].getAlgorithm() +
2151                         "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
2152                         encoder.encode(mde.digest(digests[i])));
2153             }
2154         } else {
2155             throw new IllegalStateException
2156                 ("ManifestDigester failed to create " +
2157                 "Manifest-Main-Attribute entry");
2158         }
2159 
2160         /* go through the manifest entries and create the digests */
2161 
2162         Map<String,Attributes> entries = sf.getEntries();
2163         Iterator<Map.Entry<String,Attributes>> mit =
2164                                 mf.getEntries().entrySet().iterator();
2165         while(mit.hasNext()) {
2166             Map.Entry<String,Attributes> e = mit.next();
2167             String name = e.getKey();
2168             mde = md.get(name, false);
2169             if (mde != null) {
2170                 Attributes attr = new Attributes();
2171                 for (int i=0; i < digests.length; i++) {
2172                     attr.putValue(digests[i].getAlgorithm()+"-Digest",
2173                                   encoder.encode(mde.digest(digests[i])));
2174                 }
2175                 entries.put(name, attr);
2176             }
2177         }
2178     }
2179 
2180     /**
2181      * Writes the SignatureFile to the specified OutputStream.
2182      *
2183      * @param out the output stream
2184      * @exception IOException if an I/O error has occurred
2185      */
2186 
2187     public void write(OutputStream out) throws IOException
2188     {
2189         sf.write(out);
2190     }
2191 
2192     /**
2193      * get .SF file name




  40 import java.security.*;
  41 import java.lang.reflect.Constructor;
  42 
  43 import com.sun.jarsigner.ContentSigner;
  44 import com.sun.jarsigner.ContentSignerParameters;
  45 import java.net.SocketTimeoutException;
  46 import java.net.URL;
  47 import java.net.URLClassLoader;
  48 import java.security.cert.CertPath;
  49 import java.security.cert.CertPathValidator;
  50 import java.security.cert.CertificateExpiredException;
  51 import java.security.cert.CertificateFactory;
  52 import java.security.cert.CertificateNotYetValidException;
  53 import java.security.cert.PKIXParameters;
  54 import java.security.cert.TrustAnchor;
  55 import java.util.Map.Entry;
  56 import sun.security.tools.KeyStoreUtil;
  57 import sun.security.tools.PathList;
  58 import sun.security.x509.*;
  59 import sun.security.util.*;
  60 import java.util.Base64;
  61 
  62 
  63 /**
  64  * <p>The jarsigner utility.
  65  *
  66  * The exit codes for the main method are:
  67  *
  68  * 0: success
  69  * 1: any error that the jar cannot be signed or verified, including:
  70  *      keystore loading error
  71  *      TSP communication error
  72  *      jarsigner command line error...
  73  * otherwise: error codes from -strict
  74  *
  75  * @author Roland Schemers
  76  * @author Jan Luehe
  77  */
  78 
  79 public class Main {
  80 


1103                                "1.0");
1104                 String javaVendor = System.getProperty("java.vendor");
1105                 String jdkVersion = System.getProperty("java.version");
1106                 mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
1107                                + ")");
1108                 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1109                 mfCreated = true;
1110             }
1111 
1112             /*
1113              * For each entry in jar
1114              * (except for signature-related META-INF entries),
1115              * do the following:
1116              *
1117              * - if entry is not contained in manifest, add it to manifest;
1118              * - if entry is contained in manifest, calculate its hash and
1119              *   compare it with the one in the manifest; if they are
1120              *   different, replace the hash in the manifest with the newly
1121              *   generated one. (This may invalidate existing signatures!)
1122              */

1123             Vector<ZipEntry> mfFiles = new Vector<>();
1124 
1125             boolean wasSigned = false;
1126 
1127             for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
1128                         enum_.hasMoreElements();) {
1129                 ZipEntry ze = enum_.nextElement();
1130 
1131                 if (ze.getName().startsWith(META_INF)) {
1132                     // Store META-INF files in vector, so they can be written
1133                     // out first
1134                     mfFiles.addElement(ze);
1135 
1136                     if (SignatureFileVerifier.isBlockOrSF(
1137                             ze.getName().toUpperCase(Locale.ENGLISH))) {
1138                         wasSigned = true;
1139                     }
1140 
1141                     if (signatureRelated(ze.getName())) {
1142                         // ignore signature-related and manifest files
1143                         continue;
1144                     }
1145                 }
1146 
1147                 if (manifest.getAttributes(ze.getName()) != null) {
1148                     // jar entry is contained in manifest, check and
1149                     // possibly update its digest attributes
1150                     if (updateDigests(ze, zipFile, digests,
1151                                       manifest) == true) {
1152                         mfModified = true;
1153                     }
1154                 } else if (!ze.isDirectory()) {
1155                     // Add entry to manifest
1156                     Attributes attrs = getDigestAttributes(ze, zipFile,
1157                                                            digests);

1158                     mfEntries.put(ze.getName(), attrs);
1159                     mfModified = true;
1160                 }
1161             }
1162 
1163             // Recalculate the manifest raw bytes if necessary
1164             if (mfModified) {
1165                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1166                 manifest.write(baos);
1167                 if (wasSigned) {
1168                     byte[] newBytes = baos.toByteArray();
1169                     if (mfRawBytes != null
1170                             && oldAttr.equals(manifest.getMainAttributes())) {
1171 
1172                         /*
1173                          * Note:
1174                          *
1175                          * The Attributes object is based on HashMap and can handle
1176                          * continuation columns. Therefore, even if the contents are
1177                          * not changed (in a Map view), the bytes that it write()


1936         ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
1937         if (ze == null) {
1938             // Check all entries for matching name
1939             Enumeration<? extends ZipEntry> enum_ = zf.entries();
1940             while (enum_.hasMoreElements() && ze == null) {
1941                 ze = enum_.nextElement();
1942                 if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
1943                     (ze.getName())) {
1944                     ze = null;
1945                 }
1946             }
1947         }
1948         return ze;
1949     }
1950 
1951     /*
1952      * Computes the digests of a zip entry, and returns them as an array
1953      * of base64-encoded strings.
1954      */
1955     private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
1956                                              MessageDigest[] digests)

1957         throws IOException {
1958 
1959         int n, i;
1960         InputStream is = null;
1961         try {
1962             is = zf.getInputStream(ze);
1963             long left = ze.getSize();
1964             while((left > 0)
1965                 && (n = is.read(buffer, 0, buffer.length)) != -1) {
1966                 for (i=0; i<digests.length; i++) {
1967                     digests[i].update(buffer, 0, n);
1968                 }
1969                 left -= n;
1970             }
1971         } finally {
1972             if (is != null) {
1973                 is.close();
1974             }
1975         }
1976 
1977         // complete the digests
1978         String[] base64Digests = new String[digests.length];
1979         for (i=0; i<digests.length; i++) {
1980             base64Digests[i] = Base64.getEncoder().encodeToString(digests[i].digest());
1981         }
1982         return base64Digests;
1983     }
1984 
1985     /*
1986      * Computes the digests of a zip entry, and returns them as a list of
1987      * attributes
1988      */
1989     private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
1990                                            MessageDigest[] digests)

1991         throws IOException {
1992 
1993         String[] base64Digests = getDigests(ze, zf, digests);
1994         Attributes attrs = new Attributes();
1995 
1996         for (int i=0; i<digests.length; i++) {
1997             attrs.putValue(digests[i].getAlgorithm()+"-Digest",
1998                            base64Digests[i]);
1999         }
2000         return attrs;
2001     }
2002 
2003     /*
2004      * Updates the digest attributes of a manifest entry, by adding or
2005      * replacing digest values.
2006      * A digest value is added if the manifest entry does not contain a digest
2007      * for that particular algorithm.
2008      * A digest value is replaced if it is obsolete.
2009      *
2010      * Returns true if the manifest entry has been changed, and false
2011      * otherwise.
2012      */
2013     private boolean updateDigests(ZipEntry ze, ZipFile zf,
2014                                   MessageDigest[] digests,

2015                                   Manifest mf) throws IOException {
2016         boolean update = false;
2017 
2018         Attributes attrs = mf.getAttributes(ze.getName());
2019         String[] base64Digests = getDigests(ze, zf, digests);
2020 
2021         for (int i=0; i<digests.length; i++) {
2022             // The entry name to be written into attrs
2023             String name = null;
2024             try {
2025                 // Find if the digest already exists
2026                 AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
2027                 for (Object key: attrs.keySet()) {
2028                     if (key instanceof Attributes.Name) {
2029                         String n = ((Attributes.Name)key).toString();
2030                         if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
2031                             String tmp = n.substring(0, n.length() - 7);
2032                             if (AlgorithmId.get(tmp).equals(aid)) {
2033                                 name = n;
2034                                 break;
2035                             }
2036                         }
2037                     }
2038                 }
2039             } catch (NoSuchAlgorithmException nsae) {


2072         cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
2073         cpString = PathList.appendPath(signerClassPath, cpString);
2074         URL[] urls = PathList.pathToURLs(cpString);
2075         ClassLoader appClassLoader = new URLClassLoader(urls);
2076 
2077         // attempt to find signer
2078         Class<?> signerClass = appClassLoader.loadClass(signerClassName);
2079 
2080         // Check that it implements ContentSigner
2081         Object signer = signerClass.newInstance();
2082         if (!(signer instanceof ContentSigner)) {
2083             MessageFormat form = new MessageFormat(
2084                 rb.getString("signerClass.is.not.a.signing.mechanism"));
2085             Object[] source = {signerClass.getName()};
2086             throw new IllegalArgumentException(form.format(source));
2087         }
2088         return (ContentSigner)signer;
2089     }
2090 }
2091 













2092 class SignatureFile {
2093 
2094     /** SignatureFile */
2095     Manifest sf;
2096 
2097     /** .SF base name */
2098     String baseName;
2099 
2100     public SignatureFile(MessageDigest digests[],
2101                          Manifest mf,
2102                          ManifestDigester md,
2103                          String baseName,
2104                          boolean signManifest)
2105 
2106     {
2107         this.baseName = baseName;
2108 
2109         String version = System.getProperty("java.version");
2110         String javaVendor = System.getProperty("java.vendor");
2111 
2112         sf = new Manifest();
2113         Attributes mattr = sf.getMainAttributes();

2114 
2115         mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
2116         mattr.putValue("Created-By", version + " (" + javaVendor + ")");
2117 
2118         if (signManifest) {
2119             // sign the whole manifest
2120             for (int i=0; i < digests.length; i++) {
2121                 mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
2122                                Base64.getEncoder().encodeToString(md.manifestDigest(digests[i])));
2123             }
2124         }
2125 
2126         // create digest of the manifest main attributes
2127         ManifestDigester.Entry mde =
2128                 md.get(ManifestDigester.MF_MAIN_ATTRS, false);
2129         if (mde != null) {
2130             for (int i=0; i < digests.length; i++) {
2131                 mattr.putValue(digests[i].getAlgorithm() +
2132                         "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
2133                         Base64.getEncoder().encodeToString(mde.digest(digests[i])));
2134             }
2135         } else {
2136             throw new IllegalStateException
2137                 ("ManifestDigester failed to create " +
2138                 "Manifest-Main-Attribute entry");
2139         }
2140 
2141         /* go through the manifest entries and create the digests */
2142 
2143         Map<String,Attributes> entries = sf.getEntries();
2144         Iterator<Map.Entry<String,Attributes>> mit =
2145                                 mf.getEntries().entrySet().iterator();
2146         while(mit.hasNext()) {
2147             Map.Entry<String,Attributes> e = mit.next();
2148             String name = e.getKey();
2149             mde = md.get(name, false);
2150             if (mde != null) {
2151                 Attributes attr = new Attributes();
2152                 for (int i=0; i < digests.length; i++) {
2153                     attr.putValue(digests[i].getAlgorithm()+"-Digest",
2154                                   Base64.getEncoder().encodeToString(mde.digest(digests[i])));
2155                 }
2156                 entries.put(name, attr);
2157             }
2158         }
2159     }
2160 
2161     /**
2162      * Writes the SignatureFile to the specified OutputStream.
2163      *
2164      * @param out the output stream
2165      * @exception IOException if an I/O error has occurred
2166      */
2167 
2168     public void write(OutputStream out) throws IOException
2169     {
2170         sf.write(out);
2171     }
2172 
2173     /**
2174      * get .SF file name