1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
934 mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
935 + ")");
936 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
937 mfCreated = true;
938 }
939
940 /*
941 * For each entry in jar
942 * (except for signature-related META-INF entries),
943 * do the following:
944 *
945 * - if entry is not contained in manifest, add it to manifest;
946 * - if entry is contained in manifest, calculate its hash and
947 * compare it with the one in the manifest; if they are
948 * different, replace the hash in the manifest with the newly
949 * generated one. (This may invalidate existing signatures!)
950 */
951 BASE64Encoder encoder = new JarBASE64Encoder();
952 Vector<ZipEntry> mfFiles = new Vector<ZipEntry>();
953
954 for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
955 enum_.hasMoreElements();) {
956 ZipEntry ze = enum_.nextElement();
957
958 if (ze.getName().startsWith(META_INF)) {
959 // Store META-INF files in vector, so they can be written
960 // out first
961 mfFiles.addElement(ze);
962
963 if (signatureRelated(ze.getName())) {
964 // ignore signature-related and manifest files
965 continue;
966 }
967 }
968
969 if (manifest.getAttributes(ze.getName()) != null) {
970 // jar entry is contained in manifest, check and
971 // possibly update its digest attributes
972 if (updateDigests(ze, zipFile, digests, encoder,
973 manifest) == true) {
974 mfModified = true;
975 }
976 } else if (!ze.isDirectory()) {
977 // Add entry to manifest
978 Attributes attrs = getDigestAttributes(ze, zipFile,
979 digests,
980 encoder);
981 mfEntries.put(ze.getName(), attrs);
982 mfModified = true;
983 }
984 }
985
986 // Recalculate the manifest raw bytes if necessary
987 if (mfModified) {
988 ByteArrayOutputStream baos = new ByteArrayOutputStream();
989 manifest.write(baos);
990 byte[] newBytes = baos.toByteArray();
991 if (mfRawBytes != null
992 && oldAttr.equals(manifest.getMainAttributes())) {
993
994 /*
995 * Note:
996 *
997 * The Attributes object is based on HashMap and can handle
998 * continuation columns. Therefore, even if the contents are
999 * not changed (in a Map view), the bytes that it write()
1000 * may be different from the original bytes that it read()
1001 * from. Since the signature on the main attributes is based
1002 * on raw bytes, we must retain the exact bytes.
1003 */
1004
1005 int newPos = findHeaderEnd(newBytes);
1006 int oldPos = findHeaderEnd(mfRawBytes);
1007
1008 if (newPos == oldPos) {
1009 System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
1010 } else {
1011 // cat oldHead newTail > newBytes
1012 byte[] lastBytes = new byte[oldPos +
1013 newBytes.length - newPos];
1014 System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
1015 System.arraycopy(newBytes, newPos, lastBytes, oldPos,
1016 newBytes.length - newPos);
1017 newBytes = lastBytes;
1018 }
1019 }
1020 mfRawBytes = newBytes;
1021 }
1022
1023 // Write out the manifest
1024 if (mfModified) {
1025 // manifest file has new length
1026 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1027 }
1028 if (verbose) {
1029 if (mfCreated) {
1030 System.out.println(rb.getString(" adding: ") +
1031 mfFile.getName());
1032 } else if (mfModified) {
1033 System.out.println(rb.getString(" updating: ") +
1034 mfFile.getName());
1035 }
1036 }
1037 zos.putNextEntry(mfFile);
1038 zos.write(mfRawBytes);
1039
1040 // Calculate SignatureFile (".SF") and SignatureBlockFile
1216 if (hasExpiredCert) {
1217 System.out.println(
1218 rb.getString("The signer certificate has expired."));
1219 } else if (hasExpiringCert) {
1220 System.out.println(
1221 rb.getString("The signer certificate will expire within six months."));
1222 } else if (notYetValidCert) {
1223 System.out.println(
1224 rb.getString("The signer certificate is not yet valid."));
1225 }
1226 }
1227
1228 // no IOException thrown in the above try clause, so disable
1229 // the catch clause.
1230 // } catch(IOException ioe) {
1231 // error(rb.getString("unable to sign jar: ")+ioe, ioe);
1232 // }
1233 }
1234
1235 /**
1236 * Find the position of an empty line inside bs
1237 */
1238 private int findHeaderEnd(byte[] bs) {
1239 // An empty line can be at the beginning...
1240 if (bs.length > 1 && bs[0] == '\r' && bs[1] == '\n') {
1241 return 0;
1242 }
1243 // ... or after another line
1244 for (int i=0; i<bs.length-3; i++) {
1245 if (bs[i] == '\r' && bs[i+1] == '\n' &&
1246 bs[i+2] == '\r' && bs[i+3] == '\n') {
1247 return i;
1248 }
1249 }
1250 // If header end is not found, return 0,
1251 // which means no behavior change.
1252 return 0;
1253 }
1254
1255 /**
1256 * signature-related files include:
1257 * . META-INF/MANIFEST.MF
1258 * . META-INF/SIG-*
1259 * . META-INF/*.SF
1260 * . META-INF/*.DSA
1261 * . META-INF/*.RSA
1262 */
1263 private boolean signatureRelated(String name) {
1264 String ucName = name.toUpperCase();
1265 if (ucName.equals(JarFile.MANIFEST_NAME) ||
1266 ucName.equals(META_INF) ||
1267 (ucName.startsWith(SIG_PREFIX) &&
1268 ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
1269 return true;
1270 }
1271
1272 if (ucName.startsWith(META_INF) &&
|
1 /*
2 * Copyright 1997-2010 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
934 mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
935 + ")");
936 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
937 mfCreated = true;
938 }
939
940 /*
941 * For each entry in jar
942 * (except for signature-related META-INF entries),
943 * do the following:
944 *
945 * - if entry is not contained in manifest, add it to manifest;
946 * - if entry is contained in manifest, calculate its hash and
947 * compare it with the one in the manifest; if they are
948 * different, replace the hash in the manifest with the newly
949 * generated one. (This may invalidate existing signatures!)
950 */
951 BASE64Encoder encoder = new JarBASE64Encoder();
952 Vector<ZipEntry> mfFiles = new Vector<ZipEntry>();
953
954 boolean wasSigned = false;
955
956 for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
957 enum_.hasMoreElements();) {
958 ZipEntry ze = enum_.nextElement();
959
960 if (ze.getName().startsWith(META_INF)) {
961 // Store META-INF files in vector, so they can be written
962 // out first
963 mfFiles.addElement(ze);
964
965 if (SignatureFileVerifier.isBlockOrSF(
966 ze.getName().toUpperCase(Locale.ENGLISH))) {
967 wasSigned = true;
968 }
969
970 if (signatureRelated(ze.getName())) {
971 // ignore signature-related and manifest files
972 continue;
973 }
974 }
975
976 if (manifest.getAttributes(ze.getName()) != null) {
977 // jar entry is contained in manifest, check and
978 // possibly update its digest attributes
979 if (updateDigests(ze, zipFile, digests, encoder,
980 manifest) == true) {
981 mfModified = true;
982 }
983 } else if (!ze.isDirectory()) {
984 // Add entry to manifest
985 Attributes attrs = getDigestAttributes(ze, zipFile,
986 digests,
987 encoder);
988 mfEntries.put(ze.getName(), attrs);
989 mfModified = true;
990 }
991 }
992
993 // Recalculate the manifest raw bytes if necessary
994 if (mfModified) {
995 ByteArrayOutputStream baos = new ByteArrayOutputStream();
996 manifest.write(baos);
997 if (wasSigned) {
998 byte[] newBytes = baos.toByteArray();
999 if (mfRawBytes != null
1000 && oldAttr.equals(manifest.getMainAttributes())) {
1001
1002 /*
1003 * Note:
1004 *
1005 * The Attributes object is based on HashMap and can handle
1006 * continuation columns. Therefore, even if the contents are
1007 * not changed (in a Map view), the bytes that it write()
1008 * may be different from the original bytes that it read()
1009 * from. Since the signature on the main attributes is based
1010 * on raw bytes, we must retain the exact bytes.
1011 */
1012
1013 int newPos = findHeaderEnd(newBytes);
1014 int oldPos = findHeaderEnd(mfRawBytes);
1015
1016 if (newPos == oldPos) {
1017 System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
1018 } else {
1019 // cat oldHead newTail > newBytes
1020 byte[] lastBytes = new byte[oldPos +
1021 newBytes.length - newPos];
1022 System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
1023 System.arraycopy(newBytes, newPos, lastBytes, oldPos,
1024 newBytes.length - newPos);
1025 newBytes = lastBytes;
1026 }
1027 }
1028 mfRawBytes = newBytes;
1029 } else {
1030 mfRawBytes = baos.toByteArray();
1031 }
1032 }
1033
1034 // Write out the manifest
1035 if (mfModified) {
1036 // manifest file has new length
1037 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
1038 }
1039 if (verbose) {
1040 if (mfCreated) {
1041 System.out.println(rb.getString(" adding: ") +
1042 mfFile.getName());
1043 } else if (mfModified) {
1044 System.out.println(rb.getString(" updating: ") +
1045 mfFile.getName());
1046 }
1047 }
1048 zos.putNextEntry(mfFile);
1049 zos.write(mfRawBytes);
1050
1051 // Calculate SignatureFile (".SF") and SignatureBlockFile
1227 if (hasExpiredCert) {
1228 System.out.println(
1229 rb.getString("The signer certificate has expired."));
1230 } else if (hasExpiringCert) {
1231 System.out.println(
1232 rb.getString("The signer certificate will expire within six months."));
1233 } else if (notYetValidCert) {
1234 System.out.println(
1235 rb.getString("The signer certificate is not yet valid."));
1236 }
1237 }
1238
1239 // no IOException thrown in the above try clause, so disable
1240 // the catch clause.
1241 // } catch(IOException ioe) {
1242 // error(rb.getString("unable to sign jar: ")+ioe, ioe);
1243 // }
1244 }
1245
1246 /**
1247 * Find the length of header inside bs. The header is a multiple (>=0)
1248 * lines of attributes plus an empty line. The empty line is included
1249 * in the header.
1250 */
1251 private int findHeaderEnd(byte[] bs) {
1252 // Initial state true to deal with empty header
1253 boolean newline = true; // just met a newline
1254 int len = bs.length;
1255 for (int i=0; i<len; i++) {
1256 switch (bs[i]) {
1257 case '\r':
1258 if (i < len - 1 && bs[i+1] == '\n') i++;
1259 // fallthrough
1260 case '\n':
1261 if (newline) return i+1; //+1 to get length
1262 newline = true;
1263 break;
1264 default:
1265 newline = false;
1266 }
1267 }
1268 // If header end is not found, it means the MANIFEST.MF has only
1269 // the main attributes section and it does not end with 2 newlines.
1270 // Returns the whole length so that it can be completely replaced.
1271 return len;
1272 }
1273
1274 /**
1275 * signature-related files include:
1276 * . META-INF/MANIFEST.MF
1277 * . META-INF/SIG-*
1278 * . META-INF/*.SF
1279 * . META-INF/*.DSA
1280 * . META-INF/*.RSA
1281 */
1282 private boolean signatureRelated(String name) {
1283 String ucName = name.toUpperCase();
1284 if (ucName.equals(JarFile.MANIFEST_NAME) ||
1285 ucName.equals(META_INF) ||
1286 (ucName.startsWith(SIG_PREFIX) &&
1287 ucName.indexOf("/") == ucName.lastIndexOf("/"))) {
1288 return true;
1289 }
1290
1291 if (ucName.startsWith(META_INF) &&
|