36 import java.text.MessageFormat; 37 import java.security.cert.Certificate; 38 import java.security.cert.X509Certificate; 39 import java.security.cert.CertificateException; 40 import java.security.*; 41 import java.lang.reflect.Constructor; 42 43 import com.sun.jarsigner.ContentSigner; 44 import com.sun.jarsigner.ContentSignerParameters; 45 import java.net.SocketTimeoutException; 46 import java.net.URL; 47 import java.net.URLClassLoader; 48 import java.security.cert.CertPath; 49 import java.security.cert.CertPathValidator; 50 import java.security.cert.CertificateExpiredException; 51 import java.security.cert.CertificateFactory; 52 import java.security.cert.CertificateNotYetValidException; 53 import java.security.cert.PKIXParameters; 54 import java.security.cert.TrustAnchor; 55 import java.util.Map.Entry; 56 import sun.security.tools.KeyStoreUtil; 57 import sun.security.tools.PathList; 58 import sun.security.x509.*; 59 import sun.security.util.*; 60 import java.util.Base64; 61 62 63 /** 64 * <p>The jarsigner utility. 65 * 66 * The exit codes for the main method are: 67 * 68 * 0: success 69 * 1: any error that the jar cannot be signed or verified, including: 70 * keystore loading error 71 * TSP communication error 72 * jarsigner command line error... 73 * otherwise: error codes from -strict 74 * 75 * @author Roland Schemers 80 81 // for i18n 82 private static final java.util.ResourceBundle rb = 83 java.util.ResourceBundle.getBundle 84 ("sun.security.tools.jarsigner.Resources"); 85 private static final Collator collator = Collator.getInstance(); 86 static { 87 // this is for case insensitive string comparisions 88 collator.setStrength(Collator.PRIMARY); 89 } 90 91 private static final String META_INF = "META-INF/"; 92 93 private static final Class<?>[] PARAM_STRING = { String.class }; 94 95 private static final String NONE = "NONE"; 96 private static final String P11KEYSTORE = "PKCS11"; 97 98 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds 99 100 // Attention: 101 // This is the entry that get launched by the security tool jarsigner. 102 public static void main(String args[]) throws Exception { 103 Main js = new Main(); 104 js.run(args); 105 } 106 107 static final String VERSION = "1.0"; 108 109 static final int IN_KEYSTORE = 0x01; // signer is in keystore 110 static final int IN_SCOPE = 0x02; 111 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and 112 // signer is not in alias list 113 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list 114 115 X509Certificate[] certChain; // signer's cert chain (when composing) 116 PrivateKey privateKey; // private key 117 KeyStore store; // the keystore specified by -keystore 118 // or the default keystore, never null 119 155 private String altSignerClass = null; 156 private String altSignerClasspath = null; 157 private ZipFile zipFile = null; 158 159 // Informational warnings 160 private boolean hasExpiringCert = false; 161 private boolean noTimestamp = false; 162 private Date expireDate = new Date(0L); // used in noTimestamp warning 163 164 // Severe warnings 165 private boolean hasExpiredCert = false; 166 private boolean notYetValidCert = false; 167 private boolean chainNotValidated = false; 168 private boolean notSignedByAlias = false; 169 private boolean aliasNotInStore = false; 170 private boolean hasUnsignedEntry = false; 171 private boolean badKeyUsage = false; 172 private boolean badExtendedKeyUsage = false; 173 private boolean badNetscapeCertType = false; 174 175 CertificateFactory certificateFactory; 176 CertPathValidator validator; 177 PKIXParameters pkixParameters; 178 179 public void run(String args[]) { 180 try { 181 parseArgs(args); 182 183 // Try to load and install the specified providers 184 if (providers != null) { 185 ClassLoader cl = ClassLoader.getSystemClassLoader(); 186 Enumeration<String> e = providers.elements(); 187 while (e.hasMoreElements()) { 188 String provName = e.nextElement(); 189 Class<?> provClass; 190 if (cl != null) { 191 provClass = cl.loadClass(provName); 192 } else { 193 provClass = Class.forName(provName); 194 } 560 System.out.println(rb.getString 561 (".providerName.name.provider.name")); 562 System.out.println(); 563 System.out.println(rb.getString 564 (".providerClass.class.name.of.cryptographic.service.provider.s")); 565 System.out.println(rb.getString 566 (".providerArg.arg.master.class.file.and.constructor.argument")); 567 System.out.println(); 568 System.out.println(rb.getString 569 (".strict.treat.warnings.as.errors")); 570 System.out.println(); 571 572 System.exit(0); 573 } 574 575 void verifyJar(String jarName) 576 throws Exception 577 { 578 boolean anySigned = false; // if there exists entry inside jar signed 579 JarFile jf = null; 580 581 try { 582 jf = new JarFile(jarName, true); 583 Vector<JarEntry> entriesVec = new Vector<>(); 584 byte[] buffer = new byte[8192]; 585 586 Enumeration<JarEntry> entries = jf.entries(); 587 while (entries.hasMoreElements()) { 588 JarEntry je = entries.nextElement(); 589 entriesVec.addElement(je); 590 InputStream is = null; 591 try { 592 is = jf.getInputStream(je); 593 int n; 594 while ((n = is.read(buffer, 0, buffer.length)) != -1) { 595 // we just read. this will throw a SecurityException 596 // if a signature/digest check fails. 597 } 598 } finally { 599 if (is != null) { 600 is.close(); 601 } 602 } 603 } 604 605 Manifest man = jf.getManifest(); 606 boolean hasSignature = false; 607 608 // The map to record display info, only used when -verbose provided 609 // key: signer info string 610 // value: the list of files with common key 611 Map<String,List<String>> output = new LinkedHashMap<>(); 612 613 if (man != null) { 614 if (verbose != null) System.out.println(); 615 Enumeration<JarEntry> e = entriesVec.elements(); 616 617 String tab = rb.getString("6SPACE"); 618 619 while (e.hasMoreElements()) { 620 JarEntry je = e.nextElement(); 739 } else { 740 System.out.println(files.get(0)); 741 } 742 } 743 System.out.printf(key.substring(pipe+1)); 744 } 745 } 746 System.out.println(); 747 System.out.println(rb.getString( 748 ".s.signature.was.verified.")); 749 System.out.println(rb.getString( 750 ".m.entry.is.listed.in.manifest")); 751 System.out.println(rb.getString( 752 ".k.at.least.one.certificate.was.found.in.keystore")); 753 System.out.println(rb.getString( 754 ".i.at.least.one.certificate.was.found.in.identity.scope")); 755 if (ckaliases.size() > 0) { 756 System.out.println(rb.getString( 757 ".X.not.signed.by.specified.alias.es.")); 758 } 759 System.out.println(); 760 } 761 if (man == null) 762 System.out.println(rb.getString("no.manifest.")); 763 764 if (!anySigned) { 765 if (hasSignature) { 766 System.out.println(rb.getString("jar.treated.unsigned")); 767 } else { 768 System.out.println(rb.getString("jar.is.unsigned")); 769 } 770 } else { 771 boolean warningAppeared = false; 772 boolean errorAppeared = false; 773 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 774 notYetValidCert || chainNotValidated || hasExpiredCert || 775 hasUnsignedEntry || 776 aliasNotInStore || notSignedByAlias) { 777 778 if (strict) { 779 System.out.println(rb.getString("jar.verified.with.signer.errors.")); 780 System.out.println(); 781 System.out.println(rb.getString("Error.")); 782 errorAppeared = true; 783 } else { 784 System.out.println(rb.getString("jar.verified.")); 785 System.out.println(); 852 System.out.println(rb.getString( 853 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); 854 } 855 } 856 } 857 return; 858 } catch (Exception e) { 859 System.out.println(rb.getString("jarsigner.") + e); 860 if (debug) { 861 e.printStackTrace(); 862 } 863 } finally { // close the resource 864 if (jf != null) { 865 jf.close(); 866 } 867 } 868 869 System.exit(1); 870 } 871 872 private static MessageFormat validityTimeForm = null; 873 private static MessageFormat notYetTimeForm = null; 874 private static MessageFormat expiredTimeForm = null; 875 private static MessageFormat expiringTimeForm = null; 876 877 /* 878 * Display some details about a certificate: 879 * 880 * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] 881 * [<validity-period> | <expiry-warning>] 882 * 883 * Note: no newline character at the end 884 */ 885 String printCert(String tab, Certificate c, boolean checkValidityPeriod, 886 Date timestamp, boolean checkUsage) { 887 888 StringBuilder certStr = new StringBuilder(); 889 String space = rb.getString("SPACE"); 890 X509Certificate x509Cert = null; 891 | 36 import java.text.MessageFormat; 37 import java.security.cert.Certificate; 38 import java.security.cert.X509Certificate; 39 import java.security.cert.CertificateException; 40 import java.security.*; 41 import java.lang.reflect.Constructor; 42 43 import com.sun.jarsigner.ContentSigner; 44 import com.sun.jarsigner.ContentSignerParameters; 45 import java.net.SocketTimeoutException; 46 import java.net.URL; 47 import java.net.URLClassLoader; 48 import java.security.cert.CertPath; 49 import java.security.cert.CertPathValidator; 50 import java.security.cert.CertificateExpiredException; 51 import java.security.cert.CertificateFactory; 52 import java.security.cert.CertificateNotYetValidException; 53 import java.security.cert.PKIXParameters; 54 import java.security.cert.TrustAnchor; 55 import java.util.Map.Entry; 56 import sun.security.pkcs.PKCS7; 57 import sun.security.pkcs.SignerInfo; 58 import sun.security.timestamp.TimestampToken; 59 import sun.security.tools.KeyStoreUtil; 60 import sun.security.tools.PathList; 61 import sun.security.x509.*; 62 import sun.security.util.*; 63 import java.util.Base64; 64 65 66 /** 67 * <p>The jarsigner utility. 68 * 69 * The exit codes for the main method are: 70 * 71 * 0: success 72 * 1: any error that the jar cannot be signed or verified, including: 73 * keystore loading error 74 * TSP communication error 75 * jarsigner command line error... 76 * otherwise: error codes from -strict 77 * 78 * @author Roland Schemers 83 84 // for i18n 85 private static final java.util.ResourceBundle rb = 86 java.util.ResourceBundle.getBundle 87 ("sun.security.tools.jarsigner.Resources"); 88 private static final Collator collator = Collator.getInstance(); 89 static { 90 // this is for case insensitive string comparisions 91 collator.setStrength(Collator.PRIMARY); 92 } 93 94 private static final String META_INF = "META-INF/"; 95 96 private static final Class<?>[] PARAM_STRING = { String.class }; 97 98 private static final String NONE = "NONE"; 99 private static final String P11KEYSTORE = "PKCS11"; 100 101 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds 102 103 private static final DisabledAlgorithmConstraints DISABLED_CHECK = 104 new DisabledAlgorithmConstraints( 105 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); 106 107 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections 108 .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); 109 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections 110 .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 111 112 // Attention: 113 // This is the entry that get launched by the security tool jarsigner. 114 public static void main(String args[]) throws Exception { 115 Main js = new Main(); 116 js.run(args); 117 } 118 119 static final String VERSION = "1.0"; 120 121 static final int IN_KEYSTORE = 0x01; // signer is in keystore 122 static final int IN_SCOPE = 0x02; 123 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and 124 // signer is not in alias list 125 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list 126 127 X509Certificate[] certChain; // signer's cert chain (when composing) 128 PrivateKey privateKey; // private key 129 KeyStore store; // the keystore specified by -keystore 130 // or the default keystore, never null 131 167 private String altSignerClass = null; 168 private String altSignerClasspath = null; 169 private ZipFile zipFile = null; 170 171 // Informational warnings 172 private boolean hasExpiringCert = false; 173 private boolean noTimestamp = false; 174 private Date expireDate = new Date(0L); // used in noTimestamp warning 175 176 // Severe warnings 177 private boolean hasExpiredCert = false; 178 private boolean notYetValidCert = false; 179 private boolean chainNotValidated = false; 180 private boolean notSignedByAlias = false; 181 private boolean aliasNotInStore = false; 182 private boolean hasUnsignedEntry = false; 183 private boolean badKeyUsage = false; 184 private boolean badExtendedKeyUsage = false; 185 private boolean badNetscapeCertType = false; 186 187 private boolean seeWeak = false; 188 189 CertificateFactory certificateFactory; 190 CertPathValidator validator; 191 PKIXParameters pkixParameters; 192 193 public void run(String args[]) { 194 try { 195 parseArgs(args); 196 197 // Try to load and install the specified providers 198 if (providers != null) { 199 ClassLoader cl = ClassLoader.getSystemClassLoader(); 200 Enumeration<String> e = providers.elements(); 201 while (e.hasMoreElements()) { 202 String provName = e.nextElement(); 203 Class<?> provClass; 204 if (cl != null) { 205 provClass = cl.loadClass(provName); 206 } else { 207 provClass = Class.forName(provName); 208 } 574 System.out.println(rb.getString 575 (".providerName.name.provider.name")); 576 System.out.println(); 577 System.out.println(rb.getString 578 (".providerClass.class.name.of.cryptographic.service.provider.s")); 579 System.out.println(rb.getString 580 (".providerArg.arg.master.class.file.and.constructor.argument")); 581 System.out.println(); 582 System.out.println(rb.getString 583 (".strict.treat.warnings.as.errors")); 584 System.out.println(); 585 586 System.exit(0); 587 } 588 589 void verifyJar(String jarName) 590 throws Exception 591 { 592 boolean anySigned = false; // if there exists entry inside jar signed 593 JarFile jf = null; 594 Map<String,String> digestMap = new HashMap<>(); 595 Map<String,PKCS7> sigMap = new HashMap<>(); 596 Map<String,String> sigNameMap = new HashMap<>(); 597 Map<String,String> unparsableSignatures = new HashMap<>(); 598 599 try { 600 jf = new JarFile(jarName, true); 601 Vector<JarEntry> entriesVec = new Vector<>(); 602 byte[] buffer = new byte[8192]; 603 604 Enumeration<JarEntry> entries = jf.entries(); 605 while (entries.hasMoreElements()) { 606 JarEntry je = entries.nextElement(); 607 entriesVec.addElement(je); 608 try (InputStream is = jf.getInputStream(je)) { 609 String name = je.getName(); 610 if (signatureRelated(name) 611 && SignatureFileVerifier.isBlockOrSF(name)) { 612 String alias = name.substring(name.lastIndexOf('/') + 1, 613 name.lastIndexOf('.')); 614 try { 615 if (name.endsWith(".SF")) { 616 Manifest sf = new Manifest(is); 617 boolean found = false; 618 for (Object obj : sf.getMainAttributes().keySet()) { 619 String key = obj.toString(); 620 if (key.endsWith("-Digest-Manifest")) { 621 digestMap.put(alias, 622 key.substring(0, key.length() - 16)); 623 found = true; 624 break; 625 } 626 } 627 if (!found) { 628 unparsableSignatures.putIfAbsent(alias, 629 String.format( 630 rb.getString("history.unparsable"), 631 name)); 632 } 633 } else { 634 sigNameMap.put(alias, name); 635 sigMap.put(alias, new PKCS7(is)); 636 } 637 } catch (IOException ioe) { 638 unparsableSignatures.putIfAbsent(alias, String.format( 639 rb.getString("history.unparsable"), name)); 640 } 641 } else { 642 while (is.read(buffer, 0, buffer.length) != -1) { 643 // we just read. this will throw a SecurityException 644 // if a signature/digest check fails. 645 } 646 } 647 } 648 } 649 650 Manifest man = jf.getManifest(); 651 boolean hasSignature = false; 652 653 // The map to record display info, only used when -verbose provided 654 // key: signer info string 655 // value: the list of files with common key 656 Map<String,List<String>> output = new LinkedHashMap<>(); 657 658 if (man != null) { 659 if (verbose != null) System.out.println(); 660 Enumeration<JarEntry> e = entriesVec.elements(); 661 662 String tab = rb.getString("6SPACE"); 663 664 while (e.hasMoreElements()) { 665 JarEntry je = e.nextElement(); 784 } else { 785 System.out.println(files.get(0)); 786 } 787 } 788 System.out.printf(key.substring(pipe+1)); 789 } 790 } 791 System.out.println(); 792 System.out.println(rb.getString( 793 ".s.signature.was.verified.")); 794 System.out.println(rb.getString( 795 ".m.entry.is.listed.in.manifest")); 796 System.out.println(rb.getString( 797 ".k.at.least.one.certificate.was.found.in.keystore")); 798 System.out.println(rb.getString( 799 ".i.at.least.one.certificate.was.found.in.identity.scope")); 800 if (ckaliases.size() > 0) { 801 System.out.println(rb.getString( 802 ".X.not.signed.by.specified.alias.es.")); 803 } 804 } 805 if (man == null) { 806 System.out.println(); 807 System.out.println(rb.getString("no.manifest.")); 808 } 809 810 // Even if the verbose option is not specified, all out strings 811 // must be generated so seeWeak can be updated. 812 if (!digestMap.isEmpty() 813 || !sigMap.isEmpty() 814 || !unparsableSignatures.isEmpty()) { 815 if (verbose != null) { 816 System.out.println(); 817 } 818 for (String s : sigMap.keySet()) { 819 if (!digestMap.containsKey(s)) { 820 unparsableSignatures.putIfAbsent(s, String.format( 821 rb.getString("history.nosf"), s)); 822 } 823 } 824 for (String s : digestMap.keySet()) { 825 PKCS7 p7 = sigMap.get(s); 826 if (p7 != null) { 827 String history; 828 try { 829 SignerInfo si = p7.getSignerInfos()[0]; 830 X509Certificate signer = si.getCertificate(p7); 831 String digestAlg = digestMap.get(s); 832 String sigAlg = AlgorithmId.makeSigAlg( 833 si.getDigestAlgorithmId().getName(), 834 si.getDigestEncryptionAlgorithmId().getName()); 835 PublicKey key = signer.getPublicKey(); 836 PKCS7 tsToken = si.getTsToken(); 837 if (tsToken != null) { 838 SignerInfo tsSi = tsToken.getSignerInfos()[0]; 839 X509Certificate tsSigner = tsSi.getCertificate(tsToken); 840 byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); 841 TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); 842 PublicKey tsKey = tsSigner.getPublicKey(); 843 String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName(); 844 String tsSigAlg = AlgorithmId.makeSigAlg( 845 tsSi.getDigestAlgorithmId().getName(), 846 tsSi.getDigestEncryptionAlgorithmId().getName()); 847 Calendar c = Calendar.getInstance( 848 TimeZone.getTimeZone("UTC"), 849 Locale.getDefault(Locale.Category.FORMAT)); 850 c.setTime(tsTokenInfo.getDate()); 851 history = String.format( 852 rb.getString("history.with.ts"), 853 signer.getSubjectX500Principal(), 854 withWeak(digestAlg, DIGEST_PRIMITIVE_SET), 855 withWeak(sigAlg, SIG_PRIMITIVE_SET), 856 withWeak(key), 857 c, 858 tsSigner.getSubjectX500Principal(), 859 withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET), 860 withWeak(tsSigAlg, SIG_PRIMITIVE_SET), 861 withWeak(tsKey)); 862 } else { 863 history = String.format( 864 rb.getString("history.without.ts"), 865 signer.getSubjectX500Principal(), 866 withWeak(digestAlg, DIGEST_PRIMITIVE_SET), 867 withWeak(sigAlg, SIG_PRIMITIVE_SET), 868 withWeak(key)); 869 } 870 } catch (Exception e) { 871 // The only usage of sigNameMap, remember the name 872 // of the block file if it's invalid. 873 history = String.format( 874 rb.getString("history.unparsable"), 875 sigNameMap.get(s)); 876 } 877 if (verbose != null) { 878 System.out.println(history); 879 } 880 } else { 881 unparsableSignatures.putIfAbsent(s, String.format( 882 rb.getString("history.nobk"), s)); 883 } 884 } 885 if (verbose != null) { 886 for (String s : unparsableSignatures.keySet()) { 887 System.out.println(unparsableSignatures.get(s)); 888 } 889 } 890 } 891 System.out.println(); 892 893 if (!anySigned) { 894 if (seeWeak) { 895 if (verbose != null) { 896 System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose")); 897 System.out.println("\n " + 898 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS + 899 "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS)); 900 } else { 901 System.out.println(rb.getString("jar.treated.unsigned.see.weak")); 902 } 903 } else if (hasSignature) { 904 System.out.println(rb.getString("jar.treated.unsigned")); 905 } else { 906 System.out.println(rb.getString("jar.is.unsigned")); 907 } 908 } else { 909 boolean warningAppeared = false; 910 boolean errorAppeared = false; 911 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || 912 notYetValidCert || chainNotValidated || hasExpiredCert || 913 hasUnsignedEntry || 914 aliasNotInStore || notSignedByAlias) { 915 916 if (strict) { 917 System.out.println(rb.getString("jar.verified.with.signer.errors.")); 918 System.out.println(); 919 System.out.println(rb.getString("Error.")); 920 errorAppeared = true; 921 } else { 922 System.out.println(rb.getString("jar.verified.")); 923 System.out.println(); 990 System.out.println(rb.getString( 991 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); 992 } 993 } 994 } 995 return; 996 } catch (Exception e) { 997 System.out.println(rb.getString("jarsigner.") + e); 998 if (debug) { 999 e.printStackTrace(); 1000 } 1001 } finally { // close the resource 1002 if (jf != null) { 1003 jf.close(); 1004 } 1005 } 1006 1007 System.exit(1); 1008 } 1009 1010 private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) { 1011 if (DISABLED_CHECK.permits(primitiveSet, alg, null)) { 1012 return alg; 1013 } else { 1014 seeWeak = true; 1015 return String.format(rb.getString("with.weak"), alg); 1016 } 1017 } 1018 1019 private String withWeak(PublicKey key) { 1020 if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 1021 return String.format( 1022 rb.getString("key.bit"), KeyUtil.getKeySize(key)); 1023 } else { 1024 seeWeak = true; 1025 return String.format( 1026 rb.getString("key.bit.weak"), KeyUtil.getKeySize(key)); 1027 } 1028 } 1029 1030 private static MessageFormat validityTimeForm = null; 1031 private static MessageFormat notYetTimeForm = null; 1032 private static MessageFormat expiredTimeForm = null; 1033 private static MessageFormat expiringTimeForm = null; 1034 1035 /* 1036 * Display some details about a certificate: 1037 * 1038 * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] 1039 * [<validity-period> | <expiry-warning>] 1040 * 1041 * Note: no newline character at the end 1042 */ 1043 String printCert(String tab, Certificate c, boolean checkValidityPeriod, 1044 Date timestamp, boolean checkUsage) { 1045 1046 StringBuilder certStr = new StringBuilder(); 1047 String space = rb.getString("SPACE"); 1048 X509Certificate x509Cert = null; 1049 |