src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java
changeset 57488 94691d8e746f
parent 48760 25725c11c296
equal deleted inserted replaced
57487:643978a35f6e 57488:94691d8e746f
     1 /*
     1 /*
     2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
   669             // Should not happen. User provided alg were checked, and default
   669             // Should not happen. User provided alg were checked, and default
   670             // alg should always be available.
   670             // alg should always be available.
   671             throw new AssertionError(asae);
   671             throw new AssertionError(asae);
   672         }
   672         }
   673 
   673 
   674         PrintStream ps = new PrintStream(os);
   674         ZipOutputStream zos = new ZipOutputStream(os);
   675         ZipOutputStream zos = new ZipOutputStream(ps);
       
   676 
   675 
   677         Manifest manifest = new Manifest();
   676         Manifest manifest = new Manifest();
   678         Map<String, Attributes> mfEntries = manifest.getEntries();
       
   679 
       
   680         // The Attributes of manifest before updating
       
   681         Attributes oldAttr = null;
       
   682 
       
   683         boolean mfModified = false;
       
   684         boolean mfCreated = false;
       
   685         byte[] mfRawBytes = null;
   677         byte[] mfRawBytes = null;
   686 
   678 
   687         // Check if manifest exists
   679         // Check if manifest exists
   688         ZipEntry mfFile;
   680         ZipEntry mfFile = getManifestFile(zipFile);
   689         if ((mfFile = getManifestFile(zipFile)) != null) {
   681         boolean mfCreated = mfFile == null;
       
   682         if (!mfCreated) {
   690             // Manifest exists. Read its raw bytes.
   683             // Manifest exists. Read its raw bytes.
   691             mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes();
   684             mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes();
   692             manifest.read(new ByteArrayInputStream(mfRawBytes));
   685             manifest.read(new ByteArrayInputStream(mfRawBytes));
   693             oldAttr = (Attributes) (manifest.getMainAttributes().clone());
       
   694         } else {
   686         } else {
   695             // Create new manifest
   687             // Create new manifest
   696             Attributes mattr = manifest.getMainAttributes();
   688             Attributes mattr = manifest.getMainAttributes();
   697             mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
   689             mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
   698                     "1.0");
   690                     "1.0");
   699             String javaVendor = System.getProperty("java.vendor");
   691             String javaVendor = System.getProperty("java.vendor");
   700             String jdkVersion = System.getProperty("java.version");
   692             String jdkVersion = System.getProperty("java.version");
   701             mattr.putValue("Created-By", jdkVersion + " (" + javaVendor
   693             mattr.putValue("Created-By", jdkVersion + " (" + javaVendor
   702                     + ")");
   694                     + ")");
   703             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
   695             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
   704             mfCreated = true;
       
   705         }
   696         }
   706 
   697 
   707         /*
   698         /*
   708          * For each entry in jar
   699          * For each entry in jar
   709          * (except for signature-related META-INF entries),
   700          * (except for signature-related META-INF entries),
   726             if (ze.getName().startsWith(META_INF)) {
   717             if (ze.getName().startsWith(META_INF)) {
   727                 // Store META-INF files in vector, so they can be written
   718                 // Store META-INF files in vector, so they can be written
   728                 // out first
   719                 // out first
   729                 mfFiles.addElement(ze);
   720                 mfFiles.addElement(ze);
   730 
   721 
   731                 if (SignatureFileVerifier.isBlockOrSF(
   722                 String zeNameUp = ze.getName().toUpperCase(Locale.ENGLISH);
   732                         ze.getName().toUpperCase(Locale.ENGLISH))) {
   723                 if (SignatureFileVerifier.isBlockOrSF(zeNameUp)
       
   724                     // no need to preserve binary manifest portions
       
   725                     // if the only existing signature will be replaced
       
   726                         && !zeNameUp.startsWith(SignatureFile
       
   727                             .getBaseSignatureFilesName(signerName))) {
   733                     wasSigned = true;
   728                     wasSigned = true;
   734                 }
   729                 }
   735 
   730 
   736                 if (SignatureFileVerifier.isSigningRelated(ze.getName())) {
   731                 if (SignatureFileVerifier.isSigningRelated(ze.getName())) {
   737                     // ignore signature-related and manifest files
   732                     // ignore signature-related and manifest files
   740             }
   735             }
   741 
   736 
   742             if (manifest.getAttributes(ze.getName()) != null) {
   737             if (manifest.getAttributes(ze.getName()) != null) {
   743                 // jar entry is contained in manifest, check and
   738                 // jar entry is contained in manifest, check and
   744                 // possibly update its digest attributes
   739                 // possibly update its digest attributes
   745                 if (updateDigests(ze, zipFile, digests,
   740                 updateDigests(ze, zipFile, digests, manifest);
   746                         manifest)) {
       
   747                     mfModified = true;
       
   748                 }
       
   749             } else if (!ze.isDirectory()) {
   741             } else if (!ze.isDirectory()) {
   750                 // Add entry to manifest
   742                 // Add entry to manifest
   751                 Attributes attrs = getDigestAttributes(ze, zipFile, digests);
   743                 Attributes attrs = getDigestAttributes(ze, zipFile, digests);
   752                 mfEntries.put(ze.getName(), attrs);
   744                 manifest.getEntries().put(ze.getName(), attrs);
   753                 mfModified = true;
   745             }
   754             }
   746         }
   755         }
   747 
   756 
   748         /*
   757         // Recalculate the manifest raw bytes if necessary
   749          * Note:
   758         if (mfModified) {
   750          *
   759             ByteArrayOutputStream baos = new ByteArrayOutputStream();
   751          * The Attributes object is based on HashMap and can handle
       
   752          * continuation lines. Therefore, even if the contents are not changed
       
   753          * (in a Map view), the bytes that it write() may be different from
       
   754          * the original bytes that it read() from. Since the signature is
       
   755          * based on raw bytes, we must retain the exact bytes.
       
   756          */
       
   757         boolean mfModified;
       
   758         ByteArrayOutputStream baos = new ByteArrayOutputStream();
       
   759         if (mfCreated || !wasSigned) {
       
   760             mfModified = true;
   760             manifest.write(baos);
   761             manifest.write(baos);
   761             if (wasSigned) {
   762             mfRawBytes = baos.toByteArray();
   762                 byte[] newBytes = baos.toByteArray();
   763         } else {
   763                 if (mfRawBytes != null
   764 
   764                         && oldAttr.equals(manifest.getMainAttributes())) {
   765             // the manifest before updating
   765 
   766             Manifest oldManifest = new Manifest(
   766                     /*
   767                     new ByteArrayInputStream(mfRawBytes));
   767                      * Note:
   768             mfModified = !oldManifest.equals(manifest);
   768                      *
   769             if (!mfModified) {
   769                      * The Attributes object is based on HashMap and can handle
   770                 // leave whole manifest (mfRawBytes) unmodified
   770                      * continuation columns. Therefore, even if the contents are
   771             } else {
   771                      * not changed (in a Map view), the bytes that it write()
   772                 // reproduce the manifest raw bytes for unmodified sections
   772                      * may be different from the original bytes that it read()
   773                 manifest.write(baos);
   773                      * from. Since the signature on the main attributes is based
   774                 byte[] mfNewRawBytes = baos.toByteArray();
   774                      * on raw bytes, we must retain the exact bytes.
   775                 baos.reset();
   775                      */
   776 
   776 
   777                 ManifestDigester oldMd = new ManifestDigester(mfRawBytes);
   777                     int newPos = findHeaderEnd(newBytes);
   778                 ManifestDigester newMd = new ManifestDigester(mfNewRawBytes);
   778                     int oldPos = findHeaderEnd(mfRawBytes);
   779 
   779 
   780                 // main attributes
   780                     if (newPos == oldPos) {
   781                 if (manifest.getMainAttributes().equals(
   781                         System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
   782                         oldManifest.getMainAttributes())
       
   783                         && (manifest.getEntries().isEmpty() ||
       
   784                             oldMd.getMainAttsEntry().isProperlyDelimited())) {
       
   785                     oldMd.getMainAttsEntry().reproduceRaw(baos);
       
   786                 } else {
       
   787                     newMd.getMainAttsEntry().reproduceRaw(baos);
       
   788                 }
       
   789 
       
   790                 // individual sections
       
   791                 for (Map.Entry<String,Attributes> entry :
       
   792                         manifest.getEntries().entrySet()) {
       
   793                     String sectionName = entry.getKey();
       
   794                     Attributes entryAtts = entry.getValue();
       
   795                     if (entryAtts.equals(oldManifest.getAttributes(sectionName))
       
   796                             && oldMd.get(sectionName).isProperlyDelimited()) {
       
   797                         oldMd.get(sectionName).reproduceRaw(baos);
   782                     } else {
   798                     } else {
   783                         // cat oldHead newTail > newBytes
   799                         newMd.get(sectionName).reproduceRaw(baos);
   784                         byte[] lastBytes = new byte[oldPos +
       
   785                                 newBytes.length - newPos];
       
   786                         System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
       
   787                         System.arraycopy(newBytes, newPos, lastBytes, oldPos,
       
   788                                 newBytes.length - newPos);
       
   789                         newBytes = lastBytes;
       
   790                     }
   800                     }
   791                 }
   801                 }
   792                 mfRawBytes = newBytes;
   802 
   793             } else {
       
   794                 mfRawBytes = baos.toByteArray();
   803                 mfRawBytes = baos.toByteArray();
   795             }
   804             }
   796         }
   805         }
   797 
   806 
   798         // Write out the manifest
   807         // Write out the manifest
   799         if (mfModified) {
   808         if (mfModified) {
   800             // manifest file has new length
   809             // manifest file has new length
   801             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
   810             mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
   802         }
   811         }
   803         if (handler != null) {
   812         if (handler != null) {
   804             if (mfCreated) {
   813             if (mfCreated || !mfModified) {
   805                 handler.accept("adding", mfFile.getName());
   814                 handler.accept("adding", mfFile.getName());
   806             } else if (mfModified) {
   815             } else {
   807                 handler.accept("updating", mfFile.getName());
   816                 handler.accept("updating", mfFile.getName());
   808             }
   817             }
   809         }
   818         }
   810 
       
   811         zos.putNextEntry(mfFile);
   819         zos.putNextEntry(mfFile);
   812         zos.write(mfRawBytes);
   820         zos.write(mfRawBytes);
   813 
   821 
   814         // Calculate SignatureFile (".SF") and SignatureBlockFile
   822         // Calculate SignatureFile (".SF") and SignatureBlockFile
   815         ManifestDigester manDig = new ManifestDigester(mfRawBytes);
   823         ManifestDigester manDig = new ManifestDigester(mfRawBytes);
   824         } else {
   832         } else {
   825             signer = Signature.getInstance(sigalg, sigProvider);
   833             signer = Signature.getInstance(sigalg, sigProvider);
   826         }
   834         }
   827         signer.initSign(privateKey);
   835         signer.initSign(privateKey);
   828 
   836 
   829         ByteArrayOutputStream baos = new ByteArrayOutputStream();
   837         baos.reset();
   830         sf.write(baos);
   838         sf.write(baos);
   831 
       
   832         byte[] content = baos.toByteArray();
   839         byte[] content = baos.toByteArray();
   833 
   840 
   834         signer.update(content);
   841         signer.update(content);
   835         byte[] signature = signer.sign();
   842         byte[] signature = signer.sign();
   836 
   843 
   887         for (int i = 0; i < mfFiles.size(); i++) {
   894         for (int i = 0; i < mfFiles.size(); i++) {
   888             ZipEntry ze = mfFiles.elementAt(i);
   895             ZipEntry ze = mfFiles.elementAt(i);
   889             if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
   896             if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
   890                     && !ze.getName().equalsIgnoreCase(sfFilename)
   897                     && !ze.getName().equalsIgnoreCase(sfFilename)
   891                     && !ze.getName().equalsIgnoreCase(bkFilename)) {
   898                     && !ze.getName().equalsIgnoreCase(bkFilename)) {
       
   899                 if (ze.getName().startsWith(SignatureFile
       
   900                         .getBaseSignatureFilesName(signerName))
       
   901                         && SignatureFileVerifier.isBlockOrSF(ze.getName())) {
       
   902                     if (handler != null) {
       
   903                         handler.accept("updating", ze.getName());
       
   904                     }
       
   905                     continue;
       
   906                 }
   892                 if (handler != null) {
   907                 if (handler != null) {
   893                     if (manifest.getAttributes(ze.getName()) != null) {
   908                     if (manifest.getAttributes(ze.getName()) != null) {
   894                         handler.accept("signing", ze.getName());
   909                         handler.accept("signing", ze.getName());
   895                     } else if (!ze.isDirectory()) {
   910                     } else if (!ze.isDirectory()) {
   896                         handler.accept("adding", ze.getName());
   911                         handler.accept("adding", ze.getName());
   940         try (InputStream is = zf.getInputStream(ze)) {
   955         try (InputStream is = zf.getInputStream(ze)) {
   941             is.transferTo(os);
   956             is.transferTo(os);
   942         }
   957         }
   943     }
   958     }
   944 
   959 
   945     private boolean updateDigests(ZipEntry ze, ZipFile zf,
   960     private void updateDigests(ZipEntry ze, ZipFile zf,
   946                                   MessageDigest[] digests,
   961                                   MessageDigest[] digests,
   947                                   Manifest mf) throws IOException {
   962                                   Manifest mf) throws IOException {
   948         boolean update = false;
       
   949 
       
   950         Attributes attrs = mf.getAttributes(ze.getName());
   963         Attributes attrs = mf.getAttributes(ze.getName());
   951         String[] base64Digests = getDigests(ze, zf, digests);
   964         String[] base64Digests = getDigests(ze, zf, digests);
   952 
   965 
   953         for (int i = 0; i < digests.length; i++) {
   966         for (int i = 0; i < digests.length; i++) {
   954             // The entry name to be written into attrs
   967             // The entry name to be written into attrs
   974                 // Ignored. Writing new digest entry.
   987                 // Ignored. Writing new digest entry.
   975             }
   988             }
   976 
   989 
   977             if (name == null) {
   990             if (name == null) {
   978                 name = digests[i].getAlgorithm() + "-Digest";
   991                 name = digests[i].getAlgorithm() + "-Digest";
   979                 attrs.putValue(name, base64Digests[i]);
   992             }
   980                 update = true;
   993             attrs.putValue(name, base64Digests[i]);
   981             } else {
   994         }
   982                 // compare digests, and replace the one in the manifest
       
   983                 // if they are different
       
   984                 String mfDigest = attrs.getValue(name);
       
   985                 if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
       
   986                     attrs.putValue(name, base64Digests[i]);
       
   987                     update = true;
       
   988                 }
       
   989             }
       
   990         }
       
   991         return update;
       
   992     }
   995     }
   993 
   996 
   994     private Attributes getDigestAttributes(
   997     private Attributes getDigestAttributes(
   995             ZipEntry ze, ZipFile zf, MessageDigest[] digests)
   998             ZipEntry ze, ZipFile zf, MessageDigest[] digests)
   996             throws IOException {
   999             throws IOException {
  1049                     .encodeToString(digests[i].digest());
  1052                     .encodeToString(digests[i].digest());
  1050         }
  1053         }
  1051         return base64Digests;
  1054         return base64Digests;
  1052     }
  1055     }
  1053 
  1056 
  1054     @SuppressWarnings("fallthrough")
       
  1055     private int findHeaderEnd(byte[] bs) {
       
  1056         // Initial state true to deal with empty header
       
  1057         boolean newline = true;     // just met a newline
       
  1058         int len = bs.length;
       
  1059         for (int i = 0; i < len; i++) {
       
  1060             switch (bs[i]) {
       
  1061                 case '\r':
       
  1062                     if (i < len - 1 && bs[i + 1] == '\n') i++;
       
  1063                     // fallthrough
       
  1064                 case '\n':
       
  1065                     if (newline) return i + 1;    //+1 to get length
       
  1066                     newline = true;
       
  1067                     break;
       
  1068                 default:
       
  1069                     newline = false;
       
  1070             }
       
  1071         }
       
  1072         // If header end is not found, it means the MANIFEST.MF has only
       
  1073         // the main attributes section and it does not end with 2 newlines.
       
  1074         // Returns the whole length so that it can be completely replaced.
       
  1075         return len;
       
  1076     }
       
  1077 
       
  1078     /*
  1057     /*
  1079      * Try to load the specified signing mechanism.
  1058      * Try to load the specified signing mechanism.
  1080      * The URL class loader is used.
  1059      * The URL class loader is used.
  1081      */
  1060      */
  1082     @SuppressWarnings("deprecation")
  1061     @SuppressWarnings("deprecation")
  1143                                     md.manifestDigest(digest)));
  1122                                     md.manifestDigest(digest)));
  1144                 }
  1123                 }
  1145             }
  1124             }
  1146 
  1125 
  1147             // create digest of the manifest main attributes
  1126             // create digest of the manifest main attributes
  1148             ManifestDigester.Entry mde =
  1127             ManifestDigester.Entry mde = md.getMainAttsEntry(false);
  1149                     md.get(ManifestDigester.MF_MAIN_ATTRS, false);
       
  1150             if (mde != null) {
  1128             if (mde != null) {
  1151                 for (MessageDigest digest: digests) {
  1129                 for (MessageDigest digest : digests) {
  1152                     mattr.putValue(digest.getAlgorithm() +
  1130                     mattr.putValue(digest.getAlgorithm() + "-Digest-" +
  1153                                     "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
  1131                             ManifestDigester.MF_MAIN_ATTRS,
  1154                             Base64.getEncoder().encodeToString(
  1132                             Base64.getEncoder().encodeToString(mde.digest(digest)));
  1155                                     mde.digest(digest)));
       
  1156                 }
  1133                 }
  1157             } else {
  1134             } else {
  1158                 throw new IllegalStateException
  1135                 throw new IllegalStateException
  1159                         ("ManifestDigester failed to create " +
  1136                         ("ManifestDigester failed to create " +
  1160                                 "Manifest-Main-Attribute entry");
  1137                                 "Manifest-Main-Attribute entry");
  1179         // Write .SF file
  1156         // Write .SF file
  1180         public void write(OutputStream out) throws IOException {
  1157         public void write(OutputStream out) throws IOException {
  1181             sf.write(out);
  1158             sf.write(out);
  1182         }
  1159         }
  1183 
  1160 
       
  1161         private static String getBaseSignatureFilesName(String baseName) {
       
  1162             return "META-INF/" + baseName + ".";
       
  1163         }
       
  1164 
  1184         // get .SF file name
  1165         // get .SF file name
  1185         public String getMetaName() {
  1166         public String getMetaName() {
  1186             return "META-INF/" + baseName + ".SF";
  1167             return getBaseSignatureFilesName(baseName) + "SF";
  1187         }
  1168         }
  1188 
  1169 
  1189         // get .DSA (or .DSA, .EC) file name
  1170         // get .DSA (or .DSA, .EC) file name
  1190         public String getBlockName(PrivateKey privateKey) {
  1171         public String getBlockName(PrivateKey privateKey) {
  1191             String keyAlgorithm = privateKey.getAlgorithm();
  1172             String keyAlgorithm = privateKey.getAlgorithm();
  1192             return "META-INF/" + baseName + "." + keyAlgorithm;
  1173             return getBaseSignatureFilesName(baseName) + keyAlgorithm;
  1193         }
  1174         }
  1194 
  1175 
  1195         // Generates the PKCS#7 content of block file
  1176         // Generates the PKCS#7 content of block file
  1196         @SuppressWarnings("deprecation")
  1177         @SuppressWarnings("deprecation")
  1197         public byte[] generateBlock(ContentSignerParameters params,
  1178         public byte[] generateBlock(ContentSignerParameters params,