< prev index next >

src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal/MacAppImageBuilder.java

Print this page




 351         // JDK 9, 10, and 11 have extra '/jli/' subdir
 352         Path jli = runtimeRoot.resolve("lib/libjli.dylib");
 353         if (!Files.exists(jli)) {
 354             jli = runtimeRoot.resolve("lib/jli/libjli.dylib");
 355         }
 356 
 357         Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib"));
 358     }
 359 
 360     private void sign(Map<String, ? super Object> params) throws IOException {
 361         if (Optional.ofNullable(
 362                 SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
 363             try {
 364                 addNewKeychain(params);
 365             } catch (InterruptedException e) {
 366                 Log.error(e.getMessage());
 367             }
 368             String signingIdentity =
 369                     DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
 370             if (signingIdentity != null) {

 371                 signAppBundle(params, root, signingIdentity,
 372                         BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null);

 373             }
 374             restoreKeychainList(params);
 375         }
 376     }
 377 
 378     private String getLauncherName(Map<String, ? super Object> params) {












 379         if (APP_NAME.fetchFrom(params) != null) {
 380             return APP_NAME.fetchFrom(params);
 381         } else {
 382             return MAIN_CLASS.fetchFrom(params);
 383         }
 384     }
 385 
 386     public static String getLauncherCfgName(
 387             Map<String, ? super Object> params) {
 388         return "Contents/app/" + APP_NAME.fetchFrom(params) + ".cfg";
 389     }
 390 
 391     private void copyClassPathEntries(Path javaDirectory,
 392             Map<String, ? super Object> params) throws IOException {
 393         List<RelativeFileSet> resourcesList =
 394                 APP_RESOURCES_LIST.fetchFrom(params);
 395         if (resourcesList == null) {
 396             throw new RuntimeException(
 397                     I18N.getString("message.null-classpath"));
 398         }


 718                 Platform.getMinorVersion() < 12)) {
 719             // we need this for OS X 10.12+
 720             return;
 721         }
 722 
 723         if (keyChains == null || keyChains.isEmpty()) {
 724             return;
 725         }
 726 
 727         List<String> args = new ArrayList<>();
 728         args.add("security");
 729         args.add("list-keychains");
 730         args.add("-s");
 731 
 732         args.addAll(keyChains);
 733 
 734         ProcessBuilder  pb = new ProcessBuilder(args);
 735         IOUtils.exec(pb);
 736     }
 737 
 738     public static void signAppBundle(
 739             Map<String, ? super Object> params, Path appLocation,
 740             String signingIdentity, String identifierPrefix,
 741             String entitlementsFile, String inheritedEntitlements)
 742             throws IOException {
 743         AtomicReference<IOException> toThrow = new AtomicReference<>();
 744         String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params);
 745         String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
 746 
 747         // sign all dylibs and jars
 748         try (Stream<Path> stream = Files.walk(appLocation)) {
 749             stream.peek(path -> { // fix permissions
 750                 try {
 751                     Set<PosixFilePermission> pfp =
 752                             Files.getPosixFilePermissions(path);
 753                     if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) {
 754                         pfp = EnumSet.copyOf(pfp);
 755                         pfp.add(PosixFilePermission.OWNER_WRITE);
 756                         Files.setPosixFilePermissions(path, pfp);
 757                     }
 758                 } catch (IOException e) {
 759                     Log.verbose(e);
 760                 }
 761             }).filter(p -> Files.isRegularFile(p)
 762                       && !(p.toString().contains("/Contents/MacOS/libjli.dylib")
 763                       || p.toString().endsWith(appExecutable)
 764                       || p.toString().contains("/Contents/runtime")
 765                       || p.toString().contains("/Contents/Frameworks"))).forEach(p -> {
 766                 //noinspection ThrowableResultOfMethodCallIgnored

 767                 if (toThrow.get() != null) return;
 768 
 769                 // If p is a symlink then skip the signing process.
 770                 if (Files.isSymbolicLink(p)) {
 771                     if (VERBOSE.fetchFrom(params)) {
 772                         Log.verbose(MessageFormat.format(I18N.getString(
 773                                 "message.ignoring.symlink"), p.toString()));
 774                     }



 775                 } else {
 776                     if (p.toString().endsWith(LIBRARY_NAME)) {
 777                         if (isFileSigned(p)) {
 778                             return;
 779                         }
 780                     }
 781 
 782                     List<String> args = new ArrayList<>();
 783                     args.addAll(Arrays.asList("codesign",
 784                             "-s", signingIdentity, // sign with this key


 785                             "--prefix", identifierPrefix,
 786                             // use the identifier as a prefix
 787                             "-vvvv"));
 788                     if (entitlementsFile != null &&
 789                             (p.toString().endsWith(".jar")
 790                             || p.toString().endsWith(".dylib"))) {
 791                         args.add("--entitlements");
 792                         args.add(entitlementsFile); // entitlements
 793                     } else if (inheritedEntitlements != null &&
 794                             Files.isExecutable(p)) {
 795                         args.add("--entitlements");
 796                         args.add(inheritedEntitlements);
 797                         // inherited entitlements for executable processes
 798                     }
 799                     if (keyChain != null && !keyChain.isEmpty()) {
 800                         args.add("--keychain");
 801                         args.add(keyChain);
 802                     }








 803                     args.add(p.toString());
 804 
 805                     try {
 806                         Set<PosixFilePermission> oldPermissions =
 807                                 Files.getPosixFilePermissions(p);
 808                         File f = p.toFile();
 809                         f.setWritable(true, true);
 810 
 811                         ProcessBuilder pb = new ProcessBuilder(args);

 812                         IOUtils.exec(pb);
 813 
 814                         Files.setPosixFilePermissions(p, oldPermissions);
 815                     } catch (IOException ioe) {
 816                         toThrow.set(ioe);
 817                     }
 818                 }
 819             });
 820         }
 821         IOException ioe = toThrow.get();
 822         if (ioe != null) {
 823             throw ioe;
 824         }
 825 
 826         // sign all runtime and frameworks
 827         Consumer<? super Path> signIdentifiedByPList = path -> {
 828             //noinspection ThrowableResultOfMethodCallIgnored
 829             if (toThrow.get() != null) return;
 830 
 831             try {
 832                 List<String> args = new ArrayList<>();
 833                 args.addAll(Arrays.asList("codesign",
 834                         "-f",



 835                         "-s", signingIdentity, // sign with this key
 836                         "--prefix", identifierPrefix,
 837                         // use the identifier as a prefix
 838                         "-vvvv"));

 839                 if (keyChain != null && !keyChain.isEmpty()) {
 840                     args.add("--keychain");
 841                     args.add(keyChain);
 842                 }
 843                 args.add(path.toString());
 844                 ProcessBuilder pb = new ProcessBuilder(args);
 845                 IOUtils.exec(pb);
 846 
 847                 args = new ArrayList<>();
 848                 args.addAll(Arrays.asList("codesign",
 849                         "-s", signingIdentity, // sign with this key
 850                         "--prefix", identifierPrefix,
 851                         // use the identifier as a prefix
 852                         "-vvvv"));
 853                 if (keyChain != null && !keyChain.isEmpty()) {
 854                     args.add("--keychain");
 855                     args.add(keyChain);
 856                 }
 857                 args.add(path.toString()
 858                         + "/Contents/_CodeSignature/CodeResources");
 859                 pb = new ProcessBuilder(args);
 860                 IOUtils.exec(pb);
 861             } catch (IOException e) {
 862                 toThrow.set(e);
 863             }
 864         };
 865 
 866         Path javaPath = appLocation.resolve("Contents/runtime");
 867         if (Files.isDirectory(javaPath)) {
 868             signIdentifiedByPList.accept(javaPath);
 869 
 870             ioe = toThrow.get();
 871             if (ioe != null) {
 872                 throw ioe;
 873             }
 874         }
 875         Path frameworkPath = appLocation.resolve("Contents/Frameworks");
 876         if (Files.isDirectory(frameworkPath)) {
 877             Files.list(frameworkPath)
 878                     .forEach(signIdentifiedByPList);
 879 
 880             ioe = toThrow.get();
 881             if (ioe != null) {
 882                 throw ioe;
 883             }
 884         }
 885 
 886         // sign the app itself
 887         List<String> args = new ArrayList<>();
 888         args.addAll(Arrays.asList("codesign",
 889                 "-s", signingIdentity, // sign with this key
 890                 "-vvvv")); // super verbose output
 891         if (entitlementsFile != null) {
 892             args.add("--entitlements");
 893             args.add(entitlementsFile); // entitlements
 894         }

 895         if (keyChain != null && !keyChain.isEmpty()) {
 896             args.add("--keychain");
 897             args.add(keyChain);
 898         }






 899         args.add(appLocation.toString());
 900 
 901         ProcessBuilder pb =
 902                 new ProcessBuilder(args.toArray(new String[args.size()]));

 903         IOUtils.exec(pb);
 904     }
 905 
 906     private static boolean isFileSigned(Path file) {
 907         ProcessBuilder pb =
 908                 new ProcessBuilder("codesign", "--verify", file.toString());
 909 
 910         try {
 911             IOUtils.exec(pb);
 912         } catch (IOException ex) {
 913             return false;
 914         }
 915 
 916         return true;
 917     }
 918 
 919     private static String extractBundleIdentifier(Map<String, Object> params) {
 920         if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) {
 921             return null;
 922         }




 351         // JDK 9, 10, and 11 have extra '/jli/' subdir
 352         Path jli = runtimeRoot.resolve("lib/libjli.dylib");
 353         if (!Files.exists(jli)) {
 354             jli = runtimeRoot.resolve("lib/jli/libjli.dylib");
 355         }
 356 
 357         Files.copy(jli, runtimeMacOSDir.resolve("libjli.dylib"));
 358     }
 359 
 360     private void sign(Map<String, ? super Object> params) throws IOException {
 361         if (Optional.ofNullable(
 362                 SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
 363             try {
 364                 addNewKeychain(params);
 365             } catch (InterruptedException e) {
 366                 Log.error(e.getMessage());
 367             }
 368             String signingIdentity =
 369                     DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params);
 370             if (signingIdentity != null) {
 371                 prepareEntitlements(params);
 372                 signAppBundle(params, root, signingIdentity,
 373                         BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params),
 374                         getConfig_Entitlements(params));
 375             }
 376             restoreKeychainList(params);
 377         }
 378     }
 379 
 380     static File getConfig_Entitlements(Map<String, ? super Object> params) {
 381         return new File(CONFIG_ROOT.fetchFrom(params),
 382                 getLauncherName(params) + ".entitlements");
 383     }
 384 
 385     static void prepareEntitlements(Map<String, ? super Object> params)
 386             throws IOException {
 387         createResource("entitlements.plist", params)
 388                 .setCategory(I18N.getString("resource.entitlements"))
 389                 .saveToFile(getConfig_Entitlements(params));
 390     }
 391 
 392     private static String getLauncherName(Map<String, ? super Object> params) {
 393         if (APP_NAME.fetchFrom(params) != null) {
 394             return APP_NAME.fetchFrom(params);
 395         } else {
 396             return MAIN_CLASS.fetchFrom(params);
 397         }
 398     }
 399 
 400     public static String getLauncherCfgName(
 401             Map<String, ? super Object> params) {
 402         return "Contents/app/" + APP_NAME.fetchFrom(params) + ".cfg";
 403     }
 404 
 405     private void copyClassPathEntries(Path javaDirectory,
 406             Map<String, ? super Object> params) throws IOException {
 407         List<RelativeFileSet> resourcesList =
 408                 APP_RESOURCES_LIST.fetchFrom(params);
 409         if (resourcesList == null) {
 410             throw new RuntimeException(
 411                     I18N.getString("message.null-classpath"));
 412         }


 732                 Platform.getMinorVersion() < 12)) {
 733             // we need this for OS X 10.12+
 734             return;
 735         }
 736 
 737         if (keyChains == null || keyChains.isEmpty()) {
 738             return;
 739         }
 740 
 741         List<String> args = new ArrayList<>();
 742         args.add("security");
 743         args.add("list-keychains");
 744         args.add("-s");
 745 
 746         args.addAll(keyChains);
 747 
 748         ProcessBuilder  pb = new ProcessBuilder(args);
 749         IOUtils.exec(pb);
 750     }
 751 
 752     static void signAppBundle(
 753             Map<String, ? super Object> params, Path appLocation,
 754             String signingIdentity, String identifierPrefix, File entitlements)

 755             throws IOException {
 756         AtomicReference<IOException> toThrow = new AtomicReference<>();
 757         String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params);
 758         String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
 759 
 760         // sign all dylibs and executables
 761         try (Stream<Path> stream = Files.walk(appLocation)) {
 762             stream.peek(path -> { // fix permissions
 763                 try {
 764                     Set<PosixFilePermission> pfp =
 765                             Files.getPosixFilePermissions(path);
 766                     if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) {
 767                         pfp = EnumSet.copyOf(pfp);
 768                         pfp.add(PosixFilePermission.OWNER_WRITE);
 769                         Files.setPosixFilePermissions(path, pfp);
 770                     }
 771                 } catch (IOException e) {
 772                     Log.verbose(e);
 773                 }
 774             }).filter(p -> Files.isRegularFile(p) &&
 775                       (Files.isExecutable(p) || p.toString().endsWith(".dylib"))
 776                       && !(p.toString().endsWith(appExecutable)
 777                       || p.toString().contains("/Contents/runtime")
 778                       || p.toString().contains("/Contents/Frameworks"))
 779                      ).forEach(p -> {
 780                 // noinspection ThrowableResultOfMethodCallIgnored
 781                 if (toThrow.get() != null) return;
 782 
 783                 // If p is a symlink then skip the signing process.
 784                 if (Files.isSymbolicLink(p)) {

 785                     Log.verbose(MessageFormat.format(I18N.getString(
 786                             "message.ignoring.symlink"), p.toString()));
 787                 } else if (isFileSigned(p)) {
 788                     // executable or lib already signed
 789                     Log.verbose(MessageFormat.format(I18N.getString(
 790                             "message.already.signed"), p.toString()));
 791                 } else {






 792                     List<String> args = new ArrayList<>();
 793                     args.addAll(Arrays.asList("codesign",
 794                             "--timestamp",
 795                             "--options", "runtime",
 796                             "-s", signingIdentity,
 797                             "--prefix", identifierPrefix,

 798                             "-vvvv"));











 799                     if (keyChain != null && !keyChain.isEmpty()) {
 800                         args.add("--keychain");
 801                         args.add(keyChain);
 802                     }
 803 
 804                     if (Files.isExecutable(p)) {
 805                         if (entitlements != null) {
 806                             args.add("--entitlements");
 807                             args.add(entitlements.toString());
 808                         }
 809                     }
 810 
 811                     args.add(p.toString());
 812 
 813                     try {
 814                         Set<PosixFilePermission> oldPermissions =
 815                                 Files.getPosixFilePermissions(p);
 816                         File f = p.toFile();
 817                         f.setWritable(true, true);
 818 
 819                         ProcessBuilder pb = new ProcessBuilder(args);
 820 
 821                         IOUtils.exec(pb);
 822 
 823                         Files.setPosixFilePermissions(p, oldPermissions);
 824                     } catch (IOException ioe) {
 825                         toThrow.set(ioe);
 826                     }
 827                 }
 828             });
 829         }
 830         IOException ioe = toThrow.get();
 831         if (ioe != null) {
 832             throw ioe;
 833         }
 834 
 835         // sign all runtime and frameworks
 836         Consumer<? super Path> signIdentifiedByPList = path -> {
 837             //noinspection ThrowableResultOfMethodCallIgnored
 838             if (toThrow.get() != null) return;
 839 
 840             try {
 841                 List<String> args = new ArrayList<>();
 842                 args.addAll(Arrays.asList("codesign",
 843                         "--timestamp",
 844                         "--options", "runtime",
 845                         "--deep",
 846                         "--force",
 847                         "-s", signingIdentity, // sign with this key
 848                         "--prefix", identifierPrefix,
 849                         // use the identifier as a prefix
 850                         "-vvvv"));
 851 
 852                 if (keyChain != null && !keyChain.isEmpty()) {
 853                     args.add("--keychain");
 854                     args.add(keyChain);
 855                 }
 856                 args.add(path.toString());
 857                 ProcessBuilder pb = new ProcessBuilder(args);

 858 













 859                 IOUtils.exec(pb);
 860             } catch (IOException e) {
 861                 toThrow.set(e);
 862             }
 863         };
 864 
 865         Path javaPath = appLocation.resolve("Contents/runtime");
 866         if (Files.isDirectory(javaPath)) {
 867             signIdentifiedByPList.accept(javaPath);
 868 
 869             ioe = toThrow.get();
 870             if (ioe != null) {
 871                 throw ioe;
 872             }
 873         }
 874         Path frameworkPath = appLocation.resolve("Contents/Frameworks");
 875         if (Files.isDirectory(frameworkPath)) {
 876             Files.list(frameworkPath)
 877                     .forEach(signIdentifiedByPList);
 878 
 879             ioe = toThrow.get();
 880             if (ioe != null) {
 881                 throw ioe;
 882             }
 883         }
 884 
 885         // sign the app itself
 886         List<String> args = new ArrayList<>();
 887         args.addAll(Arrays.asList("codesign",
 888                 "--timestamp",
 889                 "--options", "runtime",
 890                 "--deep",
 891                 "--force",
 892                 "-s", signingIdentity,
 893                 "-vvvv"));
 894 
 895         if (keyChain != null && !keyChain.isEmpty()) {
 896             args.add("--keychain");
 897             args.add(keyChain);
 898         }
 899 
 900         if (entitlements != null) {
 901             args.add("--entitlements");
 902             args.add(entitlements.toString());
 903         }
 904 
 905         args.add(appLocation.toString());
 906 
 907         ProcessBuilder pb =
 908                 new ProcessBuilder(args.toArray(new String[args.size()]));
 909 
 910         IOUtils.exec(pb);
 911     }
 912 
 913     private static boolean isFileSigned(Path file) {
 914         ProcessBuilder pb =
 915                 new ProcessBuilder("codesign", "--verify", file.toString());
 916 
 917         try {
 918             IOUtils.exec(pb);
 919         } catch (IOException ex) {
 920             return false;
 921         }
 922 
 923         return true;
 924     }
 925 
 926     private static String extractBundleIdentifier(Map<String, Object> params) {
 927         if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) {
 928             return null;
 929         }


< prev index next >