90 |
91 |
91 private static final String NONE = "NONE"; |
92 private static final String NONE = "NONE"; |
92 private static final String P11KEYSTORE = "PKCS11"; |
93 private static final String P11KEYSTORE = "PKCS11"; |
93 |
94 |
94 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds |
95 private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds |
|
96 private static final long ONE_YEAR = 366*24*60*60*1000L; |
95 |
97 |
96 private static final DisabledAlgorithmConstraints DISABLED_CHECK = |
98 private static final DisabledAlgorithmConstraints DISABLED_CHECK = |
97 new DisabledAlgorithmConstraints( |
99 new DisabledAlgorithmConstraints( |
98 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); |
100 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); |
99 |
101 |
100 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections |
102 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections |
101 .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); |
103 .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); |
102 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections |
104 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections |
103 .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); |
105 .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); |
|
106 |
|
107 static final String VERSION = "1.0"; |
|
108 |
|
109 static final int IN_KEYSTORE = 0x01; // signer is in keystore |
|
110 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and |
|
111 // signer is not in alias list |
|
112 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list |
104 |
113 |
105 // Attention: |
114 // Attention: |
106 // This is the entry that get launched by the security tool jarsigner. |
115 // This is the entry that get launched by the security tool jarsigner. |
107 public static void main(String args[]) throws Exception { |
116 public static void main(String args[]) throws Exception { |
108 Main js = new Main(); |
117 Main js = new Main(); |
109 js.run(args); |
118 js.run(args); |
110 } |
119 } |
111 |
|
112 static final String VERSION = "1.0"; |
|
113 |
|
114 static final int IN_KEYSTORE = 0x01; // signer is in keystore |
|
115 static final int NOT_ALIAS = 0x04; // alias list is NOT empty and |
|
116 // signer is not in alias list |
|
117 static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list |
|
118 |
120 |
119 X509Certificate[] certChain; // signer's cert chain (when composing) |
121 X509Certificate[] certChain; // signer's cert chain (when composing) |
120 PrivateKey privateKey; // private key |
122 PrivateKey privateKey; // private key |
121 KeyStore store; // the keystore specified by -keystore |
123 KeyStore store; // the keystore specified by -keystore |
122 // or the default keystore, never null |
124 // or the default keystore, never null |
984 System.out.println(rb.getString("jar.treated.unsigned")); |
994 System.out.println(rb.getString("jar.treated.unsigned")); |
985 } else { |
995 } else { |
986 System.out.println(rb.getString("jar.is.unsigned")); |
996 System.out.println(rb.getString("jar.is.unsigned")); |
987 } |
997 } |
988 } else { |
998 } else { |
989 boolean warningAppeared = false; |
999 displayMessagesAndResult(false); |
990 boolean errorAppeared = false; |
|
991 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || |
|
992 notYetValidCert || chainNotValidated || hasExpiredCert || |
|
993 hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || |
|
994 aliasNotInStore || notSignedByAlias || tsaChainNotValidated) { |
|
995 |
|
996 if (strict) { |
|
997 System.out.println(rb.getString("jar.verified.with.signer.errors.")); |
|
998 System.out.println(); |
|
999 System.out.println(rb.getString("Error.")); |
|
1000 errorAppeared = true; |
|
1001 } else { |
|
1002 System.out.println(rb.getString("jar.verified.")); |
|
1003 System.out.println(); |
|
1004 System.out.println(rb.getString("Warning.")); |
|
1005 warningAppeared = true; |
|
1006 } |
|
1007 |
|
1008 if (weakAlg != 0) { |
|
1009 // In fact, jarsigner verification did not catch this |
|
1010 // since it has not read the JarFile content itself. |
|
1011 // Everything is done with JarFile API. The signing |
|
1012 // history (digestMap etc) will show these info and |
|
1013 // print out proper warnings. |
|
1014 } |
|
1015 |
|
1016 if (badKeyUsage) { |
|
1017 System.out.println( |
|
1018 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); |
|
1019 } |
|
1020 |
|
1021 if (badExtendedKeyUsage) { |
|
1022 System.out.println( |
|
1023 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); |
|
1024 } |
|
1025 |
|
1026 if (badNetscapeCertType) { |
|
1027 System.out.println( |
|
1028 rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); |
|
1029 } |
|
1030 |
|
1031 if (hasUnsignedEntry) { |
|
1032 System.out.println(rb.getString( |
|
1033 "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); |
|
1034 } |
|
1035 if (hasExpiredCert) { |
|
1036 System.out.println(rb.getString( |
|
1037 "This.jar.contains.entries.whose.signer.certificate.has.expired.")); |
|
1038 } |
|
1039 if (notYetValidCert) { |
|
1040 System.out.println(rb.getString( |
|
1041 "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); |
|
1042 } |
|
1043 |
|
1044 if (chainNotValidated) { |
|
1045 System.out.println(String.format( |
|
1046 rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"), |
|
1047 chainNotValidatedReason.getLocalizedMessage())); |
|
1048 } |
|
1049 |
|
1050 if (tsaChainNotValidated) { |
|
1051 System.out.println(String.format( |
|
1052 rb.getString("This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"), |
|
1053 tsaChainNotValidatedReason.getLocalizedMessage())); |
|
1054 } |
|
1055 |
|
1056 if (notSignedByAlias) { |
|
1057 System.out.println( |
|
1058 rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); |
|
1059 } |
|
1060 |
|
1061 if (aliasNotInStore) { |
|
1062 System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); |
|
1063 } |
|
1064 |
|
1065 if (signerSelfSigned) { |
|
1066 System.out.println(rb.getString( |
|
1067 "This.jar.contains.entries.whose.signer.certificate.is.self.signed.")); |
|
1068 } |
|
1069 } else { |
|
1070 System.out.println(rb.getString("jar.verified.")); |
|
1071 } |
|
1072 if (hasExpiringCert || noTimestamp) { |
|
1073 if (!warningAppeared) { |
|
1074 System.out.println(); |
|
1075 System.out.println(rb.getString("Warning.")); |
|
1076 warningAppeared = true; |
|
1077 } |
|
1078 if (hasExpiringCert) { |
|
1079 System.out.println(rb.getString( |
|
1080 "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); |
|
1081 } |
|
1082 if (noTimestamp) { |
|
1083 if (hasTimestampBlock) { |
|
1084 // JarSigner API has not seen the timestamp, |
|
1085 // might have ignored it due to weak alg, etc. |
|
1086 System.out.println( |
|
1087 String.format(rb.getString("bad.timestamp.verifying"), expireDate)); |
|
1088 } else { |
|
1089 System.out.println( |
|
1090 String.format(rb.getString("no.timestamp.verifying"), expireDate)); |
|
1091 } |
|
1092 } |
|
1093 } |
|
1094 if (warningAppeared || errorAppeared) { |
|
1095 if (! (verbose != null && showcerts)) { |
|
1096 System.out.println(); |
|
1097 System.out.println(rb.getString( |
|
1098 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); |
|
1099 } |
|
1100 } |
|
1101 } |
1000 } |
1102 return; |
1001 return; |
1103 } catch (Exception e) { |
1002 } catch (Exception e) { |
1104 System.out.println(rb.getString("jarsigner.") + e); |
1003 System.out.println(rb.getString("jarsigner.") + e); |
1105 if (debug) { |
1004 if (debug) { |
1110 jf.close(); |
1009 jf.close(); |
1111 } |
1010 } |
1112 } |
1011 } |
1113 |
1012 |
1114 System.exit(1); |
1013 System.exit(1); |
|
1014 } |
|
1015 |
|
1016 private void displayMessagesAndResult(boolean isSigning) { |
|
1017 String result; |
|
1018 List<String> errors = new ArrayList<>(); |
|
1019 List<String> warnings = new ArrayList<>(); |
|
1020 List<String> info = new ArrayList<>(); |
|
1021 |
|
1022 boolean signerNotExpired = expireDate == null |
|
1023 || expireDate.after(new Date()); |
|
1024 |
|
1025 if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || |
|
1026 notYetValidCert || chainNotValidated || hasExpiredCert || |
|
1027 hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || |
|
1028 aliasNotInStore || notSignedByAlias || |
|
1029 tsaChainNotValidated || |
|
1030 (hasExpiredTsaCert && !signerNotExpired)) { |
|
1031 |
|
1032 if (strict) { |
|
1033 result = rb.getString(isSigning |
|
1034 ? "jar.signed.with.signer.errors." |
|
1035 : "jar.verified.with.signer.errors."); |
|
1036 } else { |
|
1037 result = rb.getString(isSigning |
|
1038 ? "jar.signed." |
|
1039 : "jar.verified."); |
|
1040 } |
|
1041 |
|
1042 if (badKeyUsage) { |
|
1043 errors.add(rb.getString(isSigning |
|
1044 ? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing." |
|
1045 : "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); |
|
1046 } |
|
1047 |
|
1048 if (badExtendedKeyUsage) { |
|
1049 errors.add(rb.getString(isSigning |
|
1050 ? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing." |
|
1051 : "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); |
|
1052 } |
|
1053 |
|
1054 if (badNetscapeCertType) { |
|
1055 errors.add(rb.getString(isSigning |
|
1056 ? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing." |
|
1057 : "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); |
|
1058 } |
|
1059 |
|
1060 // only in verifying |
|
1061 if (hasUnsignedEntry) { |
|
1062 errors.add(rb.getString( |
|
1063 "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); |
|
1064 } |
|
1065 if (hasExpiredCert) { |
|
1066 errors.add(rb.getString(isSigning |
|
1067 ? "The.signer.certificate.has.expired." |
|
1068 : "This.jar.contains.entries.whose.signer.certificate.has.expired.")); |
|
1069 } |
|
1070 if (notYetValidCert) { |
|
1071 errors.add(rb.getString(isSigning |
|
1072 ? "The.signer.certificate.is.not.yet.valid." |
|
1073 : "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); |
|
1074 } |
|
1075 |
|
1076 if (chainNotValidated) { |
|
1077 errors.add(String.format(rb.getString(isSigning |
|
1078 ? "The.signer.s.certificate.chain.is.invalid.reason.1" |
|
1079 : "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"), |
|
1080 chainNotValidatedReason.getLocalizedMessage())); |
|
1081 } |
|
1082 |
|
1083 if (hasExpiredTsaCert) { |
|
1084 errors.add(rb.getString("The.timestamp.has.expired.")); |
|
1085 } |
|
1086 if (tsaChainNotValidated) { |
|
1087 errors.add(String.format(rb.getString(isSigning |
|
1088 ? "The.tsa.certificate.chain.is.invalid.reason.1" |
|
1089 : "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"), |
|
1090 tsaChainNotValidatedReason.getLocalizedMessage())); |
|
1091 } |
|
1092 |
|
1093 // only in verifying |
|
1094 if (notSignedByAlias) { |
|
1095 errors.add( |
|
1096 rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); |
|
1097 } |
|
1098 |
|
1099 // only in verifying |
|
1100 if (aliasNotInStore) { |
|
1101 errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); |
|
1102 } |
|
1103 |
|
1104 if (signerSelfSigned) { |
|
1105 errors.add(rb.getString(isSigning |
|
1106 ? "The.signer.s.certificate.is.self.signed." |
|
1107 : "This.jar.contains.entries.whose.signer.certificate.is.self.signed.")); |
|
1108 } |
|
1109 |
|
1110 // weakAlg only detected in signing. The jar file is |
|
1111 // now simply treated unsigned in verifying. |
|
1112 if ((weakAlg & 1) == 1) { |
|
1113 errors.add(String.format( |
|
1114 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1115 digestalg, "-digestalg")); |
|
1116 } |
|
1117 |
|
1118 if ((weakAlg & 2) == 2) { |
|
1119 errors.add(String.format( |
|
1120 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1121 sigalg, "-sigalg")); |
|
1122 } |
|
1123 if ((weakAlg & 4) == 4) { |
|
1124 errors.add(String.format( |
|
1125 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1126 tSADigestAlg, "-tsadigestalg")); |
|
1127 } |
|
1128 if ((weakAlg & 8) == 8) { |
|
1129 errors.add(String.format( |
|
1130 rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."), |
|
1131 privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey))); |
|
1132 } |
|
1133 } else { |
|
1134 result = rb.getString(isSigning ? "jar.signed." : "jar.verified."); |
|
1135 } |
|
1136 |
|
1137 if (hasExpiredTsaCert) { |
|
1138 // No need to warn about expiring if already expired |
|
1139 hasExpiringTsaCert = false; |
|
1140 } |
|
1141 |
|
1142 if (hasExpiringCert || |
|
1143 (hasExpiringTsaCert && expireDate != null) || |
|
1144 (noTimestamp && expireDate != null) || |
|
1145 (hasExpiredTsaCert && signerNotExpired)) { |
|
1146 |
|
1147 if (hasExpiredTsaCert && signerNotExpired) { |
|
1148 if (expireDate != null) { |
|
1149 warnings.add(String.format( |
|
1150 rb.getString("The.timestamp.expired.1.but.usable.2"), |
|
1151 tsaExpireDate, |
|
1152 expireDate)); |
|
1153 } |
|
1154 // Reset the flag so exit code is 0 |
|
1155 hasExpiredTsaCert = false; |
|
1156 } |
|
1157 if (hasExpiringCert) { |
|
1158 warnings.add(rb.getString(isSigning |
|
1159 ? "The.signer.certificate.will.expire.within.six.months." |
|
1160 : "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); |
|
1161 } |
|
1162 if (hasExpiringTsaCert && expireDate != null) { |
|
1163 if (expireDate.after(tsaExpireDate)) { |
|
1164 warnings.add(String.format(rb.getString( |
|
1165 "The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate)); |
|
1166 } else { |
|
1167 warnings.add(String.format(rb.getString( |
|
1168 "The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate)); |
|
1169 } |
|
1170 } |
|
1171 if (noTimestamp && expireDate != null) { |
|
1172 if (hasTimestampBlock) { |
|
1173 warnings.add(String.format(rb.getString(isSigning |
|
1174 ? "invalid.timestamp.signing" |
|
1175 : "bad.timestamp.verifying"), expireDate)); |
|
1176 } else { |
|
1177 warnings.add(String.format(rb.getString(isSigning |
|
1178 ? "no.timestamp.signing" |
|
1179 : "no.timestamp.verifying"), expireDate)); |
|
1180 } |
|
1181 } |
|
1182 } |
|
1183 |
|
1184 System.out.println(result); |
|
1185 if (strict) { |
|
1186 if (!errors.isEmpty()) { |
|
1187 System.out.println(); |
|
1188 System.out.println(rb.getString("Error.")); |
|
1189 errors.forEach(System.out::println); |
|
1190 } |
|
1191 if (!warnings.isEmpty()) { |
|
1192 System.out.println(); |
|
1193 System.out.println(rb.getString("Warning.")); |
|
1194 warnings.forEach(System.out::println); |
|
1195 } |
|
1196 } else { |
|
1197 if (!errors.isEmpty() || !warnings.isEmpty()) { |
|
1198 System.out.println(); |
|
1199 System.out.println(rb.getString("Warning.")); |
|
1200 errors.forEach(System.out::println); |
|
1201 warnings.forEach(System.out::println); |
|
1202 } |
|
1203 } |
|
1204 if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) { |
|
1205 if (! (verbose != null && showcerts)) { |
|
1206 System.out.println(); |
|
1207 System.out.println(rb.getString( |
|
1208 "Re.run.with.the.verbose.and.certs.options.for.more.details.")); |
|
1209 } |
|
1210 } |
|
1211 |
|
1212 if (isSigning || verbose != null) { |
|
1213 // Always print out expireDate, unless expired or expiring. |
|
1214 if (!hasExpiringCert && !hasExpiredCert |
|
1215 && expireDate != null && signerNotExpired) { |
|
1216 info.add(String.format(rb.getString( |
|
1217 "The.signer.certificate.will.expire.on.1."), expireDate)); |
|
1218 } |
|
1219 if (!noTimestamp) { |
|
1220 if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) { |
|
1221 if (signerNotExpired) { |
|
1222 info.add(String.format(rb.getString( |
|
1223 "The.timestamp.will.expire.on.1."), tsaExpireDate)); |
|
1224 } else { |
|
1225 info.add(String.format(rb.getString( |
|
1226 "signer.cert.expired.1.but.timestamp.good.2."), |
|
1227 expireDate, |
|
1228 tsaExpireDate)); |
|
1229 } |
|
1230 } |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 if (!info.isEmpty()) { |
|
1235 System.out.println(); |
|
1236 info.forEach(System.out::println); |
|
1237 } |
1115 } |
1238 } |
1116 |
1239 |
1117 private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) { |
1240 private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) { |
1118 if (DISABLED_CHECK.permits(primitiveSet, alg, null)) { |
1241 if (DISABLED_CHECK.permits(primitiveSet, alg, null)) { |
1119 return alg; |
1242 return alg; |
1180 } |
1304 } |
1181 |
1305 |
1182 if (x509Cert != null) { |
1306 if (x509Cert != null) { |
1183 |
1307 |
1184 certStr.append("\n").append(tab).append("["); |
1308 certStr.append("\n").append(tab).append("["); |
1185 Date notAfter = x509Cert.getNotAfter(); |
1309 |
1186 try { |
1310 if (trustedCerts.contains(x509Cert)) { |
1187 boolean printValidity = true; |
1311 certStr.append(rb.getString("trusted.certificate")); |
1188 if (timestamp == null) { |
1312 } else { |
1189 if (expireDate.getTime() == 0 || expireDate.after(notAfter)) { |
1313 Date notAfter = x509Cert.getNotAfter(); |
1190 expireDate = notAfter; |
1314 try { |
1191 } |
1315 boolean printValidity = true; |
1192 x509Cert.checkValidity(); |
1316 if (isTsCert) { |
1193 // test if cert will expire within six months |
1317 if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) { |
1194 if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { |
1318 tsaExpireDate = notAfter; |
1195 if (!isTsCert) hasExpiringCert = true; |
|
1196 if (expiringTimeForm == null) { |
|
1197 expiringTimeForm = new MessageFormat( |
|
1198 rb.getString("certificate.will.expire.on")); |
|
1199 } |
1319 } |
1200 Object[] source = { notAfter }; |
1320 } else { |
1201 certStr.append(expiringTimeForm.format(source)); |
1321 if (expireDate == null || expireDate.after(notAfter)) { |
1202 printValidity = false; |
1322 expireDate = notAfter; |
1203 } |
1323 } |
1204 } else { |
1324 } |
1205 x509Cert.checkValidity(timestamp); |
1325 if (timestamp == null) { |
1206 } |
1326 x509Cert.checkValidity(); |
1207 if (printValidity) { |
1327 // test if cert will expire within six months (or one year for tsa) |
1208 if (validityTimeForm == null) { |
1328 long age = isTsCert ? ONE_YEAR : SIX_MONTHS; |
1209 validityTimeForm = new MessageFormat( |
1329 if (notAfter.getTime() < System.currentTimeMillis() + age) { |
1210 rb.getString("certificate.is.valid.from")); |
1330 if (isTsCert) { |
1211 } |
1331 hasExpiringTsaCert = true; |
1212 Object[] source = { x509Cert.getNotBefore(), notAfter }; |
1332 } else { |
1213 certStr.append(validityTimeForm.format(source)); |
1333 hasExpiringCert = true; |
1214 } |
1334 } |
1215 } catch (CertificateExpiredException cee) { |
1335 if (expiringTimeForm == null) { |
1216 if (!isTsCert) hasExpiredCert = true; |
1336 expiringTimeForm = new MessageFormat( |
1217 |
1337 rb.getString("certificate.will.expire.on")); |
1218 if (expiredTimeForm == null) { |
1338 } |
1219 expiredTimeForm = new MessageFormat( |
1339 Object[] source = {notAfter}; |
1220 rb.getString("certificate.expired.on")); |
1340 certStr.append(expiringTimeForm.format(source)); |
1221 } |
1341 printValidity = false; |
1222 Object[] source = { notAfter }; |
1342 } |
1223 certStr.append(expiredTimeForm.format(source)); |
1343 } else { |
1224 |
1344 x509Cert.checkValidity(timestamp); |
1225 } catch (CertificateNotYetValidException cnyve) { |
1345 } |
1226 if (!isTsCert) notYetValidCert = true; |
1346 if (printValidity) { |
1227 |
1347 if (validityTimeForm == null) { |
1228 if (notYetTimeForm == null) { |
1348 validityTimeForm = new MessageFormat( |
1229 notYetTimeForm = new MessageFormat( |
1349 rb.getString("certificate.is.valid.from")); |
1230 rb.getString("certificate.is.not.valid.until")); |
1350 } |
1231 } |
1351 Object[] source = {x509Cert.getNotBefore(), notAfter}; |
1232 Object[] source = { x509Cert.getNotBefore() }; |
1352 certStr.append(validityTimeForm.format(source)); |
1233 certStr.append(notYetTimeForm.format(source)); |
1353 } |
|
1354 } catch (CertificateExpiredException cee) { |
|
1355 if (isTsCert) { |
|
1356 hasExpiredTsaCert = true; |
|
1357 } else { |
|
1358 hasExpiredCert = true; |
|
1359 } |
|
1360 |
|
1361 if (expiredTimeForm == null) { |
|
1362 expiredTimeForm = new MessageFormat( |
|
1363 rb.getString("certificate.expired.on")); |
|
1364 } |
|
1365 Object[] source = {notAfter}; |
|
1366 certStr.append(expiredTimeForm.format(source)); |
|
1367 |
|
1368 } catch (CertificateNotYetValidException cnyve) { |
|
1369 if (!isTsCert) notYetValidCert = true; |
|
1370 |
|
1371 if (notYetTimeForm == null) { |
|
1372 notYetTimeForm = new MessageFormat( |
|
1373 rb.getString("certificate.is.not.valid.until")); |
|
1374 } |
|
1375 Object[] source = {x509Cert.getNotBefore()}; |
|
1376 certStr.append(notYetTimeForm.format(source)); |
|
1377 } |
1234 } |
1378 } |
1235 certStr.append("]"); |
1379 certStr.append("]"); |
1236 |
1380 |
1237 if (checkUsage) { |
1381 if (checkUsage) { |
1238 boolean[] bad = new boolean[3]; |
1382 boolean[] bad = new boolean[3]; |
1517 if (failedCause != null) { |
1659 if (failedCause != null) { |
1518 signedJarFile.delete(); |
1660 signedJarFile.delete(); |
1519 error(failedMessage, failedCause); |
1661 error(failedMessage, failedCause); |
1520 } |
1662 } |
1521 |
1663 |
|
1664 if (verbose != null) { |
|
1665 System.out.println(); |
|
1666 } |
|
1667 |
1522 // The JarSigner API always accepts the timestamp received. |
1668 // The JarSigner API always accepts the timestamp received. |
1523 // We need to extract the certs from the signed jar to |
1669 // We need to extract the certs from the signed jar to |
1524 // validate it. |
1670 // validate it. |
1525 if (!noTimestamp) { |
1671 try (JarFile check = new JarFile(signedJarFile)) { |
1526 try (JarFile check = new JarFile(signedJarFile)) { |
1672 PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry( |
1527 PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry( |
1673 "META-INF/" + sigfile + "." + privateKey.getAlgorithm()))); |
1528 "META-INF/" + sigfile + "." + privateKey.getAlgorithm()))); |
1674 Timestamp ts = null; |
|
1675 try { |
1529 SignerInfo si = p7.getSignerInfos()[0]; |
1676 SignerInfo si = p7.getSignerInfos()[0]; |
1530 PKCS7 tsToken = si.getTsToken(); |
1677 if (si.getTsToken() != null) { |
1531 SignerInfo tsSi = tsToken.getSignerInfos()[0]; |
1678 hasTimestampBlock = true; |
1532 try { |
1679 } |
1533 validateCertChain(Validator.VAR_TSA_SERVER, |
1680 ts = si.getTimestamp(); |
1534 tsSi.getCertificateChain(tsToken), null); |
|
1535 } catch (Exception e) { |
|
1536 tsaChainNotValidated = true; |
|
1537 tsaChainNotValidatedReason = e; |
|
1538 } |
|
1539 } catch (Exception e) { |
1681 } catch (Exception e) { |
1540 if (debug) { |
1682 tsaChainNotValidated = true; |
1541 e.printStackTrace(); |
1683 tsaChainNotValidatedReason = e; |
1542 } |
1684 } |
1543 } |
1685 // Spaces before the ">>> Signer" and other lines are different |
1544 } |
1686 String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts); |
1545 |
1687 if (verbose != null) { |
1546 // no IOException thrown in the follow try clause, so disable |
1688 System.out.println(result); |
1547 // the try clause. |
1689 } |
1548 // try { |
1690 } catch (Exception e) { |
1549 if (signedjar == null) { |
1691 if (debug) { |
1550 // attempt an atomic rename. If that fails, |
1692 e.printStackTrace(); |
1551 // rename the original jar file, then the signed |
1693 } |
1552 // one, then delete the original. |
1694 } |
1553 if (!signedJarFile.renameTo(jarFile)) { |
1695 |
1554 File origJar = new File(jarName+".orig"); |
1696 if (signedjar == null) { |
1555 |
1697 // attempt an atomic rename. If that fails, |
1556 if (jarFile.renameTo(origJar)) { |
1698 // rename the original jar file, then the signed |
1557 if (signedJarFile.renameTo(jarFile)) { |
1699 // one, then delete the original. |
1558 origJar.delete(); |
1700 if (!signedJarFile.renameTo(jarFile)) { |
1559 } else { |
1701 File origJar = new File(jarName+".orig"); |
1560 MessageFormat form = new MessageFormat(rb.getString |
1702 |
1561 ("attempt.to.rename.signedJarFile.to.jarFile.failed")); |
1703 if (jarFile.renameTo(origJar)) { |
1562 Object[] source = {signedJarFile, jarFile}; |
1704 if (signedJarFile.renameTo(jarFile)) { |
1563 error(form.format(source)); |
1705 origJar.delete(); |
1564 } |
|
1565 } else { |
1706 } else { |
1566 MessageFormat form = new MessageFormat(rb.getString |
1707 MessageFormat form = new MessageFormat(rb.getString |
1567 ("attempt.to.rename.jarFile.to.origJar.failed")); |
1708 ("attempt.to.rename.signedJarFile.to.jarFile.failed")); |
1568 Object[] source = {jarFile, origJar}; |
1709 Object[] source = {signedJarFile, jarFile}; |
1569 error(form.format(source)); |
1710 error(form.format(source)); |
1570 } |
1711 } |
1571 } |
|
1572 } |
|
1573 |
|
1574 boolean warningAppeared = false; |
|
1575 if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage |
|
1576 || badNetscapeCertType || notYetValidCert |
|
1577 || chainNotValidated || tsaChainNotValidated |
|
1578 || hasExpiredCert || signerSelfSigned) { |
|
1579 if (strict) { |
|
1580 System.out.println(rb.getString("jar.signed.with.signer.errors.")); |
|
1581 System.out.println(); |
|
1582 System.out.println(rb.getString("Error.")); |
|
1583 } else { |
1712 } else { |
1584 System.out.println(rb.getString("jar.signed.")); |
1713 MessageFormat form = new MessageFormat(rb.getString |
1585 System.out.println(); |
1714 ("attempt.to.rename.jarFile.to.origJar.failed")); |
1586 System.out.println(rb.getString("Warning.")); |
1715 Object[] source = {jarFile, origJar}; |
1587 warningAppeared = true; |
1716 error(form.format(source)); |
1588 } |
1717 } |
1589 |
1718 } |
1590 if (badKeyUsage) { |
1719 } |
1591 System.out.println( |
1720 |
1592 rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); |
1721 displayMessagesAndResult(true); |
1593 } |
|
1594 |
|
1595 if (badExtendedKeyUsage) { |
|
1596 System.out.println( |
|
1597 rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); |
|
1598 } |
|
1599 |
|
1600 if (badNetscapeCertType) { |
|
1601 System.out.println( |
|
1602 rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); |
|
1603 } |
|
1604 |
|
1605 if (hasExpiredCert) { |
|
1606 System.out.println( |
|
1607 rb.getString("The.signer.certificate.has.expired.")); |
|
1608 } else if (notYetValidCert) { |
|
1609 System.out.println( |
|
1610 rb.getString("The.signer.certificate.is.not.yet.valid.")); |
|
1611 } |
|
1612 |
|
1613 if (chainNotValidated) { |
|
1614 System.out.println(String.format( |
|
1615 rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1"), |
|
1616 chainNotValidatedReason.getLocalizedMessage())); |
|
1617 } |
|
1618 |
|
1619 if (tsaChainNotValidated) { |
|
1620 System.out.println(String.format( |
|
1621 rb.getString("The.tsa.certificate.chain.is.invalid.reason.1"), |
|
1622 tsaChainNotValidatedReason.getLocalizedMessage())); |
|
1623 } |
|
1624 |
|
1625 if (signerSelfSigned) { |
|
1626 System.out.println( |
|
1627 rb.getString("The.signer.s.certificate.is.self.signed.")); |
|
1628 } |
|
1629 |
|
1630 if ((weakAlg & 1) == 1) { |
|
1631 System.out.println(String.format( |
|
1632 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1633 digestalg, "-digestalg")); |
|
1634 } |
|
1635 |
|
1636 if ((weakAlg & 2) == 2) { |
|
1637 System.out.println(String.format( |
|
1638 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1639 sigalg, "-sigalg")); |
|
1640 } |
|
1641 if ((weakAlg & 4) == 4) { |
|
1642 System.out.println(String.format( |
|
1643 rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), |
|
1644 tSADigestAlg, "-tsadigestalg")); |
|
1645 } |
|
1646 if ((weakAlg & 8) == 8) { |
|
1647 System.out.println(String.format( |
|
1648 rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."), |
|
1649 privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey))); |
|
1650 } |
|
1651 } else { |
|
1652 System.out.println(rb.getString("jar.signed.")); |
|
1653 } |
|
1654 if (hasExpiringCert || noTimestamp) { |
|
1655 if (!warningAppeared) { |
|
1656 System.out.println(); |
|
1657 System.out.println(rb.getString("Warning.")); |
|
1658 } |
|
1659 |
|
1660 if (hasExpiringCert) { |
|
1661 System.out.println( |
|
1662 rb.getString("The.signer.certificate.will.expire.within.six.months.")); |
|
1663 } |
|
1664 |
|
1665 if (noTimestamp) { |
|
1666 System.out.println( |
|
1667 String.format(rb.getString("no.timestamp.signing"), expireDate)); |
|
1668 } |
|
1669 } |
|
1670 |
|
1671 // no IOException thrown in the above try clause, so disable |
|
1672 // the catch clause. |
|
1673 // } catch(IOException ioe) { |
|
1674 // error(rb.getString("unable.to.sign.jar.")+ioe, ioe); |
|
1675 // } |
|
1676 } |
1722 } |
1677 |
1723 |
1678 /** |
1724 /** |
1679 * signature-related files include: |
1725 * signature-related files include: |
1680 * . META-INF/MANIFEST.MF |
1726 * . META-INF/MANIFEST.MF |
1689 } |
1735 } |
1690 |
1736 |
1691 Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>(); |
1737 Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>(); |
1692 |
1738 |
1693 /** |
1739 /** |
1694 * Returns a string of singer info, with a newline at the end |
1740 * Returns a string of signer info, with a newline at the end. |
|
1741 * Called by verifyJar(). |
1695 */ |
1742 */ |
1696 private String signerInfo(CodeSigner signer, String tab) throws Exception { |
1743 private String signerInfo(CodeSigner signer, String tab) throws Exception { |
1697 if (cacheForSignerInfo.containsKey(signer)) { |
1744 if (cacheForSignerInfo.containsKey(signer)) { |
1698 return cacheForSignerInfo.get(signer); |
1745 return cacheForSignerInfo.get(signer); |
1699 } |
1746 } |
1700 StringBuilder sb = new StringBuilder(); |
|
1701 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); |
1747 List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); |
1702 // display the signature timestamp, if present |
1748 // signing time is only displayed on verification |
|
1749 Timestamp ts = signer.getTimestamp(); |
|
1750 String tsLine = ""; |
|
1751 if (ts != null) { |
|
1752 tsLine = printTimestamp(tab, ts) + "\n"; |
|
1753 } |
|
1754 // Spaces before the ">>> Signer" and other lines are the same. |
|
1755 |
|
1756 String result = certsAndTSInfo(tab, tab, certs, ts); |
|
1757 cacheForSignerInfo.put(signer, tsLine + result); |
|
1758 return result; |
|
1759 } |
|
1760 |
|
1761 /** |
|
1762 * Fills info on certs and timestamp into a StringBuilder, sets |
|
1763 * warning flags (through printCert) and validates cert chains. |
|
1764 * |
|
1765 * @param tab1 spaces before the ">>> Signer" line |
|
1766 * @param tab2 spaces before the other lines |
|
1767 * @param certs the signer cert |
|
1768 * @param ts the timestamp, can be null |
|
1769 * @return the info as a string |
|
1770 */ |
|
1771 private String certsAndTSInfo( |
|
1772 String tab1, |
|
1773 String tab2, |
|
1774 List<? extends Certificate> certs, Timestamp ts) |
|
1775 throws Exception { |
|
1776 |
1703 Date timestamp; |
1777 Date timestamp; |
1704 Timestamp ts = signer.getTimestamp(); |
|
1705 if (ts != null) { |
1778 if (ts != null) { |
1706 sb.append(printTimestamp(tab, ts)); |
|
1707 sb.append('\n'); |
|
1708 timestamp = ts.getTimestamp(); |
1779 timestamp = ts.getTimestamp(); |
|
1780 noTimestamp = false; |
1709 } else { |
1781 } else { |
1710 timestamp = null; |
1782 timestamp = null; |
1711 noTimestamp = true; |
|
1712 } |
1783 } |
1713 // display the certificate(sb). The first one is end-entity cert and |
1784 // display the certificate(sb). The first one is end-entity cert and |
1714 // its KeyUsage should be checked. |
1785 // its KeyUsage should be checked. |
1715 boolean first = true; |
1786 boolean first = true; |
1716 sb.append(tab).append(rb.getString("...Signer")).append('\n'); |
1787 StringBuilder sb = new StringBuilder(); |
|
1788 sb.append(tab1).append(rb.getString("...Signer")).append('\n'); |
1717 for (Certificate c : certs) { |
1789 for (Certificate c : certs) { |
1718 sb.append(printCert(false, tab, c, timestamp, first)); |
1790 sb.append(printCert(false, tab2, c, timestamp, first)); |
1719 sb.append('\n'); |
1791 sb.append('\n'); |
1720 first = false; |
1792 first = false; |
1721 } |
1793 } |
1722 try { |
1794 try { |
1723 validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts); |
1795 validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts); |
1724 } catch (Exception e) { |
1796 } catch (Exception e) { |
1725 chainNotValidated = true; |
1797 chainNotValidated = true; |
1726 chainNotValidatedReason = e; |
1798 chainNotValidatedReason = e; |
1727 sb.append(tab).append(rb.getString(".Invalid.certificate.chain.")) |
1799 sb.append(tab2).append(rb.getString(".Invalid.certificate.chain.")) |
1728 .append(e.getLocalizedMessage()).append("]\n"); |
1800 .append(e.getLocalizedMessage()).append("]\n"); |
1729 } |
1801 } |
1730 if (ts != null) { |
1802 if (ts != null) { |
1731 sb.append(tab).append(rb.getString("...TSA")).append('\n'); |
1803 sb.append(tab1).append(rb.getString("...TSA")).append('\n'); |
1732 for (Certificate c : ts.getSignerCertPath().getCertificates()) { |
1804 for (Certificate c : ts.getSignerCertPath().getCertificates()) { |
1733 sb.append(printCert(true, tab, c, timestamp, false)); |
1805 sb.append(printCert(true, tab2, c, null, false)); |
1734 sb.append('\n'); |
1806 sb.append('\n'); |
1735 } |
1807 } |
1736 try { |
1808 try { |
1737 validateCertChain(Validator.VAR_TSA_SERVER, |
1809 validateCertChain(Validator.VAR_TSA_SERVER, |
1738 ts.getSignerCertPath().getCertificates(), null); |
1810 ts.getSignerCertPath().getCertificates(), null); |
1739 } catch (Exception e) { |
1811 } catch (Exception e) { |
1740 tsaChainNotValidated = true; |
1812 tsaChainNotValidated = true; |
1741 tsaChainNotValidatedReason = e; |
1813 tsaChainNotValidatedReason = e; |
1742 sb.append(tab).append(rb.getString(".Invalid.TSA.certificate.chain.")) |
1814 sb.append(tab2).append(rb.getString(".Invalid.TSA.certificate.chain.")) |
1743 .append(e.getLocalizedMessage()).append("]\n"); |
1815 .append(e.getLocalizedMessage()).append("]\n"); |
1744 } |
1816 } |
1745 } |
1817 } |
1746 if (certs.size() == 1 |
1818 if (certs.size() == 1 |
1747 && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) { |
1819 && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) { |
1748 signerSelfSigned = true; |
1820 signerSelfSigned = true; |
1749 } |
1821 } |
1750 String result = sb.toString(); |
1822 |
1751 cacheForSignerInfo.put(signer, result); |
1823 return sb.toString(); |
1752 return result; |
|
1753 } |
1824 } |
1754 |
1825 |
1755 void loadKeyStore(String keyStoreName, boolean prompt) { |
1826 void loadKeyStore(String keyStoreName, boolean prompt) { |
1756 |
1827 |
1757 if (!nullStream && keyStoreName == null) { |
1828 if (!nullStream && keyStoreName == null) { |
1758 keyStoreName = System.getProperty("user.home") + File.separator |
1829 keyStoreName = System.getProperty("user.home") + File.separator |
1759 + ".keystore"; |
1830 + ".keystore"; |
1760 } |
1831 } |
1761 |
1832 |
1762 try { |
1833 try { |
1763 Set<TrustAnchor> tas = new HashSet<>(); |
|
1764 try { |
1834 try { |
1765 KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); |
1835 KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); |
1766 if (caks != null) { |
1836 if (caks != null) { |
1767 Enumeration<String> aliases = caks.aliases(); |
1837 Enumeration<String> aliases = caks.aliases(); |
1768 while (aliases.hasMoreElements()) { |
1838 while (aliases.hasMoreElements()) { |
1769 String a = aliases.nextElement(); |
1839 String a = aliases.nextElement(); |
1770 try { |
1840 try { |
1771 tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null)); |
1841 trustedCerts.add((X509Certificate)caks.getCertificate(a)); |
1772 } catch (Exception e2) { |
1842 } catch (Exception e2) { |
1773 // ignore, when a SecretkeyEntry does not include a cert |
1843 // ignore, when a SecretkeyEntry does not include a cert |
1774 } |
1844 } |
1775 } |
1845 } |
1776 } |
1846 } |