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
|