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 {
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 }
|
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));
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 {
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 throws IOException {
742 AtomicReference<IOException> toThrow = new AtomicReference<>();
743 String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params);
744 String keyChain = SIGNING_KEYCHAIN.fetchFrom(params);
745
746 // sign all dylibs and executables
747 try (Stream<Path> stream = Files.walk(appLocation)) {
748 stream.peek(path -> { // fix permissions
749 try {
750 Set<PosixFilePermission> pfp =
751 Files.getPosixFilePermissions(path);
752 if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) {
753 pfp = EnumSet.copyOf(pfp);
754 pfp.add(PosixFilePermission.OWNER_WRITE);
755 Files.setPosixFilePermissions(path, pfp);
756 }
757 } catch (IOException e) {
758 Log.verbose(e);
759 }
760 }).filter(p -> Files.isRegularFile(p) &&
761 (Files.isExecutable(p) || p.toString().endsWith(".dylib"))
762 && !(p.toString().endsWith(appExecutable)
763 || p.toString().contains("/Contents/runtime")
764 || p.toString().contains("/Contents/Frameworks"))
765 ).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 List<String> args = new ArrayList<>();
782 args.addAll(Arrays.asList("codesign",
783 "--timestamp",
784 "--options", "runtime",
785 "-s", signingIdentity,
786 "--prefix", identifierPrefix,
787 "-vvvv"));
788 if (keyChain != null && !keyChain.isEmpty()) {
789 args.add("--keychain");
790 args.add(keyChain);
791 }
792 args.add(p.toString());
793
794 try {
795 Set<PosixFilePermission> oldPermissions =
796 Files.getPosixFilePermissions(p);
797 File f = p.toFile();
798 f.setWritable(true, true);
799
800 ProcessBuilder pb = new ProcessBuilder(args);
801
802 IOUtils.exec(pb);
803
804 Files.setPosixFilePermissions(p, oldPermissions);
805 } catch (IOException ioe) {
806 toThrow.set(ioe);
807 }
808 }
809 });
810 }
811 IOException ioe = toThrow.get();
812 if (ioe != null) {
813 throw ioe;
814 }
815
816 // sign all runtime and frameworks
817 Consumer<? super Path> signIdentifiedByPList = path -> {
818 //noinspection ThrowableResultOfMethodCallIgnored
819 if (toThrow.get() != null) return;
820
821 try {
822 List<String> args = new ArrayList<>();
823 args.addAll(Arrays.asList("codesign",
824 "--timestamp",
825 "--options", "runtime",
826 "--deep",
827 "--force",
828 "-s", signingIdentity, // sign with this key
829 "--prefix", identifierPrefix,
830 // use the identifier as a prefix
831 "-vvvv"));
832
833 if (keyChain != null && !keyChain.isEmpty()) {
834 args.add("--keychain");
835 args.add(keyChain);
836 }
837 args.add(path.toString());
838 ProcessBuilder pb = new ProcessBuilder(args);
839
840 IOUtils.exec(pb);
841 } catch (IOException e) {
842 toThrow.set(e);
843 }
844 };
845
846 Path javaPath = appLocation.resolve("Contents/runtime");
847 if (Files.isDirectory(javaPath)) {
848 signIdentifiedByPList.accept(javaPath);
849
850 ioe = toThrow.get();
851 if (ioe != null) {
852 throw ioe;
853 }
854 }
855 Path frameworkPath = appLocation.resolve("Contents/Frameworks");
856 if (Files.isDirectory(frameworkPath)) {
857 Files.list(frameworkPath)
858 .forEach(signIdentifiedByPList);
859
860 ioe = toThrow.get();
861 if (ioe != null) {
862 throw ioe;
863 }
864 }
865
866 // sign the app itself
867 List<String> args = new ArrayList<>();
868 args.addAll(Arrays.asList("codesign",
869 "--timestamp",
870 "--options", "runtime",
871 "--deep",
872 "--force",
873 "-s", signingIdentity,
874 "-vvvv"));
875 if (keyChain != null && !keyChain.isEmpty()) {
876 args.add("--keychain");
877 args.add(keyChain);
878 }
879 args.add(appLocation.toString());
880
881 ProcessBuilder pb =
882 new ProcessBuilder(args.toArray(new String[args.size()]));
883
884 IOUtils.exec(pb);
885 }
886
887 private static boolean isFileSigned(Path file) {
888 ProcessBuilder pb =
889 new ProcessBuilder("codesign", "--verify", file.toString());
890
891 try {
892 IOUtils.exec(pb);
893 } catch (IOException ex) {
894 return false;
895 }
896
897 return true;
898 }
899
900 private static String extractBundleIdentifier(Map<String, Object> params) {
901 if (PREDEFINED_APP_IMAGE.fetchFrom(params) == null) {
902 return null;
903 }
|