src/share/classes/sun/security/tools/JarSigner.java

Print this page
rev 352 : 6948909: Jarsigner removes MANIFEST.MF info for badly packages jar's
Reviewed-by: mullan, xuelei
   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) &&