1 /* 2 * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.tools.keytool; 27 28 import java.io.*; 29 import java.nio.file.Files; 30 import java.nio.file.Paths; 31 import java.security.CodeSigner; 32 import java.security.CryptoPrimitive; 33 import java.security.KeyStore; 34 import java.security.KeyStoreException; 35 import java.security.MessageDigest; 36 import java.security.Key; 37 import java.security.PublicKey; 38 import java.security.PrivateKey; 39 import java.security.Security; 40 import java.security.Signature; 41 import java.security.Timestamp; 42 import java.security.UnrecoverableEntryException; 43 import java.security.UnrecoverableKeyException; 44 import java.security.NoSuchAlgorithmException; 45 import java.security.Principal; 46 import java.security.Provider; 47 import java.security.cert.Certificate; 48 import java.security.cert.CertificateFactory; 49 import java.security.cert.CertStoreException; 50 import java.security.cert.CRL; 51 import java.security.cert.X509Certificate; 52 import java.security.cert.CertificateException; 53 import java.security.interfaces.ECKey; 54 import java.security.spec.AlgorithmParameterSpec; 55 import java.security.spec.ECParameterSpec; 56 import java.text.Collator; 57 import java.text.MessageFormat; 58 import java.util.*; 59 import java.util.jar.JarEntry; 60 import java.util.jar.JarFile; 61 import java.lang.reflect.Constructor; 62 import java.math.BigInteger; 63 import java.net.URI; 64 import java.net.URL; 65 import java.net.URLClassLoader; 66 import java.security.cert.CertStore; 67 68 import java.security.cert.X509CRL; 69 import java.security.cert.X509CRLEntry; 70 import java.security.cert.X509CRLSelector; 71 import javax.security.auth.x500.X500Principal; 72 import java.util.Base64; 73 74 import sun.security.util.DisabledAlgorithmConstraints; 75 import sun.security.util.KeyUtil; 76 import sun.security.util.NamedCurve; 77 import sun.security.util.ObjectIdentifier; 78 import sun.security.pkcs10.PKCS10; 79 import sun.security.pkcs10.PKCS10Attribute; 80 import sun.security.provider.X509Factory; 81 import sun.security.provider.certpath.CertStoreHelper; 82 import sun.security.util.Password; 83 import sun.security.util.SecurityProviderConstants; 84 import sun.security.util.SignatureUtil; 85 import javax.crypto.KeyGenerator; 86 import javax.crypto.SecretKey; 87 import javax.crypto.SecretKeyFactory; 88 import javax.crypto.spec.PBEKeySpec; 89 90 import sun.security.pkcs.PKCS9Attribute; 91 import sun.security.tools.KeyStoreUtil; 92 import sun.security.tools.PathList; 93 import sun.security.util.DerValue; 94 import sun.security.util.Pem; 95 import sun.security.x509.*; 96 97 import static java.security.KeyStore.*; 98 import static sun.security.tools.keytool.Main.Command.*; 99 import static sun.security.tools.keytool.Main.Option.*; 100 101 /** 102 * This tool manages keystores. 103 * 104 * @author Jan Luehe 105 * 106 * 107 * @see java.security.KeyStore 108 * @see sun.security.provider.KeyProtector 109 * @see sun.security.provider.JavaKeyStore 110 * 111 * @since 1.2 112 */ 113 public final class Main { 114 115 private static final byte[] CRLF = new byte[] {'\r', '\n'}; 116 117 private boolean debug = false; 118 private Command command = null; 119 private String sigAlgName = null; 120 private String keyAlgName = null; 121 private boolean verbose = false; 122 private int keysize = -1; 123 private boolean rfc = false; 124 private long validity = (long)90; 125 private String alias = null; 126 private String dname = null; 127 private String dest = null; 128 private String filename = null; 129 private String infilename = null; 130 private String outfilename = null; 131 private String srcksfname = null; 132 133 // User-specified providers are added before any command is called. 134 // However, they are not removed before the end of the main() method. 135 // If you're calling KeyTool.main() directly in your own Java program, 136 // please programtically add any providers you need and do not specify 137 // them through the command line. 138 139 private Set<Pair <String, String>> providers = null; 140 private String storetype = null; 141 private String srcProviderName = null; 142 private String providerName = null; 143 private String pathlist = null; 144 private char[] storePass = null; 145 private char[] storePassNew = null; 146 private char[] keyPass = null; 147 private char[] keyPassNew = null; 148 private char[] newPass = null; 149 private char[] destKeyPass = null; 150 private char[] srckeyPass = null; 151 private String ksfname = null; 152 private File ksfile = null; 153 private InputStream ksStream = null; // keystore stream 154 private String sslserver = null; 155 private String jarfile = null; 156 private KeyStore keyStore = null; 157 private boolean token = false; 158 private boolean nullStream = false; 159 private boolean kssave = false; 160 private boolean noprompt = false; 161 private boolean trustcacerts = false; 162 private boolean nowarn = false; 163 private boolean protectedPath = false; 164 private boolean srcprotectedPath = false; 165 private CertificateFactory cf = null; 166 private KeyStore caks = null; // "cacerts" keystore 167 private char[] srcstorePass = null; 168 private String srcstoretype = null; 169 private Set<char[]> passwords = new HashSet<>(); 170 private String startDate = null; 171 172 private List<String> ids = new ArrayList<>(); // used in GENCRL 173 private List<String> v3ext = new ArrayList<>(); 174 175 // In-place importkeystore is special. 176 // A backup is needed, and no need to prompt for deststorepass. 177 private boolean inplaceImport = false; 178 private String inplaceBackupName = null; 179 180 // Warnings on weak algorithms etc 181 private List<String> weakWarnings = new ArrayList<>(); 182 183 private static final DisabledAlgorithmConstraints DISABLED_CHECK = 184 new DisabledAlgorithmConstraints( 185 DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); 186 187 private static final DisabledAlgorithmConstraints LEGACY_CHECK = 188 new DisabledAlgorithmConstraints( 189 DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS); 190 191 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections 192 .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 193 194 enum Command { 195 CERTREQ("Generates.a.certificate.request", 196 ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME, 197 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 198 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 199 CHANGEALIAS("Changes.an.entry.s.alias", 200 ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS, 201 STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 202 PROVIDERPATH, V, PROTECTED), 203 DELETE("Deletes.an.entry", 204 ALIAS, KEYSTORE, STOREPASS, STORETYPE, 205 PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 206 PROVIDERPATH, V, PROTECTED), 207 EXPORTCERT("Exports.certificate", 208 RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS, 209 STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 210 PROVIDERPATH, V, PROTECTED), 211 GENKEYPAIR("Generates.a.key.pair", 212 ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME, 213 STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, 214 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 215 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 216 GENSECKEY("Generates.a.secret.key", 217 ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE, 218 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 219 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 220 GENCERT("Generates.certificate.from.a.certificate.request", 221 RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME, 222 STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, 223 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 224 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 225 IMPORTCERT("Imports.a.certificate.or.a.certificate.chain", 226 NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN, 227 KEYPASS, KEYSTORE, STOREPASS, STORETYPE, 228 PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 229 PROVIDERPATH, V), 230 IMPORTPASS("Imports.a.password", 231 ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE, 232 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 233 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 234 IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore", 235 SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE, 236 DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS, 237 SRCPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME, 238 SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS, 239 NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, 240 V), 241 KEYPASSWD("Changes.the.key.password.of.an.entry", 242 ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS, 243 STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 244 PROVIDERPATH, V), 245 LIST("Lists.entries.in.a.keystore", 246 RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE, 247 PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, 248 PROVIDERPATH, V, PROTECTED), 249 PRINTCERT("Prints.the.content.of.a.certificate", 250 RFC, FILEIN, SSLSERVER, JARFILE, V), 251 PRINTCERTREQ("Prints.the.content.of.a.certificate.request", 252 FILEIN, V), 253 PRINTCRL("Prints.the.content.of.a.CRL.file", 254 FILEIN, V), 255 STOREPASSWD("Changes.the.store.password.of.a.keystore", 256 NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, 257 PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), 258 259 // Undocumented start here, KEYCLONE is used a marker in -help; 260 261 KEYCLONE("Clones.a.key.entry", 262 ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE, 263 KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, 264 PROVIDERARG, PROVIDERPATH, V), 265 SELFCERT("Generates.a.self.signed.certificate", 266 ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS, 267 STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, 268 PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), 269 GENCRL("Generates.CRL", 270 RFC, FILEOUT, ID, 271 ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE, 272 STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, 273 PROVIDERARG, PROVIDERPATH, V, PROTECTED), 274 IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database", 275 FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, 276 PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V); 277 278 final String description; 279 final Option[] options; 280 Command(String d, Option... o) { 281 description = d; 282 options = o; 283 } 284 @Override 285 public String toString() { 286 return "-" + name().toLowerCase(Locale.ENGLISH); 287 } 288 }; 289 290 enum Option { 291 ALIAS("alias", "<alias>", "alias.name.of.the.entry.to.process"), 292 DESTALIAS("destalias", "<destalias>", "destination.alias"), 293 DESTKEYPASS("destkeypass", "<arg>", "destination.key.password"), 294 DESTKEYSTORE("destkeystore", "<destkeystore>", "destination.keystore.name"), 295 DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"), 296 DESTPROVIDERNAME("destprovidername", "<destprovidername>", "destination.keystore.provider.name"), 297 DESTSTOREPASS("deststorepass", "<arg>", "destination.keystore.password"), 298 DESTSTORETYPE("deststoretype", "<deststoretype>", "destination.keystore.type"), 299 DNAME("dname", "<dname>", "distinguished.name"), 300 EXT("ext", "<value>", "X.509.extension"), 301 FILEOUT("file", "<filename>", "output.file.name"), 302 FILEIN("file", "<filename>", "input.file.name"), 303 ID("id", "<id:reason>", "Serial.ID.of.cert.to.revoke"), 304 INFILE("infile", "<filename>", "input.file.name"), 305 KEYALG("keyalg", "<keyalg>", "key.algorithm.name"), 306 KEYPASS("keypass", "<arg>", "key.password"), 307 KEYSIZE("keysize", "<keysize>", "key.bit.size"), 308 KEYSTORE("keystore", "<keystore>", "keystore.name"), 309 NEW("new", "<arg>", "new.password"), 310 NOPROMPT("noprompt", null, "do.not.prompt"), 311 OUTFILE("outfile", "<filename>", "output.file.name"), 312 PROTECTED("protected", null, "password.through.protected.mechanism"), 313 PROVIDERARG("providerarg", "<arg>", "provider.argument"), 314 PROVIDERCLASS("providerclass", "<providerclass>", "provider.class.name"), 315 PROVIDERNAME("providername", "<providername>", "provider.name"), 316 PROVIDERPATH("providerpath", "<pathlist>", "provider.classpath"), 317 RFC("rfc", null, "output.in.RFC.style"), 318 SIGALG("sigalg", "<sigalg>", "signature.algorithm.name"), 319 SRCALIAS("srcalias", "<srcalias>", "source.alias"), 320 SRCKEYPASS("srckeypass", "<arg>", "source.key.password"), 321 SRCKEYSTORE("srckeystore", "<srckeystore>", "source.keystore.name"), 322 SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"), 323 SRCPROVIDERNAME("srcprovidername", "<srcprovidername>", "source.keystore.provider.name"), 324 SRCSTOREPASS("srcstorepass", "<arg>", "source.keystore.password"), 325 SRCSTORETYPE("srcstoretype", "<srcstoretype>", "source.keystore.type"), 326 SSLSERVER("sslserver", "<server[:port]>", "SSL.server.host.and.port"), 327 JARFILE("jarfile", "<filename>", "signed.jar.file"), 328 STARTDATE("startdate", "<startdate>", "certificate.validity.start.date.time"), 329 STOREPASS("storepass", "<arg>", "keystore.password"), 330 STORETYPE("storetype", "<storetype>", "keystore.type"), 331 TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"), 332 V("v", null, "verbose.output"), 333 VALIDITY("validity", "<valDays>", "validity.number.of.days"); 334 335 final String name, arg, description; 336 Option(String name, String arg, String description) { 337 this.name = name; 338 this.arg = arg; 339 this.description = description; 340 } 341 @Override 342 public String toString() { 343 return "-" + name; 344 } 345 }; 346 347 private static final Class<?>[] PARAM_STRING = { String.class }; 348 349 private static final String NONE = "NONE"; 350 private static final String P11KEYSTORE = "PKCS11"; 351 private static final String P12KEYSTORE = "PKCS12"; 352 private static final String keyAlias = "mykey"; 353 354 // for i18n 355 private static final java.util.ResourceBundle rb = 356 java.util.ResourceBundle.getBundle( 357 "sun.security.tools.keytool.Resources"); 358 private static final Collator collator = Collator.getInstance(); 359 static { 360 // this is for case insensitive string comparisons 361 collator.setStrength(Collator.PRIMARY); 362 }; 363 364 private Main() { } 365 366 public static void main(String[] args) throws Exception { 367 Main kt = new Main(); 368 kt.run(args, System.out); 369 } 370 371 private void run(String[] args, PrintStream out) throws Exception { 372 try { 373 parseArgs(args); 374 if (command != null) { 375 doCommands(out); 376 } 377 } catch (Exception e) { 378 System.out.println(rb.getString("keytool.error.") + e); 379 if (verbose) { 380 e.printStackTrace(System.out); 381 } 382 if (!debug) { 383 System.exit(1); 384 } else { 385 throw e; 386 } 387 } finally { 388 printWeakWarnings(false); 389 for (char[] pass : passwords) { 390 if (pass != null) { 391 Arrays.fill(pass, ' '); 392 pass = null; 393 } 394 } 395 396 if (ksStream != null) { 397 ksStream.close(); 398 } 399 } 400 } 401 402 /** 403 * Parse command line arguments. 404 */ 405 void parseArgs(String[] args) { 406 407 int i=0; 408 boolean help = args.length == 0; 409 410 for (i=0; (i < args.length) && args[i].startsWith("-"); i++) { 411 412 String flags = args[i]; 413 414 // Check if the last option needs an arg 415 if (i == args.length - 1) { 416 for (Option option: Option.values()) { 417 // Only options with an arg need to be checked 418 if (collator.compare(flags, option.toString()) == 0) { 419 if (option.arg != null) errorNeedArgument(flags); 420 break; 421 } 422 } 423 } 424 425 /* 426 * Check modifiers 427 */ 428 String modifier = null; 429 int pos = flags.indexOf(':'); 430 if (pos > 0) { 431 modifier = flags.substring(pos+1); 432 flags = flags.substring(0, pos); 433 } 434 /* 435 * command modes 436 */ 437 boolean isCommand = false; 438 for (Command c: Command.values()) { 439 if (collator.compare(flags, c.toString()) == 0) { 440 command = c; 441 isCommand = true; 442 break; 443 } 444 } 445 446 if (isCommand) { 447 // already recognized as a command 448 } else if (collator.compare(flags, "-export") == 0) { 449 command = EXPORTCERT; 450 } else if (collator.compare(flags, "-genkey") == 0) { 451 command = GENKEYPAIR; 452 } else if (collator.compare(flags, "-import") == 0) { 453 command = IMPORTCERT; 454 } else if (collator.compare(flags, "-importpassword") == 0) { 455 command = IMPORTPASS; 456 } else if (collator.compare(flags, "-help") == 0) { 457 help = true; 458 } else if (collator.compare(flags, "-nowarn") == 0) { 459 nowarn = true; 460 } 461 462 /* 463 * specifiers 464 */ 465 else if (collator.compare(flags, "-keystore") == 0 || 466 collator.compare(flags, "-destkeystore") == 0) { 467 ksfname = args[++i]; 468 } else if (collator.compare(flags, "-storepass") == 0 || 469 collator.compare(flags, "-deststorepass") == 0) { 470 storePass = getPass(modifier, args[++i]); 471 passwords.add(storePass); 472 } else if (collator.compare(flags, "-storetype") == 0 || 473 collator.compare(flags, "-deststoretype") == 0) { 474 storetype = KeyStoreUtil.niceStoreTypeName(args[++i]); 475 } else if (collator.compare(flags, "-srcstorepass") == 0) { 476 srcstorePass = getPass(modifier, args[++i]); 477 passwords.add(srcstorePass); 478 } else if (collator.compare(flags, "-srcstoretype") == 0) { 479 srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]); 480 } else if (collator.compare(flags, "-srckeypass") == 0) { 481 srckeyPass = getPass(modifier, args[++i]); 482 passwords.add(srckeyPass); 483 } else if (collator.compare(flags, "-srcprovidername") == 0) { 484 srcProviderName = args[++i]; 485 } else if (collator.compare(flags, "-providername") == 0 || 486 collator.compare(flags, "-destprovidername") == 0) { 487 providerName = args[++i]; 488 } else if (collator.compare(flags, "-providerpath") == 0) { 489 pathlist = args[++i]; 490 } else if (collator.compare(flags, "-keypass") == 0) { 491 keyPass = getPass(modifier, args[++i]); 492 passwords.add(keyPass); 493 } else if (collator.compare(flags, "-new") == 0) { 494 newPass = getPass(modifier, args[++i]); 495 passwords.add(newPass); 496 } else if (collator.compare(flags, "-destkeypass") == 0) { 497 destKeyPass = getPass(modifier, args[++i]); 498 passwords.add(destKeyPass); 499 } else if (collator.compare(flags, "-alias") == 0 || 500 collator.compare(flags, "-srcalias") == 0) { 501 alias = args[++i]; 502 } else if (collator.compare(flags, "-dest") == 0 || 503 collator.compare(flags, "-destalias") == 0) { 504 dest = args[++i]; 505 } else if (collator.compare(flags, "-dname") == 0) { 506 dname = args[++i]; 507 } else if (collator.compare(flags, "-keysize") == 0) { 508 keysize = Integer.parseInt(args[++i]); 509 } else if (collator.compare(flags, "-keyalg") == 0) { 510 keyAlgName = args[++i]; 511 } else if (collator.compare(flags, "-sigalg") == 0) { 512 sigAlgName = args[++i]; 513 } else if (collator.compare(flags, "-startdate") == 0) { 514 startDate = args[++i]; 515 } else if (collator.compare(flags, "-validity") == 0) { 516 validity = Long.parseLong(args[++i]); 517 } else if (collator.compare(flags, "-ext") == 0) { 518 v3ext.add(args[++i]); 519 } else if (collator.compare(flags, "-id") == 0) { 520 ids.add(args[++i]); 521 } else if (collator.compare(flags, "-file") == 0) { 522 filename = args[++i]; 523 } else if (collator.compare(flags, "-infile") == 0) { 524 infilename = args[++i]; 525 } else if (collator.compare(flags, "-outfile") == 0) { 526 outfilename = args[++i]; 527 } else if (collator.compare(flags, "-sslserver") == 0) { 528 sslserver = args[++i]; 529 } else if (collator.compare(flags, "-jarfile") == 0) { 530 jarfile = args[++i]; 531 } else if (collator.compare(flags, "-srckeystore") == 0) { 532 srcksfname = args[++i]; 533 } else if ((collator.compare(flags, "-provider") == 0) || 534 (collator.compare(flags, "-providerclass") == 0)) { 535 if (providers == null) { 536 providers = new HashSet<Pair <String, String>> (3); 537 } 538 String providerClass = args[++i]; 539 String providerArg = null; 540 541 if (args.length > (i+1)) { 542 flags = args[i+1]; 543 if (collator.compare(flags, "-providerarg") == 0) { 544 if (args.length == (i+2)) errorNeedArgument(flags); 545 providerArg = args[i+2]; 546 i += 2; 547 } 548 } 549 providers.add( 550 Pair.of(providerClass, providerArg)); 551 } 552 553 /* 554 * options 555 */ 556 else if (collator.compare(flags, "-v") == 0) { 557 verbose = true; 558 } else if (collator.compare(flags, "-debug") == 0) { 559 debug = true; 560 } else if (collator.compare(flags, "-rfc") == 0) { 561 rfc = true; 562 } else if (collator.compare(flags, "-noprompt") == 0) { 563 noprompt = true; 564 } else if (collator.compare(flags, "-trustcacerts") == 0) { 565 trustcacerts = true; 566 } else if (collator.compare(flags, "-protected") == 0 || 567 collator.compare(flags, "-destprotected") == 0) { 568 protectedPath = true; 569 } else if (collator.compare(flags, "-srcprotected") == 0) { 570 srcprotectedPath = true; 571 } else { 572 System.err.println(rb.getString("Illegal.option.") + flags); 573 tinyHelp(); 574 } 575 } 576 577 if (i<args.length) { 578 System.err.println(rb.getString("Illegal.option.") + args[i]); 579 tinyHelp(); 580 } 581 582 if (command == null) { 583 if (help) { 584 usage(); 585 } else { 586 System.err.println(rb.getString("Usage.error.no.command.provided")); 587 tinyHelp(); 588 } 589 } else if (help) { 590 usage(); 591 command = null; 592 } 593 } 594 595 boolean isKeyStoreRelated(Command cmd) { 596 return cmd != PRINTCERT && cmd != PRINTCERTREQ; 597 } 598 599 600 /** 601 * Execute the commands. 602 */ 603 void doCommands(PrintStream out) throws Exception { 604 if (P11KEYSTORE.equalsIgnoreCase(storetype) || 605 KeyStoreUtil.isWindowsKeyStore(storetype)) { 606 token = true; 607 if (ksfname == null) { 608 ksfname = NONE; 609 } 610 } 611 if (NONE.equals(ksfname)) { 612 nullStream = true; 613 } 614 615 if (token && !nullStream) { 616 System.err.println(MessageFormat.format(rb.getString 617 (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype)); 618 System.err.println(); 619 tinyHelp(); 620 } 621 622 if (token && 623 (command == KEYPASSWD || command == STOREPASSWD)) { 624 throw new UnsupportedOperationException(MessageFormat.format(rb.getString 625 (".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype)); 626 } 627 628 if (token && (keyPass != null || newPass != null || destKeyPass != null)) { 629 throw new IllegalArgumentException(MessageFormat.format(rb.getString 630 (".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype)); 631 } 632 633 if (protectedPath) { 634 if (storePass != null || keyPass != null || 635 newPass != null || destKeyPass != null) { 636 throw new IllegalArgumentException(rb.getString 637 ("if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified")); 638 } 639 } 640 641 if (srcprotectedPath) { 642 if (srcstorePass != null || srckeyPass != null) { 643 throw new IllegalArgumentException(rb.getString 644 ("if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified")); 645 } 646 } 647 648 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { 649 if (storePass != null || keyPass != null || 650 newPass != null || destKeyPass != null) { 651 throw new IllegalArgumentException(rb.getString 652 ("if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified")); 653 } 654 } 655 656 if (KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { 657 if (srcstorePass != null || srckeyPass != null) { 658 throw new IllegalArgumentException(rb.getString 659 ("if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified")); 660 } 661 } 662 663 if (validity <= (long)0) { 664 throw new Exception 665 (rb.getString("Validity.must.be.greater.than.zero")); 666 } 667 668 // Try to load and install specified provider 669 if (providers != null) { 670 ClassLoader cl = null; 671 if (pathlist != null) { 672 String path = null; 673 path = PathList.appendPath( 674 path, System.getProperty("java.class.path")); 675 path = PathList.appendPath( 676 path, System.getProperty("env.class.path")); 677 path = PathList.appendPath(path, pathlist); 678 679 URL[] urls = PathList.pathToURLs(path); 680 cl = new URLClassLoader(urls); 681 } else { 682 cl = ClassLoader.getSystemClassLoader(); 683 } 684 685 for (Pair <String, String> provider: providers) { 686 String provName = provider.fst; 687 Class<?> provClass; 688 if (cl != null) { 689 provClass = cl.loadClass(provName); 690 } else { 691 provClass = Class.forName(provName); 692 } 693 694 String provArg = provider.snd; 695 Object obj; 696 if (provArg == null) { 697 obj = provClass.newInstance(); 698 } else { 699 Constructor<?> c = provClass.getConstructor(PARAM_STRING); 700 obj = c.newInstance(provArg); 701 } 702 if (!(obj instanceof Provider)) { 703 MessageFormat form = new MessageFormat 704 (rb.getString("provName.not.a.provider")); 705 Object[] source = {provName}; 706 throw new Exception(form.format(source)); 707 } 708 Security.addProvider((Provider)obj); 709 } 710 } 711 712 if (command == LIST && verbose && rfc) { 713 System.err.println(rb.getString 714 ("Must.not.specify.both.v.and.rfc.with.list.command")); 715 tinyHelp(); 716 } 717 718 // Make sure provided passwords are at least 6 characters long 719 if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) { 720 throw new Exception(rb.getString 721 ("Key.password.must.be.at.least.6.characters")); 722 } 723 if (newPass != null && newPass.length < 6) { 724 throw new Exception(rb.getString 725 ("New.password.must.be.at.least.6.characters")); 726 } 727 if (destKeyPass != null && destKeyPass.length < 6) { 728 throw new Exception(rb.getString 729 ("New.password.must.be.at.least.6.characters")); 730 } 731 732 // Set this before inplaceImport check so we can compare name. 733 if (ksfname == null) { 734 ksfname = System.getProperty("user.home") + File.separator 735 + ".keystore"; 736 } 737 738 KeyStore srcKeyStore = null; 739 if (command == IMPORTKEYSTORE) { 740 inplaceImport = inplaceImportCheck(); 741 if (inplaceImport) { 742 // We load srckeystore first so we have srcstorePass that 743 // can be assigned to storePass 744 srcKeyStore = loadSourceKeyStore(); 745 if (storePass == null) { 746 storePass = srcstorePass; 747 } 748 } 749 } 750 751 // Check if keystore exists. 752 // If no keystore has been specified at the command line, try to use 753 // the default, which is located in $HOME/.keystore. 754 // If the command is "genkey", "identitydb", "import", or "printcert", 755 // it is OK not to have a keystore. 756 757 // DO NOT open the existing keystore if this is an in-place import. 758 // The keystore should be created as brand new. 759 if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) { 760 try { 761 ksfile = new File(ksfname); 762 // Check if keystore file is empty 763 if (ksfile.exists() && ksfile.length() == 0) { 764 throw new Exception(rb.getString 765 ("Keystore.file.exists.but.is.empty.") + ksfname); 766 } 767 ksStream = new FileInputStream(ksfile); 768 } catch (FileNotFoundException e) { 769 if (command != GENKEYPAIR && 770 command != GENSECKEY && 771 command != IDENTITYDB && 772 command != IMPORTCERT && 773 command != IMPORTPASS && 774 command != IMPORTKEYSTORE && 775 command != PRINTCRL) { 776 throw new Exception(rb.getString 777 ("Keystore.file.does.not.exist.") + ksfname); 778 } 779 } 780 } 781 782 if ((command == KEYCLONE || command == CHANGEALIAS) 783 && dest == null) { 784 dest = getAlias("destination"); 785 if ("".equals(dest)) { 786 throw new Exception(rb.getString 787 ("Must.specify.destination.alias")); 788 } 789 } 790 791 if (command == DELETE && alias == null) { 792 alias = getAlias(null); 793 if ("".equals(alias)) { 794 throw new Exception(rb.getString("Must.specify.alias")); 795 } 796 } 797 798 // Create new keystore 799 if (storetype == null) { 800 storetype = KeyStore.getDefaultType(); 801 } 802 if (providerName == null) { 803 keyStore = KeyStore.getInstance(storetype); 804 } else { 805 keyStore = KeyStore.getInstance(storetype, providerName); 806 } 807 808 /* 809 * Load the keystore data. 810 * 811 * At this point, it's OK if no keystore password has been provided. 812 * We want to make sure that we can load the keystore data, i.e., 813 * the keystore data has the right format. If we cannot load the 814 * keystore, why bother asking the user for his or her password? 815 * Only if we were able to load the keystore, and no keystore 816 * password has been provided, will we prompt the user for the 817 * keystore password to verify the keystore integrity. 818 * This means that the keystore is loaded twice: first load operation 819 * checks the keystore format, second load operation verifies the 820 * keystore integrity. 821 * 822 * If the keystore password has already been provided (at the 823 * command line), however, the keystore is loaded only once, and the 824 * keystore format and integrity are checked "at the same time". 825 * 826 * Null stream keystores are loaded later. 827 */ 828 if (!nullStream) { 829 if (inplaceImport) { 830 keyStore.load(null, storePass); 831 } else { 832 keyStore.load(ksStream, storePass); 833 } 834 if (ksStream != null) { 835 ksStream.close(); 836 } 837 } 838 839 if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) { 840 throw new UnsupportedOperationException(rb.getString 841 (".keypasswd.commands.not.supported.if.storetype.is.PKCS12")); 842 } 843 844 // All commands that create or modify the keystore require a keystore 845 // password. 846 847 if (nullStream && storePass != null) { 848 keyStore.load(null, storePass); 849 } else if (!nullStream && storePass != null) { 850 // If we are creating a new non nullStream-based keystore, 851 // insist that the password be at least 6 characters 852 if (ksStream == null && storePass.length < 6) { 853 throw new Exception(rb.getString 854 ("Keystore.password.must.be.at.least.6.characters")); 855 } 856 } else if (storePass == null) { 857 858 // only prompt if (protectedPath == false) 859 860 if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) && 861 (command == CERTREQ || 862 command == DELETE || 863 command == GENKEYPAIR || 864 command == GENSECKEY || 865 command == IMPORTCERT || 866 command == IMPORTPASS || 867 command == IMPORTKEYSTORE || 868 command == KEYCLONE || 869 command == CHANGEALIAS || 870 command == SELFCERT || 871 command == STOREPASSWD || 872 command == KEYPASSWD || 873 command == IDENTITYDB)) { 874 int count = 0; 875 do { 876 if (command == IMPORTKEYSTORE) { 877 System.err.print 878 (rb.getString("Enter.destination.keystore.password.")); 879 } else { 880 System.err.print 881 (rb.getString("Enter.keystore.password.")); 882 } 883 System.err.flush(); 884 storePass = Password.readPassword(System.in); 885 passwords.add(storePass); 886 887 // If we are creating a new non nullStream-based keystore, 888 // insist that the password be at least 6 characters 889 if (!nullStream && (storePass == null || storePass.length < 6)) { 890 System.err.println(rb.getString 891 ("Keystore.password.is.too.short.must.be.at.least.6.characters")); 892 storePass = null; 893 } 894 895 // If the keystore file does not exist and needs to be 896 // created, the storepass should be prompted twice. 897 if (storePass != null && !nullStream && ksStream == null) { 898 System.err.print(rb.getString("Re.enter.new.password.")); 899 char[] storePassAgain = Password.readPassword(System.in); 900 passwords.add(storePassAgain); 901 if (!Arrays.equals(storePass, storePassAgain)) { 902 System.err.println 903 (rb.getString("They.don.t.match.Try.again")); 904 storePass = null; 905 } 906 } 907 908 count++; 909 } while ((storePass == null) && count < 3); 910 911 912 if (storePass == null) { 913 System.err.println 914 (rb.getString("Too.many.failures.try.later")); 915 return; 916 } 917 } else if (!protectedPath 918 && !KeyStoreUtil.isWindowsKeyStore(storetype) 919 && isKeyStoreRelated(command)) { 920 // here we have EXPORTCERT and LIST (info valid until STOREPASSWD) 921 if (command != PRINTCRL) { 922 System.err.print(rb.getString("Enter.keystore.password.")); 923 System.err.flush(); 924 storePass = Password.readPassword(System.in); 925 passwords.add(storePass); 926 } 927 } 928 929 // Now load a nullStream-based keystore, 930 // or verify the integrity of an input stream-based keystore 931 if (nullStream) { 932 keyStore.load(null, storePass); 933 } else if (ksStream != null) { 934 ksStream = new FileInputStream(ksfile); 935 keyStore.load(ksStream, storePass); 936 ksStream.close(); 937 } 938 } 939 940 if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) { 941 MessageFormat form = new MessageFormat(rb.getString( 942 "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.")); 943 if (keyPass != null && !Arrays.equals(storePass, keyPass)) { 944 Object[] source = {"-keypass"}; 945 System.err.println(form.format(source)); 946 keyPass = storePass; 947 } 948 if (newPass != null && !Arrays.equals(storePass, newPass)) { 949 Object[] source = {"-new"}; 950 System.err.println(form.format(source)); 951 newPass = storePass; 952 } 953 if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) { 954 Object[] source = {"-destkeypass"}; 955 System.err.println(form.format(source)); 956 destKeyPass = storePass; 957 } 958 } 959 960 // Create a certificate factory 961 if (command == PRINTCERT || command == IMPORTCERT 962 || command == IDENTITYDB || command == PRINTCRL) { 963 cf = CertificateFactory.getInstance("X509"); 964 } 965 966 // -trustcacerts can only be specified on -importcert. 967 // Reset it so that warnings on CA cert will remain for 968 // -printcert, etc. 969 if (command != IMPORTCERT) { 970 trustcacerts = false; 971 } 972 973 if (trustcacerts) { 974 caks = KeyStoreUtil.getCacertsKeyStore(); 975 } 976 977 // Perform the specified command 978 if (command == CERTREQ) { 979 if (filename != null) { 980 try (PrintStream ps = new PrintStream(new FileOutputStream 981 (filename))) { 982 doCertReq(alias, sigAlgName, ps); 983 } 984 } else { 985 doCertReq(alias, sigAlgName, out); 986 } 987 if (verbose && filename != null) { 988 MessageFormat form = new MessageFormat(rb.getString 989 ("Certification.request.stored.in.file.filename.")); 990 Object[] source = {filename}; 991 System.err.println(form.format(source)); 992 System.err.println(rb.getString("Submit.this.to.your.CA")); 993 } 994 } else if (command == DELETE) { 995 doDeleteEntry(alias); 996 kssave = true; 997 } else if (command == EXPORTCERT) { 998 if (filename != null) { 999 try (PrintStream ps = new PrintStream(new FileOutputStream 1000 (filename))) { 1001 doExportCert(alias, ps); 1002 } 1003 } else { 1004 doExportCert(alias, out); 1005 } 1006 if (filename != null) { 1007 MessageFormat form = new MessageFormat(rb.getString 1008 ("Certificate.stored.in.file.filename.")); 1009 Object[] source = {filename}; 1010 System.err.println(form.format(source)); 1011 } 1012 } else if (command == GENKEYPAIR) { 1013 if (keyAlgName == null) { 1014 keyAlgName = "DSA"; 1015 } 1016 doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName); 1017 kssave = true; 1018 } else if (command == GENSECKEY) { 1019 if (keyAlgName == null) { 1020 keyAlgName = "DES"; 1021 } 1022 doGenSecretKey(alias, keyAlgName, keysize); 1023 kssave = true; 1024 } else if (command == IMPORTPASS) { 1025 if (keyAlgName == null) { 1026 keyAlgName = "PBE"; 1027 } 1028 // password is stored as a secret key 1029 doGenSecretKey(alias, keyAlgName, keysize); 1030 kssave = true; 1031 } else if (command == IDENTITYDB) { 1032 if (filename != null) { 1033 try (InputStream inStream = new FileInputStream(filename)) { 1034 doImportIdentityDatabase(inStream); 1035 } 1036 } else { 1037 doImportIdentityDatabase(System.in); 1038 } 1039 } else if (command == IMPORTCERT) { 1040 InputStream inStream = System.in; 1041 if (filename != null) { 1042 inStream = new FileInputStream(filename); 1043 } 1044 String importAlias = (alias!=null)?alias:keyAlias; 1045 try { 1046 if (keyStore.entryInstanceOf( 1047 importAlias, KeyStore.PrivateKeyEntry.class)) { 1048 kssave = installReply(importAlias, inStream); 1049 if (kssave) { 1050 System.err.println(rb.getString 1051 ("Certificate.reply.was.installed.in.keystore")); 1052 } else { 1053 System.err.println(rb.getString 1054 ("Certificate.reply.was.not.installed.in.keystore")); 1055 } 1056 } else if (!keyStore.containsAlias(importAlias) || 1057 keyStore.entryInstanceOf(importAlias, 1058 KeyStore.TrustedCertificateEntry.class)) { 1059 kssave = addTrustedCert(importAlias, inStream); 1060 if (kssave) { 1061 System.err.println(rb.getString 1062 ("Certificate.was.added.to.keystore")); 1063 } else { 1064 System.err.println(rb.getString 1065 ("Certificate.was.not.added.to.keystore")); 1066 } 1067 } 1068 } finally { 1069 if (inStream != System.in) { 1070 inStream.close(); 1071 } 1072 } 1073 } else if (command == IMPORTKEYSTORE) { 1074 // When not in-place import, srcKeyStore is not loaded yet. 1075 if (srcKeyStore == null) { 1076 srcKeyStore = loadSourceKeyStore(); 1077 } 1078 doImportKeyStore(srcKeyStore); 1079 kssave = true; 1080 } else if (command == KEYCLONE) { 1081 keyPassNew = newPass; 1082 1083 // added to make sure only key can go thru 1084 if (alias == null) { 1085 alias = keyAlias; 1086 } 1087 if (keyStore.containsAlias(alias) == false) { 1088 MessageFormat form = new MessageFormat 1089 (rb.getString("Alias.alias.does.not.exist")); 1090 Object[] source = {alias}; 1091 throw new Exception(form.format(source)); 1092 } 1093 if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { 1094 MessageFormat form = new MessageFormat(rb.getString( 1095 "Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key")); 1096 Object[] source = {alias}; 1097 throw new Exception(form.format(source)); 1098 } 1099 1100 doCloneEntry(alias, dest, true); // Now everything can be cloned 1101 kssave = true; 1102 } else if (command == CHANGEALIAS) { 1103 if (alias == null) { 1104 alias = keyAlias; 1105 } 1106 doCloneEntry(alias, dest, false); 1107 // in PKCS11, clone a PrivateKeyEntry will delete the old one 1108 if (keyStore.containsAlias(alias)) { 1109 doDeleteEntry(alias); 1110 } 1111 kssave = true; 1112 } else if (command == KEYPASSWD) { 1113 keyPassNew = newPass; 1114 doChangeKeyPasswd(alias); 1115 kssave = true; 1116 } else if (command == LIST) { 1117 if (storePass == null 1118 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { 1119 printNoIntegrityWarning(); 1120 } 1121 1122 if (alias != null) { 1123 doPrintEntry(rb.getString("the.certificate"), alias, out); 1124 } else { 1125 doPrintEntries(out); 1126 } 1127 } else if (command == PRINTCERT) { 1128 doPrintCert(out); 1129 } else if (command == SELFCERT) { 1130 doSelfCert(alias, dname, sigAlgName); 1131 kssave = true; 1132 } else if (command == STOREPASSWD) { 1133 storePassNew = newPass; 1134 if (storePassNew == null) { 1135 storePassNew = getNewPasswd("keystore password", storePass); 1136 } 1137 kssave = true; 1138 } else if (command == GENCERT) { 1139 if (alias == null) { 1140 alias = keyAlias; 1141 } 1142 InputStream inStream = System.in; 1143 if (infilename != null) { 1144 inStream = new FileInputStream(infilename); 1145 } 1146 PrintStream ps = null; 1147 if (outfilename != null) { 1148 ps = new PrintStream(new FileOutputStream(outfilename)); 1149 out = ps; 1150 } 1151 try { 1152 doGenCert(alias, sigAlgName, inStream, out); 1153 } finally { 1154 if (inStream != System.in) { 1155 inStream.close(); 1156 } 1157 if (ps != null) { 1158 ps.close(); 1159 } 1160 } 1161 } else if (command == GENCRL) { 1162 if (alias == null) { 1163 alias = keyAlias; 1164 } 1165 if (filename != null) { 1166 try (PrintStream ps = 1167 new PrintStream(new FileOutputStream(filename))) { 1168 doGenCRL(ps); 1169 } 1170 } else { 1171 doGenCRL(out); 1172 } 1173 } else if (command == PRINTCERTREQ) { 1174 if (filename != null) { 1175 try (InputStream inStream = new FileInputStream(filename)) { 1176 doPrintCertReq(inStream, out); 1177 } 1178 } else { 1179 doPrintCertReq(System.in, out); 1180 } 1181 } else if (command == PRINTCRL) { 1182 doPrintCRL(filename, out); 1183 } 1184 1185 // If we need to save the keystore, do so. 1186 if (kssave) { 1187 if (verbose) { 1188 MessageFormat form = new MessageFormat 1189 (rb.getString(".Storing.ksfname.")); 1190 Object[] source = {nullStream ? "keystore" : ksfname}; 1191 System.err.println(form.format(source)); 1192 } 1193 1194 if (token) { 1195 keyStore.store(null, null); 1196 } else { 1197 char[] pass = (storePassNew!=null) ? storePassNew : storePass; 1198 if (nullStream) { 1199 keyStore.store(null, pass); 1200 } else { 1201 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 1202 keyStore.store(bout, pass); 1203 try (FileOutputStream fout = new FileOutputStream(ksfname)) { 1204 fout.write(bout.toByteArray()); 1205 } 1206 } 1207 } 1208 } 1209 1210 if (isKeyStoreRelated(command) 1211 && !token && !nullStream && ksfname != null) { 1212 1213 // JKS storetype warning on the final result keystore 1214 File f = new File(ksfname); 1215 if (f.exists()) { 1216 // Read the first 4 bytes to determine 1217 // if we're dealing with JKS/JCEKS type store 1218 String realType = keyStoreType(f); 1219 if (realType.equalsIgnoreCase("JKS") 1220 || realType.equalsIgnoreCase("JCEKS")) { 1221 boolean allCerts = true; 1222 for (String a : Collections.list(keyStore.aliases())) { 1223 if (!keyStore.entryInstanceOf( 1224 a, TrustedCertificateEntry.class)) { 1225 allCerts = false; 1226 break; 1227 } 1228 } 1229 // Don't warn for "cacerts" style keystore. 1230 if (!allCerts) { 1231 weakWarnings.add(String.format( 1232 rb.getString("jks.storetype.warning"), 1233 realType, ksfname)); 1234 } 1235 } 1236 if (inplaceImport) { 1237 String realSourceStoreType = 1238 keyStoreType(new File(inplaceBackupName)); 1239 String format = 1240 realType.equalsIgnoreCase(realSourceStoreType) ? 1241 rb.getString("backup.keystore.warning") : 1242 rb.getString("migrate.keystore.warning"); 1243 weakWarnings.add( 1244 String.format(format, 1245 srcksfname, 1246 realSourceStoreType, 1247 inplaceBackupName, 1248 realType)); 1249 } 1250 } 1251 } 1252 } 1253 1254 private String keyStoreType(File f) throws IOException { 1255 int MAGIC = 0xfeedfeed; 1256 int JCEKS_MAGIC = 0xcececece; 1257 try (DataInputStream dis = new DataInputStream( 1258 new FileInputStream(f))) { 1259 int xMagic = dis.readInt(); 1260 if (xMagic == MAGIC) { 1261 return "JKS"; 1262 } else if (xMagic == JCEKS_MAGIC) { 1263 return "JCEKS"; 1264 } else { 1265 return "Non JKS/JCEKS"; 1266 } 1267 } 1268 } 1269 1270 /** 1271 * Generate a certificate: Read PKCS10 request from in, and print 1272 * certificate to out. Use alias as CA, sigAlgName as the signature 1273 * type. 1274 */ 1275 private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out) 1276 throws Exception { 1277 1278 1279 if (keyStore.containsAlias(alias) == false) { 1280 MessageFormat form = new MessageFormat 1281 (rb.getString("Alias.alias.does.not.exist")); 1282 Object[] source = {alias}; 1283 throw new Exception(form.format(source)); 1284 } 1285 Certificate signerCert = keyStore.getCertificate(alias); 1286 byte[] encoded = signerCert.getEncoded(); 1287 X509CertImpl signerCertImpl = new X509CertImpl(encoded); 1288 X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get( 1289 X509CertImpl.NAME + "." + X509CertImpl.INFO); 1290 X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." + 1291 X509CertInfo.DN_NAME); 1292 1293 Date firstDate = getStartDate(startDate); 1294 Date lastDate = new Date(); 1295 lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); 1296 CertificateValidity interval = new CertificateValidity(firstDate, 1297 lastDate); 1298 1299 PrivateKey privateKey = 1300 (PrivateKey)recoverKey(alias, storePass, keyPass).fst; 1301 if (sigAlgName == null) { 1302 sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); 1303 } 1304 Signature signature = Signature.getInstance(sigAlgName); 1305 AlgorithmParameterSpec params = AlgorithmId 1306 .getDefaultAlgorithmParameterSpec(sigAlgName, privateKey); 1307 1308 SignatureUtil.initSignWithParam(signature, privateKey, params, null); 1309 1310 X509CertInfo info = new X509CertInfo(); 1311 AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params); 1312 info.set(X509CertInfo.VALIDITY, interval); 1313 info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( 1314 new java.util.Random().nextInt() & 0x7fffffff)); 1315 info.set(X509CertInfo.VERSION, 1316 new CertificateVersion(CertificateVersion.V3)); 1317 info.set(X509CertInfo.ALGORITHM_ID, 1318 new CertificateAlgorithmId(algID)); 1319 info.set(X509CertInfo.ISSUER, issuer); 1320 1321 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 1322 boolean canRead = false; 1323 StringBuffer sb = new StringBuffer(); 1324 while (true) { 1325 String s = reader.readLine(); 1326 if (s == null) break; 1327 // OpenSSL does not use NEW 1328 //if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) { 1329 if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) { 1330 canRead = true; 1331 //} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) { 1332 } else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) { 1333 break; 1334 } else if (canRead) { 1335 sb.append(s); 1336 } 1337 } 1338 byte[] rawReq = Pem.decode(new String(sb)); 1339 PKCS10 req = new PKCS10(rawReq); 1340 1341 checkWeak(rb.getString("the.certificate.request"), req); 1342 1343 info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo())); 1344 info.set(X509CertInfo.SUBJECT, 1345 dname==null?req.getSubjectName():new X500Name(dname)); 1346 CertificateExtensions reqex = null; 1347 Iterator<PKCS10Attribute> attrs = req.getAttributes().getAttributes().iterator(); 1348 while (attrs.hasNext()) { 1349 PKCS10Attribute attr = attrs.next(); 1350 if (attr.getAttributeId().equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) { 1351 reqex = (CertificateExtensions)attr.getAttributeValue(); 1352 } 1353 } 1354 CertificateExtensions ext = createV3Extensions( 1355 reqex, 1356 null, 1357 v3ext, 1358 req.getSubjectPublicKeyInfo(), 1359 signerCert.getPublicKey()); 1360 info.set(X509CertInfo.EXTENSIONS, ext); 1361 X509CertImpl cert = new X509CertImpl(info); 1362 cert.sign(privateKey, params, sigAlgName, null); 1363 dumpCert(cert, out); 1364 for (Certificate ca: keyStore.getCertificateChain(alias)) { 1365 if (ca instanceof X509Certificate) { 1366 X509Certificate xca = (X509Certificate)ca; 1367 if (!KeyStoreUtil.isSelfSigned(xca)) { 1368 dumpCert(xca, out); 1369 } 1370 } 1371 } 1372 1373 checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias)); 1374 checkWeak(rb.getString("the.generated.certificate"), cert); 1375 } 1376 1377 private void doGenCRL(PrintStream out) 1378 throws Exception { 1379 if (ids == null) { 1380 throw new Exception("Must provide -id when -gencrl"); 1381 } 1382 Certificate signerCert = keyStore.getCertificate(alias); 1383 byte[] encoded = signerCert.getEncoded(); 1384 X509CertImpl signerCertImpl = new X509CertImpl(encoded); 1385 X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get( 1386 X509CertImpl.NAME + "." + X509CertImpl.INFO); 1387 X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." + 1388 X509CertInfo.DN_NAME); 1389 1390 Date firstDate = getStartDate(startDate); 1391 Date lastDate = (Date) firstDate.clone(); 1392 lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60); 1393 CertificateValidity interval = new CertificateValidity(firstDate, 1394 lastDate); 1395 1396 1397 PrivateKey privateKey = 1398 (PrivateKey)recoverKey(alias, storePass, keyPass).fst; 1399 if (sigAlgName == null) { 1400 sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); 1401 } 1402 1403 X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()]; 1404 for (int i=0; i<ids.size(); i++) { 1405 String id = ids.get(i); 1406 int d = id.indexOf(':'); 1407 if (d >= 0) { 1408 CRLExtensions ext = new CRLExtensions(); 1409 ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1)))); 1410 badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)), 1411 firstDate, ext); 1412 } else { 1413 badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate); 1414 } 1415 } 1416 X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts); 1417 crl.sign(privateKey, sigAlgName); 1418 if (rfc) { 1419 out.println("-----BEGIN X509 CRL-----"); 1420 out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(crl.getEncodedInternal())); 1421 out.println("-----END X509 CRL-----"); 1422 } else { 1423 out.write(crl.getEncodedInternal()); 1424 } 1425 checkWeak(rb.getString("the.generated.crl"), crl, privateKey); 1426 } 1427 1428 /** 1429 * Creates a PKCS#10 cert signing request, corresponding to the 1430 * keys (and name) associated with a given alias. 1431 */ 1432 private void doCertReq(String alias, String sigAlgName, PrintStream out) 1433 throws Exception 1434 { 1435 if (alias == null) { 1436 alias = keyAlias; 1437 } 1438 1439 Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass); 1440 PrivateKey privKey = (PrivateKey)objs.fst; 1441 if (keyPass == null) { 1442 keyPass = objs.snd; 1443 } 1444 1445 Certificate cert = keyStore.getCertificate(alias); 1446 if (cert == null) { 1447 MessageFormat form = new MessageFormat 1448 (rb.getString("alias.has.no.public.key.certificate.")); 1449 Object[] source = {alias}; 1450 throw new Exception(form.format(source)); 1451 } 1452 PKCS10 request = new PKCS10(cert.getPublicKey()); 1453 CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null); 1454 // Attribute name is not significant 1455 request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS, 1456 new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext)); 1457 1458 // Construct a Signature object, so that we can sign the request 1459 if (sigAlgName == null) { 1460 sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm()); 1461 } 1462 1463 Signature signature = Signature.getInstance(sigAlgName); 1464 AlgorithmParameterSpec params = AlgorithmId 1465 .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); 1466 SignatureUtil.initSignWithParam(signature, privKey, params, null); 1467 1468 X500Name subject = dname == null? 1469 new X500Name(((X509Certificate)cert).getSubjectDN().toString()): 1470 new X500Name(dname); 1471 1472 // Sign the request and base-64 encode it 1473 request.encodeAndSign(subject, signature); 1474 request.print(out); 1475 1476 checkWeak(rb.getString("the.generated.certificate.request"), request); 1477 } 1478 1479 /** 1480 * Deletes an entry from the keystore. 1481 */ 1482 private void doDeleteEntry(String alias) throws Exception { 1483 if (keyStore.containsAlias(alias) == false) { 1484 MessageFormat form = new MessageFormat 1485 (rb.getString("Alias.alias.does.not.exist")); 1486 Object[] source = {alias}; 1487 throw new Exception(form.format(source)); 1488 } 1489 keyStore.deleteEntry(alias); 1490 } 1491 1492 /** 1493 * Exports a certificate from the keystore. 1494 */ 1495 private void doExportCert(String alias, PrintStream out) 1496 throws Exception 1497 { 1498 if (storePass == null 1499 && !KeyStoreUtil.isWindowsKeyStore(storetype)) { 1500 printNoIntegrityWarning(); 1501 } 1502 if (alias == null) { 1503 alias = keyAlias; 1504 } 1505 if (keyStore.containsAlias(alias) == false) { 1506 MessageFormat form = new MessageFormat 1507 (rb.getString("Alias.alias.does.not.exist")); 1508 Object[] source = {alias}; 1509 throw new Exception(form.format(source)); 1510 } 1511 1512 X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias); 1513 if (cert == null) { 1514 MessageFormat form = new MessageFormat 1515 (rb.getString("Alias.alias.has.no.certificate")); 1516 Object[] source = {alias}; 1517 throw new Exception(form.format(source)); 1518 } 1519 dumpCert(cert, out); 1520 checkWeak(rb.getString("the.certificate"), cert); 1521 } 1522 1523 /** 1524 * Prompt the user for a keypass when generating a key entry. 1525 * @param alias the entry we will set password for 1526 * @param orig the original entry of doing a dup, null if generate new 1527 * @param origPass the password to copy from if user press ENTER 1528 */ 1529 private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{ 1530 if (P12KEYSTORE.equalsIgnoreCase(storetype)) { 1531 return origPass; 1532 } else if (!token && !protectedPath) { 1533 // Prompt for key password 1534 int count; 1535 for (count = 0; count < 3; count++) { 1536 MessageFormat form = new MessageFormat(rb.getString 1537 ("Enter.key.password.for.alias.")); 1538 Object[] source = {alias}; 1539 System.err.println(form.format(source)); 1540 if (orig == null) { 1541 System.err.print(rb.getString 1542 (".RETURN.if.same.as.keystore.password.")); 1543 } else { 1544 form = new MessageFormat(rb.getString 1545 (".RETURN.if.same.as.for.otherAlias.")); 1546 Object[] src = {orig}; 1547 System.err.print(form.format(src)); 1548 } 1549 System.err.flush(); 1550 char[] entered = Password.readPassword(System.in); 1551 passwords.add(entered); 1552 if (entered == null) { 1553 return origPass; 1554 } else if (entered.length >= 6) { 1555 System.err.print(rb.getString("Re.enter.new.password.")); 1556 char[] passAgain = Password.readPassword(System.in); 1557 passwords.add(passAgain); 1558 if (!Arrays.equals(entered, passAgain)) { 1559 System.err.println 1560 (rb.getString("They.don.t.match.Try.again")); 1561 continue; 1562 } 1563 return entered; 1564 } else { 1565 System.err.println(rb.getString 1566 ("Key.password.is.too.short.must.be.at.least.6.characters")); 1567 } 1568 } 1569 if (count == 3) { 1570 if (command == KEYCLONE) { 1571 throw new Exception(rb.getString 1572 ("Too.many.failures.Key.entry.not.cloned")); 1573 } else { 1574 throw new Exception(rb.getString 1575 ("Too.many.failures.key.not.added.to.keystore")); 1576 } 1577 } 1578 } 1579 return null; // PKCS11, MSCAPI, or -protected 1580 } 1581 1582 /* 1583 * Prompt the user for the password credential to be stored. 1584 */ 1585 private char[] promptForCredential() throws Exception { 1586 // Handle password supplied via stdin 1587 if (System.console() == null) { 1588 char[] importPass = Password.readPassword(System.in); 1589 passwords.add(importPass); 1590 return importPass; 1591 } 1592 1593 int count; 1594 for (count = 0; count < 3; count++) { 1595 System.err.print( 1596 rb.getString("Enter.the.password.to.be.stored.")); 1597 System.err.flush(); 1598 char[] entered = Password.readPassword(System.in); 1599 passwords.add(entered); 1600 System.err.print(rb.getString("Re.enter.password.")); 1601 char[] passAgain = Password.readPassword(System.in); 1602 passwords.add(passAgain); 1603 if (!Arrays.equals(entered, passAgain)) { 1604 System.err.println(rb.getString("They.don.t.match.Try.again")); 1605 continue; 1606 } 1607 return entered; 1608 } 1609 1610 if (count == 3) { 1611 throw new Exception(rb.getString 1612 ("Too.many.failures.key.not.added.to.keystore")); 1613 } 1614 1615 return null; 1616 } 1617 1618 /** 1619 * Creates a new secret key. 1620 */ 1621 private void doGenSecretKey(String alias, String keyAlgName, 1622 int keysize) 1623 throws Exception 1624 { 1625 if (alias == null) { 1626 alias = keyAlias; 1627 } 1628 if (keyStore.containsAlias(alias)) { 1629 MessageFormat form = new MessageFormat(rb.getString 1630 ("Secret.key.not.generated.alias.alias.already.exists")); 1631 Object[] source = {alias}; 1632 throw new Exception(form.format(source)); 1633 } 1634 1635 // Use the keystore's default PBE algorithm for entry protection 1636 boolean useDefaultPBEAlgorithm = true; 1637 SecretKey secKey = null; 1638 1639 if (keyAlgName.toUpperCase(Locale.ENGLISH).startsWith("PBE")) { 1640 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); 1641 1642 // User is prompted for PBE credential 1643 secKey = 1644 factory.generateSecret(new PBEKeySpec(promptForCredential())); 1645 1646 // Check whether a specific PBE algorithm was specified 1647 if (!"PBE".equalsIgnoreCase(keyAlgName)) { 1648 useDefaultPBEAlgorithm = false; 1649 } 1650 1651 if (verbose) { 1652 MessageFormat form = new MessageFormat(rb.getString( 1653 "Generated.keyAlgName.secret.key")); 1654 Object[] source = 1655 {useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()}; 1656 System.err.println(form.format(source)); 1657 } 1658 } else { 1659 KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName); 1660 if (keysize == -1) { 1661 if ("DES".equalsIgnoreCase(keyAlgName)) { 1662 keysize = 56; 1663 } else if ("DESede".equalsIgnoreCase(keyAlgName)) { 1664 keysize = 168; 1665 } else { 1666 throw new Exception(rb.getString 1667 ("Please.provide.keysize.for.secret.key.generation")); 1668 } 1669 } 1670 keygen.init(keysize); 1671 secKey = keygen.generateKey(); 1672 1673 if (verbose) { 1674 MessageFormat form = new MessageFormat(rb.getString 1675 ("Generated.keysize.bit.keyAlgName.secret.key")); 1676 Object[] source = {new Integer(keysize), 1677 secKey.getAlgorithm()}; 1678 System.err.println(form.format(source)); 1679 } 1680 } 1681 1682 if (keyPass == null) { 1683 keyPass = promptForKeyPass(alias, null, storePass); 1684 } 1685 1686 if (useDefaultPBEAlgorithm) { 1687 keyStore.setKeyEntry(alias, secKey, keyPass, null); 1688 } else { 1689 keyStore.setEntry(alias, new KeyStore.SecretKeyEntry(secKey), 1690 new KeyStore.PasswordProtection(keyPass, keyAlgName, null)); 1691 } 1692 } 1693 1694 /** 1695 * If no signature algorithm was specified at the command line, 1696 * we choose one that is compatible with the selected private key 1697 */ 1698 private static String getCompatibleSigAlgName(String keyAlgName) 1699 throws Exception { 1700 if ("DSA".equalsIgnoreCase(keyAlgName)) { 1701 return "SHA256WithDSA"; 1702 } else if ("RSA".equalsIgnoreCase(keyAlgName)) { 1703 return "SHA256WithRSA"; 1704 } else if ("EC".equalsIgnoreCase(keyAlgName)) { 1705 return "SHA256withECDSA"; 1706 } else { 1707 throw new Exception(rb.getString 1708 ("Cannot.derive.signature.algorithm")); 1709 } 1710 } 1711 /** 1712 * Creates a new key pair and self-signed certificate. 1713 */ 1714 private void doGenKeyPair(String alias, String dname, String keyAlgName, 1715 int keysize, String sigAlgName) 1716 throws Exception 1717 { 1718 if (keysize == -1) { 1719 if ("EC".equalsIgnoreCase(keyAlgName)) { 1720 keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE; 1721 } else if ("RSA".equalsIgnoreCase(keyAlgName)) { 1722 keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE; 1723 } else if ("RSASSA-PSS".equalsIgnoreCase(keyAlgName)) { 1724 keysize = SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE; 1725 } else if ("DSA".equalsIgnoreCase(keyAlgName)) { 1726 keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE; 1727 } 1728 } 1729 1730 if (alias == null) { 1731 alias = keyAlias; 1732 } 1733 1734 if (keyStore.containsAlias(alias)) { 1735 MessageFormat form = new MessageFormat(rb.getString 1736 ("Key.pair.not.generated.alias.alias.already.exists")); 1737 Object[] source = {alias}; 1738 throw new Exception(form.format(source)); 1739 } 1740 1741 if (sigAlgName == null) { 1742 sigAlgName = getCompatibleSigAlgName(keyAlgName); 1743 } 1744 CertAndKeyGen keypair = 1745 new CertAndKeyGen(keyAlgName, sigAlgName, providerName); 1746 1747 1748 // If DN is provided, parse it. Otherwise, prompt the user for it. 1749 X500Name x500Name; 1750 if (dname == null) { 1751 x500Name = getX500Name(); 1752 } else { 1753 x500Name = new X500Name(dname); 1754 } 1755 1756 keypair.generate(keysize); 1757 PrivateKey privKey = keypair.getPrivateKey(); 1758 1759 CertificateExtensions ext = createV3Extensions( 1760 null, 1761 null, 1762 v3ext, 1763 keypair.getPublicKeyAnyway(), 1764 null); 1765 1766 X509Certificate[] chain = new X509Certificate[1]; 1767 chain[0] = keypair.getSelfCertificate( 1768 x500Name, getStartDate(startDate), validity*24L*60L*60L, ext); 1769 1770 if (verbose) { 1771 MessageFormat form = new MessageFormat(rb.getString 1772 ("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for")); 1773 Object[] source = {new Integer(keysize), 1774 privKey.getAlgorithm(), 1775 chain[0].getSigAlgName(), 1776 new Long(validity), 1777 x500Name}; 1778 System.err.println(form.format(source)); 1779 } 1780 1781 if (keyPass == null) { 1782 keyPass = promptForKeyPass(alias, null, storePass); 1783 } 1784 checkWeak(rb.getString("the.generated.certificate"), chain[0]); 1785 keyStore.setKeyEntry(alias, privKey, keyPass, chain); 1786 } 1787 1788 /** 1789 * Clones an entry 1790 * @param orig original alias 1791 * @param dest destination alias 1792 * @changePassword if the password can be changed 1793 */ 1794 private void doCloneEntry(String orig, String dest, boolean changePassword) 1795 throws Exception 1796 { 1797 if (orig == null) { 1798 orig = keyAlias; 1799 } 1800 1801 if (keyStore.containsAlias(dest)) { 1802 MessageFormat form = new MessageFormat 1803 (rb.getString("Destination.alias.dest.already.exists")); 1804 Object[] source = {dest}; 1805 throw new Exception(form.format(source)); 1806 } 1807 1808 Pair<Entry,char[]> objs = recoverEntry(keyStore, orig, storePass, keyPass); 1809 Entry entry = objs.fst; 1810 keyPass = objs.snd; 1811 1812 PasswordProtection pp = null; 1813 1814 if (keyPass != null) { // protected 1815 if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) { 1816 keyPassNew = keyPass; 1817 } else { 1818 if (keyPassNew == null) { 1819 keyPassNew = promptForKeyPass(dest, orig, keyPass); 1820 } 1821 } 1822 pp = new PasswordProtection(keyPassNew); 1823 } 1824 keyStore.setEntry(dest, entry, pp); 1825 } 1826 1827 /** 1828 * Changes a key password. 1829 */ 1830 private void doChangeKeyPasswd(String alias) throws Exception 1831 { 1832 1833 if (alias == null) { 1834 alias = keyAlias; 1835 } 1836 Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass); 1837 Key privKey = objs.fst; 1838 if (keyPass == null) { 1839 keyPass = objs.snd; 1840 } 1841 1842 if (keyPassNew == null) { 1843 MessageFormat form = new MessageFormat 1844 (rb.getString("key.password.for.alias.")); 1845 Object[] source = {alias}; 1846 keyPassNew = getNewPasswd(form.format(source), keyPass); 1847 } 1848 keyStore.setKeyEntry(alias, privKey, keyPassNew, 1849 keyStore.getCertificateChain(alias)); 1850 } 1851 1852 /** 1853 * Imports a JDK 1.1-style identity database. We can only store one 1854 * certificate per identity, because we use the identity's name as the 1855 * alias (which references a keystore entry), and aliases must be unique. 1856 */ 1857 private void doImportIdentityDatabase(InputStream in) 1858 throws Exception 1859 { 1860 System.err.println(rb.getString 1861 ("No.entries.from.identity.database.added")); 1862 } 1863 1864 /** 1865 * Prints a single keystore entry. 1866 */ 1867 private void doPrintEntry(String label, String alias, PrintStream out) 1868 throws Exception 1869 { 1870 if (keyStore.containsAlias(alias) == false) { 1871 MessageFormat form = new MessageFormat 1872 (rb.getString("Alias.alias.does.not.exist")); 1873 Object[] source = {alias}; 1874 throw new Exception(form.format(source)); 1875 } 1876 1877 if (verbose || rfc || debug) { 1878 MessageFormat form = new MessageFormat 1879 (rb.getString("Alias.name.alias")); 1880 Object[] source = {alias}; 1881 out.println(form.format(source)); 1882 1883 if (!token) { 1884 form = new MessageFormat(rb.getString 1885 ("Creation.date.keyStore.getCreationDate.alias.")); 1886 Object[] src = {keyStore.getCreationDate(alias)}; 1887 out.println(form.format(src)); 1888 } 1889 } else { 1890 if (!token) { 1891 MessageFormat form = new MessageFormat 1892 (rb.getString("alias.keyStore.getCreationDate.alias.")); 1893 Object[] source = {alias, keyStore.getCreationDate(alias)}; 1894 out.print(form.format(source)); 1895 } else { 1896 MessageFormat form = new MessageFormat 1897 (rb.getString("alias.")); 1898 Object[] source = {alias}; 1899 out.print(form.format(source)); 1900 } 1901 } 1902 1903 if (keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { 1904 if (verbose || rfc || debug) { 1905 Object[] source = {"SecretKeyEntry"}; 1906 out.println(new MessageFormat( 1907 rb.getString("Entry.type.type.")).format(source)); 1908 } else { 1909 out.println("SecretKeyEntry, "); 1910 } 1911 } else if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { 1912 if (verbose || rfc || debug) { 1913 Object[] source = {"PrivateKeyEntry"}; 1914 out.println(new MessageFormat( 1915 rb.getString("Entry.type.type.")).format(source)); 1916 } else { 1917 out.println("PrivateKeyEntry, "); 1918 } 1919 1920 // Get the chain 1921 Certificate[] chain = keyStore.getCertificateChain(alias); 1922 if (chain != null) { 1923 if (verbose || rfc || debug) { 1924 out.println(rb.getString 1925 ("Certificate.chain.length.") + chain.length); 1926 for (int i = 0; i < chain.length; i ++) { 1927 MessageFormat form = new MessageFormat 1928 (rb.getString("Certificate.i.1.")); 1929 Object[] source = {new Integer((i + 1))}; 1930 out.println(form.format(source)); 1931 if (verbose && (chain[i] instanceof X509Certificate)) { 1932 printX509Cert((X509Certificate)(chain[i]), out); 1933 } else if (debug) { 1934 out.println(chain[i].toString()); 1935 } else { 1936 dumpCert(chain[i], out); 1937 } 1938 checkWeak(label, chain[i]); 1939 } 1940 } else { 1941 // Print the digest of the user cert only 1942 out.println 1943 (rb.getString("Certificate.fingerprint.SHA.256.") + 1944 getCertFingerPrint("SHA-256", chain[0])); 1945 checkWeak(label, chain[0]); 1946 } 1947 } 1948 } else if (keyStore.entryInstanceOf(alias, 1949 KeyStore.TrustedCertificateEntry.class)) { 1950 // We have a trusted certificate entry 1951 Certificate cert = keyStore.getCertificate(alias); 1952 Object[] source = {"trustedCertEntry"}; 1953 String mf = new MessageFormat( 1954 rb.getString("Entry.type.type.")).format(source) + "\n"; 1955 if (verbose && (cert instanceof X509Certificate)) { 1956 out.println(mf); 1957 printX509Cert((X509Certificate)cert, out); 1958 } else if (rfc) { 1959 out.println(mf); 1960 dumpCert(cert, out); 1961 } else if (debug) { 1962 out.println(cert.toString()); 1963 } else { 1964 out.println("trustedCertEntry, "); 1965 out.println(rb.getString("Certificate.fingerprint.SHA.256.") 1966 + getCertFingerPrint("SHA-256", cert)); 1967 } 1968 checkWeak(label, cert); 1969 } else { 1970 out.println(rb.getString("Unknown.Entry.Type")); 1971 } 1972 } 1973 1974 boolean inplaceImportCheck() throws Exception { 1975 if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) || 1976 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { 1977 return false; 1978 } 1979 1980 if (srcksfname != null) { 1981 File srcksfile = new File(srcksfname); 1982 if (srcksfile.exists() && srcksfile.length() == 0) { 1983 throw new Exception(rb.getString 1984 ("Source.keystore.file.exists.but.is.empty.") + 1985 srcksfname); 1986 } 1987 if (srcksfile.getCanonicalFile() 1988 .equals(new File(ksfname).getCanonicalFile())) { 1989 return true; 1990 } else { 1991 // Informational, especially if destkeystore is not 1992 // provided, which default to ~/.keystore. 1993 System.err.println(String.format(rb.getString( 1994 "importing.keystore.status"), srcksfname, ksfname)); 1995 return false; 1996 } 1997 } else { 1998 throw new Exception(rb.getString 1999 ("Please.specify.srckeystore")); 2000 } 2001 } 2002 2003 /** 2004 * Load the srckeystore from a stream, used in -importkeystore 2005 * @returns the src KeyStore 2006 */ 2007 KeyStore loadSourceKeyStore() throws Exception { 2008 2009 InputStream is = null; 2010 File srcksfile = null; 2011 2012 if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) || 2013 KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { 2014 if (!NONE.equals(srcksfname)) { 2015 System.err.println(MessageFormat.format(rb.getString 2016 (".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype)); 2017 System.err.println(); 2018 tinyHelp(); 2019 } 2020 } else { 2021 srcksfile = new File(srcksfname); 2022 is = new FileInputStream(srcksfile); 2023 } 2024 2025 KeyStore store; 2026 try { 2027 if (srcstoretype == null) { 2028 srcstoretype = KeyStore.getDefaultType(); 2029 } 2030 if (srcProviderName == null) { 2031 store = KeyStore.getInstance(srcstoretype); 2032 } else { 2033 store = KeyStore.getInstance(srcstoretype, srcProviderName); 2034 } 2035 2036 if (srcstorePass == null 2037 && !srcprotectedPath 2038 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { 2039 System.err.print(rb.getString("Enter.source.keystore.password.")); 2040 System.err.flush(); 2041 srcstorePass = Password.readPassword(System.in); 2042 passwords.add(srcstorePass); 2043 } 2044 2045 // always let keypass be storepass when using pkcs12 2046 if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) { 2047 if (srckeyPass != null && srcstorePass != null && 2048 !Arrays.equals(srcstorePass, srckeyPass)) { 2049 MessageFormat form = new MessageFormat(rb.getString( 2050 "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.")); 2051 Object[] source = {"-srckeypass"}; 2052 System.err.println(form.format(source)); 2053 srckeyPass = srcstorePass; 2054 } 2055 } 2056 2057 store.load(is, srcstorePass); // "is" already null in PKCS11 2058 } finally { 2059 if (is != null) { 2060 is.close(); 2061 } 2062 } 2063 2064 if (srcstorePass == null 2065 && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { 2066 // anti refactoring, copied from printNoIntegrityWarning(), 2067 // but change 2 lines 2068 System.err.println(); 2069 System.err.println(rb.getString 2070 (".WARNING.WARNING.WARNING.")); 2071 System.err.println(rb.getString 2072 (".The.integrity.of.the.information.stored.in.the.srckeystore.")); 2073 System.err.println(rb.getString 2074 (".WARNING.WARNING.WARNING.")); 2075 System.err.println(); 2076 } 2077 2078 return store; 2079 } 2080 2081 /** 2082 * import all keys and certs from importkeystore. 2083 * keep alias unchanged if no name conflict, otherwise, prompt. 2084 * keep keypass unchanged for keys 2085 */ 2086 private void doImportKeyStore(KeyStore srcKS) throws Exception { 2087 2088 if (alias != null) { 2089 doImportKeyStoreSingle(srcKS, alias); 2090 } else { 2091 if (dest != null || srckeyPass != null) { 2092 throw new Exception(rb.getString( 2093 "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified")); 2094 } 2095 doImportKeyStoreAll(srcKS); 2096 } 2097 2098 if (inplaceImport) { 2099 // Backup to file.old or file.old2... 2100 // The keystore is not rewritten yet now. 2101 for (int n = 1; /* forever */; n++) { 2102 inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n); 2103 File bkFile = new File(inplaceBackupName); 2104 if (!bkFile.exists()) { 2105 Files.copy(Paths.get(srcksfname), bkFile.toPath()); 2106 break; 2107 } 2108 } 2109 2110 } 2111 2112 /* 2113 * Information display rule of -importkeystore 2114 * 1. inside single, shows failure 2115 * 2. inside all, shows sucess 2116 * 3. inside all where there is a failure, prompt for continue 2117 * 4. at the final of all, shows summary 2118 */ 2119 } 2120 2121 /** 2122 * Import a single entry named alias from srckeystore 2123 * @returns 1 if the import action succeed 2124 * 0 if user choose to ignore an alias-dumplicated entry 2125 * 2 if setEntry throws Exception 2126 */ 2127 private int doImportKeyStoreSingle(KeyStore srckeystore, String alias) 2128 throws Exception { 2129 2130 String newAlias = (dest==null) ? alias : dest; 2131 2132 if (keyStore.containsAlias(newAlias)) { 2133 Object[] source = {alias}; 2134 if (noprompt) { 2135 System.err.println(new MessageFormat(rb.getString( 2136 "Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source)); 2137 } else { 2138 String reply = getYesNoReply(new MessageFormat(rb.getString( 2139 "Existing.entry.alias.alias.exists.overwrite.no.")).format(source)); 2140 if ("NO".equals(reply)) { 2141 newAlias = inputStringFromStdin(rb.getString 2142 ("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.")); 2143 if ("".equals(newAlias)) { 2144 System.err.println(new MessageFormat(rb.getString( 2145 "Entry.for.alias.alias.not.imported.")).format( 2146 source)); 2147 return 0; 2148 } 2149 } 2150 } 2151 } 2152 2153 Pair<Entry,char[]> objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass); 2154 Entry entry = objs.fst; 2155 2156 PasswordProtection pp = null; 2157 2158 // According to keytool.html, "The destination entry will be protected 2159 // using destkeypass. If destkeypass is not provided, the destination 2160 // entry will be protected with the source entry password." 2161 // so always try to protect with destKeyPass. 2162 char[] newPass = null; 2163 if (destKeyPass != null) { 2164 newPass = destKeyPass; 2165 pp = new PasswordProtection(destKeyPass); 2166 } else if (objs.snd != null) { 2167 newPass = objs.snd; 2168 pp = new PasswordProtection(objs.snd); 2169 } 2170 2171 try { 2172 Certificate c = srckeystore.getCertificate(alias); 2173 if (c != null) { 2174 checkWeak("<" + newAlias + ">", c); 2175 } 2176 keyStore.setEntry(newAlias, entry, pp); 2177 // Place the check so that only successful imports are blocked. 2178 // For example, we don't block a failed SecretEntry import. 2179 if (P12KEYSTORE.equalsIgnoreCase(storetype)) { 2180 if (newPass != null && !Arrays.equals(newPass, storePass)) { 2181 throw new Exception(rb.getString( 2182 "The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.")); 2183 } 2184 } 2185 return 1; 2186 } catch (KeyStoreException kse) { 2187 Object[] source2 = {alias, kse.toString()}; 2188 MessageFormat form = new MessageFormat(rb.getString( 2189 "Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.")); 2190 System.err.println(form.format(source2)); 2191 return 2; 2192 } 2193 } 2194 2195 private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception { 2196 2197 int ok = 0; 2198 int count = srckeystore.size(); 2199 for (Enumeration<String> e = srckeystore.aliases(); 2200 e.hasMoreElements(); ) { 2201 String alias = e.nextElement(); 2202 int result = doImportKeyStoreSingle(srckeystore, alias); 2203 if (result == 1) { 2204 ok++; 2205 Object[] source = {alias}; 2206 MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported.")); 2207 System.err.println(form.format(source)); 2208 } else if (result == 2) { 2209 if (!noprompt) { 2210 String reply = getYesNoReply("Do you want to quit the import process? [no]: "); 2211 if ("YES".equals(reply)) { 2212 break; 2213 } 2214 } 2215 } 2216 } 2217 Object[] source = {ok, count-ok}; 2218 MessageFormat form = new MessageFormat(rb.getString( 2219 "Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled")); 2220 System.err.println(form.format(source)); 2221 } 2222 2223 /** 2224 * Prints all keystore entries. 2225 */ 2226 private void doPrintEntries(PrintStream out) 2227 throws Exception 2228 { 2229 // Adjust displayed keystore type if needed. 2230 String keystoreTypeToPrint = keyStore.getType(); 2231 if ("JKS".equalsIgnoreCase(keystoreTypeToPrint)) { 2232 if (ksfile != null && ksfile.exists()) { 2233 String realType = keyStoreType(ksfile); 2234 // If the magic number does not conform to JKS 2235 // then it must be PKCS12 2236 if (!"JKS".equalsIgnoreCase(realType)) { 2237 keystoreTypeToPrint = P12KEYSTORE; 2238 } 2239 } 2240 } 2241 out.println(rb.getString("Keystore.type.") + keystoreTypeToPrint); 2242 out.println(rb.getString("Keystore.provider.") + 2243 keyStore.getProvider().getName()); 2244 out.println(); 2245 2246 MessageFormat form; 2247 form = (keyStore.size() == 1) ? 2248 new MessageFormat(rb.getString 2249 ("Your.keystore.contains.keyStore.size.entry")) : 2250 new MessageFormat(rb.getString 2251 ("Your.keystore.contains.keyStore.size.entries")); 2252 Object[] source = {new Integer(keyStore.size())}; 2253 out.println(form.format(source)); 2254 out.println(); 2255 2256 List<String> aliases = Collections.list(keyStore.aliases()); 2257 aliases.sort(String::compareTo); 2258 for (String alias : aliases) { 2259 doPrintEntry("<" + alias + ">", alias, out); 2260 if (verbose || rfc) { 2261 out.println(rb.getString("NEWLINE")); 2262 out.println(rb.getString 2263 ("STAR")); 2264 out.println(rb.getString 2265 ("STARNN")); 2266 } 2267 } 2268 } 2269 2270 private static <T> Iterable<T> e2i(final Enumeration<T> e) { 2271 return new Iterable<T>() { 2272 @Override 2273 public Iterator<T> iterator() { 2274 return new Iterator<T>() { 2275 @Override 2276 public boolean hasNext() { 2277 return e.hasMoreElements(); 2278 } 2279 @Override 2280 public T next() { 2281 return e.nextElement(); 2282 } 2283 public void remove() { 2284 throw new UnsupportedOperationException("Not supported yet."); 2285 } 2286 }; 2287 } 2288 }; 2289 } 2290 2291 /** 2292 * Loads CRLs from a source. This method is also called in JarSigner. 2293 * @param src the source, which means System.in if null, or a URI, 2294 * or a bare file path name 2295 */ 2296 public static Collection<? extends CRL> loadCRLs(String src) throws Exception { 2297 InputStream in = null; 2298 URI uri = null; 2299 if (src == null) { 2300 in = System.in; 2301 } else { 2302 try { 2303 uri = new URI(src); 2304 if (uri.getScheme().equals("ldap")) { 2305 // No input stream for LDAP 2306 } else { 2307 in = uri.toURL().openStream(); 2308 } 2309 } catch (Exception e) { 2310 try { 2311 in = new FileInputStream(src); 2312 } catch (Exception e2) { 2313 if (uri == null || uri.getScheme() == null) { 2314 throw e2; // More likely a bare file path 2315 } else { 2316 throw e; // More likely a protocol or network problem 2317 } 2318 } 2319 } 2320 } 2321 if (in != null) { 2322 try { 2323 // Read the full stream before feeding to X509Factory, 2324 // otherwise, keytool -gencrl | keytool -printcrl 2325 // might not work properly, since -gencrl is slow 2326 // and there's no data in the pipe at the beginning. 2327 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 2328 byte[] b = new byte[4096]; 2329 while (true) { 2330 int len = in.read(b); 2331 if (len < 0) break; 2332 bout.write(b, 0, len); 2333 } 2334 return CertificateFactory.getInstance("X509").generateCRLs( 2335 new ByteArrayInputStream(bout.toByteArray())); 2336 } finally { 2337 if (in != System.in) { 2338 in.close(); 2339 } 2340 } 2341 } else { // must be LDAP, and uri is not null 2342 // Lazily load LDAPCertStoreHelper if present 2343 CertStoreHelper helper = CertStoreHelper.getInstance("LDAP"); 2344 String path = uri.getPath(); 2345 if (path.charAt(0) == '/') path = path.substring(1); 2346 CertStore s = helper.getCertStore(uri); 2347 X509CRLSelector sel = 2348 helper.wrap(new X509CRLSelector(), null, path); 2349 return s.getCRLs(sel); 2350 } 2351 } 2352 2353 /** 2354 * Returns CRLs described in a X509Certificate's CRLDistributionPoints 2355 * Extension. Only those containing a general name of type URI are read. 2356 */ 2357 public static List<CRL> readCRLsFromCert(X509Certificate cert) 2358 throws Exception { 2359 List<CRL> crls = new ArrayList<>(); 2360 CRLDistributionPointsExtension ext = 2361 X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension(); 2362 if (ext == null) return crls; 2363 List<DistributionPoint> distPoints = 2364 ext.get(CRLDistributionPointsExtension.POINTS); 2365 for (DistributionPoint o: distPoints) { 2366 GeneralNames names = o.getFullName(); 2367 if (names != null) { 2368 for (GeneralName name: names.names()) { 2369 if (name.getType() == GeneralNameInterface.NAME_URI) { 2370 URIName uriName = (URIName)name.getName(); 2371 for (CRL crl: loadCRLs(uriName.getName())) { 2372 if (crl instanceof X509CRL) { 2373 crls.add((X509CRL)crl); 2374 } 2375 } 2376 break; // Different name should point to same CRL 2377 } 2378 } 2379 } 2380 } 2381 return crls; 2382 } 2383 2384 private static String verifyCRL(KeyStore ks, CRL crl) 2385 throws Exception { 2386 X509CRLImpl xcrl = (X509CRLImpl)crl; 2387 X500Principal issuer = xcrl.getIssuerX500Principal(); 2388 for (String s: e2i(ks.aliases())) { 2389 Certificate cert = ks.getCertificate(s); 2390 if (cert instanceof X509Certificate) { 2391 X509Certificate xcert = (X509Certificate)cert; 2392 if (xcert.getSubjectX500Principal().equals(issuer)) { 2393 try { 2394 ((X509CRLImpl)crl).verify(cert.getPublicKey()); 2395 return s; 2396 } catch (Exception e) { 2397 } 2398 } 2399 } 2400 } 2401 return null; 2402 } 2403 2404 private void doPrintCRL(String src, PrintStream out) 2405 throws Exception { 2406 for (CRL crl: loadCRLs(src)) { 2407 printCRL(crl, out); 2408 String issuer = null; 2409 Certificate signer = null; 2410 if (caks != null) { 2411 issuer = verifyCRL(caks, crl); 2412 if (issuer != null) { 2413 signer = caks.getCertificate(issuer); 2414 out.printf(rb.getString( 2415 "verified.by.s.in.s.weak"), 2416 issuer, 2417 "cacerts", 2418 withWeak(signer.getPublicKey())); 2419 out.println(); 2420 } 2421 } 2422 if (issuer == null && keyStore != null) { 2423 issuer = verifyCRL(keyStore, crl); 2424 if (issuer != null) { 2425 signer = keyStore.getCertificate(issuer); 2426 out.printf(rb.getString( 2427 "verified.by.s.in.s.weak"), 2428 issuer, 2429 "keystore", 2430 withWeak(signer.getPublicKey())); 2431 out.println(); 2432 } 2433 } 2434 if (issuer == null) { 2435 out.println(rb.getString 2436 ("STAR")); 2437 out.println(rb.getString 2438 ("warning.not.verified.make.sure.keystore.is.correct")); 2439 out.println(rb.getString 2440 ("STARNN")); 2441 } 2442 checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey()); 2443 } 2444 } 2445 2446 private void printCRL(CRL crl, PrintStream out) 2447 throws Exception { 2448 X509CRL xcrl = (X509CRL)crl; 2449 if (rfc) { 2450 out.println("-----BEGIN X509 CRL-----"); 2451 out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded())); 2452 out.println("-----END X509 CRL-----"); 2453 } else { 2454 String s; 2455 if (crl instanceof X509CRLImpl) { 2456 X509CRLImpl x509crl = (X509CRLImpl) crl; 2457 s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId())); 2458 } else { 2459 s = crl.toString(); 2460 } 2461 out.println(s); 2462 } 2463 } 2464 2465 private void doPrintCertReq(InputStream in, PrintStream out) 2466 throws Exception { 2467 2468 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 2469 StringBuffer sb = new StringBuffer(); 2470 boolean started = false; 2471 while (true) { 2472 String s = reader.readLine(); 2473 if (s == null) break; 2474 if (!started) { 2475 if (s.startsWith("-----")) { 2476 started = true; 2477 } 2478 } else { 2479 if (s.startsWith("-----")) { 2480 break; 2481 } 2482 sb.append(s); 2483 } 2484 } 2485 PKCS10 req = new PKCS10(Pem.decode(new String(sb))); 2486 2487 PublicKey pkey = req.getSubjectPublicKeyInfo(); 2488 out.printf(rb.getString("PKCS.10.with.weak"), 2489 req.getSubjectName(), 2490 pkey.getFormat(), 2491 withWeak(pkey), 2492 withWeak(req.getSigAlg())); 2493 for (PKCS10Attribute attr: req.getAttributes().getAttributes()) { 2494 ObjectIdentifier oid = attr.getAttributeId(); 2495 if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) { 2496 CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue(); 2497 if (exts != null) { 2498 printExtensions(rb.getString("Extension.Request."), exts, out); 2499 } 2500 } else { 2501 out.println("Attribute: " + attr.getAttributeId()); 2502 PKCS9Attribute pkcs9Attr = 2503 new PKCS9Attribute(attr.getAttributeId(), 2504 attr.getAttributeValue()); 2505 out.print(pkcs9Attr.getName() + ": "); 2506 Object attrVal = attr.getAttributeValue(); 2507 out.println(attrVal instanceof String[] ? 2508 Arrays.toString((String[]) attrVal) : 2509 attrVal); 2510 } 2511 } 2512 if (debug) { 2513 out.println(req); // Just to see more, say, public key length... 2514 } 2515 checkWeak(rb.getString("the.certificate.request"), req); 2516 } 2517 2518 /** 2519 * Reads a certificate (or certificate chain) and prints its contents in 2520 * a human readable format. 2521 */ 2522 private void printCertFromStream(InputStream in, PrintStream out) 2523 throws Exception 2524 { 2525 Collection<? extends Certificate> c = null; 2526 try { 2527 c = cf.generateCertificates(in); 2528 } catch (CertificateException ce) { 2529 throw new Exception(rb.getString("Failed.to.parse.input"), ce); 2530 } 2531 if (c.isEmpty()) { 2532 throw new Exception(rb.getString("Empty.input")); 2533 } 2534 Certificate[] certs = c.toArray(new Certificate[c.size()]); 2535 for (int i=0; i<certs.length; i++) { 2536 X509Certificate x509Cert = null; 2537 try { 2538 x509Cert = (X509Certificate)certs[i]; 2539 } catch (ClassCastException cce) { 2540 throw new Exception(rb.getString("Not.X.509.certificate")); 2541 } 2542 if (certs.length > 1) { 2543 MessageFormat form = new MessageFormat 2544 (rb.getString("Certificate.i.1.")); 2545 Object[] source = {new Integer(i + 1)}; 2546 out.println(form.format(source)); 2547 } 2548 if (rfc) 2549 dumpCert(x509Cert, out); 2550 else 2551 printX509Cert(x509Cert, out); 2552 if (i < (certs.length-1)) { 2553 out.println(); 2554 } 2555 checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert); 2556 } 2557 } 2558 2559 private static String oneInMany(String label, int i, int num) { 2560 if (num == 1) { 2561 return label; 2562 } else { 2563 return String.format(rb.getString("one.in.many"), label, i+1, num); 2564 } 2565 } 2566 2567 private void doPrintCert(final PrintStream out) throws Exception { 2568 if (jarfile != null) { 2569 JarFile jf = new JarFile(jarfile, true); 2570 Enumeration<JarEntry> entries = jf.entries(); 2571 Set<CodeSigner> ss = new HashSet<>(); 2572 byte[] buffer = new byte[8192]; 2573 int pos = 0; 2574 while (entries.hasMoreElements()) { 2575 JarEntry je = entries.nextElement(); 2576 try (InputStream is = jf.getInputStream(je)) { 2577 while (is.read(buffer) != -1) { 2578 // we just read. this will throw a SecurityException 2579 // if a signature/digest check fails. This also 2580 // populate the signers 2581 } 2582 } 2583 CodeSigner[] signers = je.getCodeSigners(); 2584 if (signers != null) { 2585 for (CodeSigner signer: signers) { 2586 if (!ss.contains(signer)) { 2587 ss.add(signer); 2588 out.printf(rb.getString("Signer.d."), ++pos); 2589 out.println(); 2590 out.println(); 2591 out.println(rb.getString("Signature.")); 2592 out.println(); 2593 2594 List<? extends Certificate> certs 2595 = signer.getSignerCertPath().getCertificates(); 2596 int cc = 0; 2597 for (Certificate cert: certs) { 2598 X509Certificate x = (X509Certificate)cert; 2599 if (rfc) { 2600 out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n"); 2601 dumpCert(x, out); 2602 } else { 2603 printX509Cert(x, out); 2604 } 2605 out.println(); 2606 checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x); 2607 } 2608 Timestamp ts = signer.getTimestamp(); 2609 if (ts != null) { 2610 out.println(rb.getString("Timestamp.")); 2611 out.println(); 2612 certs = ts.getSignerCertPath().getCertificates(); 2613 cc = 0; 2614 for (Certificate cert: certs) { 2615 X509Certificate x = (X509Certificate)cert; 2616 if (rfc) { 2617 out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n"); 2618 dumpCert(x, out); 2619 } else { 2620 printX509Cert(x, out); 2621 } 2622 out.println(); 2623 checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x); 2624 } 2625 } 2626 } 2627 } 2628 } 2629 } 2630 jf.close(); 2631 if (ss.isEmpty()) { 2632 out.println(rb.getString("Not.a.signed.jar.file")); 2633 } 2634 } else if (sslserver != null) { 2635 // Lazily load SSLCertStoreHelper if present 2636 CertStoreHelper helper = CertStoreHelper.getInstance("SSLServer"); 2637 CertStore cs = helper.getCertStore(new URI("https://" + sslserver)); 2638 Collection<? extends Certificate> chain; 2639 try { 2640 chain = cs.getCertificates(null); 2641 if (chain.isEmpty()) { 2642 // If the certs are not retrieved, we consider it an error 2643 // even if the URL connection is successful. 2644 throw new Exception(rb.getString( 2645 "No.certificate.from.the.SSL.server")); 2646 } 2647 } catch (CertStoreException cse) { 2648 if (cse.getCause() instanceof IOException) { 2649 throw new Exception(rb.getString( 2650 "No.certificate.from.the.SSL.server"), 2651 cse.getCause()); 2652 } else { 2653 throw cse; 2654 } 2655 } 2656 2657 int i = 0; 2658 for (Certificate cert : chain) { 2659 try { 2660 if (rfc) { 2661 dumpCert(cert, out); 2662 } else { 2663 out.println("Certificate #" + i); 2664 out.println("===================================="); 2665 printX509Cert((X509Certificate)cert, out); 2666 out.println(); 2667 } 2668 checkWeak(oneInMany(rb.getString("the.certificate"), i++, chain.size()), cert); 2669 } catch (Exception e) { 2670 if (debug) { 2671 e.printStackTrace(); 2672 } 2673 } 2674 } 2675 } else { 2676 if (filename != null) { 2677 try (FileInputStream inStream = new FileInputStream(filename)) { 2678 printCertFromStream(inStream, out); 2679 } 2680 } else { 2681 printCertFromStream(System.in, out); 2682 } 2683 } 2684 } 2685 /** 2686 * Creates a self-signed certificate, and stores it as a single-element 2687 * certificate chain. 2688 */ 2689 private void doSelfCert(String alias, String dname, String sigAlgName) 2690 throws Exception 2691 { 2692 if (alias == null) { 2693 alias = keyAlias; 2694 } 2695 2696 Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass); 2697 PrivateKey privKey = (PrivateKey)objs.fst; 2698 if (keyPass == null) 2699 keyPass = objs.snd; 2700 2701 // Determine the signature algorithm 2702 if (sigAlgName == null) { 2703 sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm()); 2704 } 2705 2706 // Get the old certificate 2707 Certificate oldCert = keyStore.getCertificate(alias); 2708 if (oldCert == null) { 2709 MessageFormat form = new MessageFormat 2710 (rb.getString("alias.has.no.public.key")); 2711 Object[] source = {alias}; 2712 throw new Exception(form.format(source)); 2713 } 2714 if (!(oldCert instanceof X509Certificate)) { 2715 MessageFormat form = new MessageFormat 2716 (rb.getString("alias.has.no.X.509.certificate")); 2717 Object[] source = {alias}; 2718 throw new Exception(form.format(source)); 2719 } 2720 2721 // convert to X509CertImpl, so that we can modify selected fields 2722 // (no public APIs available yet) 2723 byte[] encoded = oldCert.getEncoded(); 2724 X509CertImpl certImpl = new X509CertImpl(encoded); 2725 X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME 2726 + "." + 2727 X509CertImpl.INFO); 2728 2729 // Extend its validity 2730 Date firstDate = getStartDate(startDate); 2731 Date lastDate = new Date(); 2732 lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); 2733 CertificateValidity interval = new CertificateValidity(firstDate, 2734 lastDate); 2735 certInfo.set(X509CertInfo.VALIDITY, interval); 2736 2737 // Make new serial number 2738 certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( 2739 new java.util.Random().nextInt() & 0x7fffffff)); 2740 2741 // Set owner and issuer fields 2742 X500Name owner; 2743 if (dname == null) { 2744 // Get the owner name from the certificate 2745 owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." + 2746 X509CertInfo.DN_NAME); 2747 } else { 2748 // Use the owner name specified at the command line 2749 owner = new X500Name(dname); 2750 certInfo.set(X509CertInfo.SUBJECT + "." + 2751 X509CertInfo.DN_NAME, owner); 2752 } 2753 // Make issuer same as owner (self-signed!) 2754 certInfo.set(X509CertInfo.ISSUER + "." + 2755 X509CertInfo.DN_NAME, owner); 2756 2757 // The inner and outer signature algorithms have to match. 2758 // The way we achieve that is really ugly, but there seems to be no 2759 // other solution: We first sign the cert, then retrieve the 2760 // outer sigalg and use it to set the inner sigalg 2761 X509CertImpl newCert = new X509CertImpl(certInfo); 2762 AlgorithmParameterSpec params = AlgorithmId 2763 .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); 2764 newCert.sign(privKey, params, sigAlgName, null); 2765 AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG); 2766 certInfo.set(CertificateAlgorithmId.NAME + "." + 2767 CertificateAlgorithmId.ALGORITHM, sigAlgid); 2768 2769 certInfo.set(X509CertInfo.VERSION, 2770 new CertificateVersion(CertificateVersion.V3)); 2771 2772 CertificateExtensions ext = createV3Extensions( 2773 null, 2774 (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS), 2775 v3ext, 2776 oldCert.getPublicKey(), 2777 null); 2778 certInfo.set(X509CertInfo.EXTENSIONS, ext); 2779 // Sign the new certificate 2780 newCert = new X509CertImpl(certInfo); 2781 newCert.sign(privKey, params, sigAlgName, null); 2782 2783 // Store the new certificate as a single-element certificate chain 2784 keyStore.setKeyEntry(alias, privKey, 2785 (keyPass != null) ? keyPass : storePass, 2786 new Certificate[] { newCert } ); 2787 2788 if (verbose) { 2789 System.err.println(rb.getString("New.certificate.self.signed.")); 2790 System.err.print(newCert.toString()); 2791 System.err.println(); 2792 } 2793 } 2794 2795 /** 2796 * Processes a certificate reply from a certificate authority. 2797 * 2798 * <p>Builds a certificate chain on top of the certificate reply, 2799 * using trusted certificates from the keystore. The chain is complete 2800 * after a self-signed certificate has been encountered. The self-signed 2801 * certificate is considered a root certificate authority, and is stored 2802 * at the end of the chain. 2803 * 2804 * <p>The newly generated chain replaces the old chain associated with the 2805 * key entry. 2806 * 2807 * @return true if the certificate reply was installed, otherwise false. 2808 */ 2809 private boolean installReply(String alias, InputStream in) 2810 throws Exception 2811 { 2812 if (alias == null) { 2813 alias = keyAlias; 2814 } 2815 2816 Pair<Key,char[]> objs = recoverKey(alias, storePass, keyPass); 2817 PrivateKey privKey = (PrivateKey)objs.fst; 2818 if (keyPass == null) { 2819 keyPass = objs.snd; 2820 } 2821 2822 Certificate userCert = keyStore.getCertificate(alias); 2823 if (userCert == null) { 2824 MessageFormat form = new MessageFormat 2825 (rb.getString("alias.has.no.public.key.certificate.")); 2826 Object[] source = {alias}; 2827 throw new Exception(form.format(source)); 2828 } 2829 2830 // Read the certificates in the reply 2831 Collection<? extends Certificate> c = cf.generateCertificates(in); 2832 if (c.isEmpty()) { 2833 throw new Exception(rb.getString("Reply.has.no.certificates")); 2834 } 2835 Certificate[] replyCerts = c.toArray(new Certificate[c.size()]); 2836 Certificate[] newChain; 2837 if (replyCerts.length == 1) { 2838 // single-cert reply 2839 newChain = establishCertChain(userCert, replyCerts[0]); 2840 } else { 2841 // cert-chain reply (e.g., PKCS#7) 2842 newChain = validateReply(alias, userCert, replyCerts); 2843 } 2844 2845 // Now store the newly established chain in the keystore. The new 2846 // chain replaces the old one. The chain can be null if user chooses no. 2847 if (newChain != null) { 2848 keyStore.setKeyEntry(alias, privKey, 2849 (keyPass != null) ? keyPass : storePass, 2850 newChain); 2851 return true; 2852 } else { 2853 return false; 2854 } 2855 } 2856 2857 /** 2858 * Imports a certificate and adds it to the list of trusted certificates. 2859 * 2860 * @return true if the certificate was added, otherwise false. 2861 */ 2862 private boolean addTrustedCert(String alias, InputStream in) 2863 throws Exception 2864 { 2865 if (alias == null) { 2866 throw new Exception(rb.getString("Must.specify.alias")); 2867 } 2868 if (keyStore.containsAlias(alias)) { 2869 MessageFormat form = new MessageFormat(rb.getString 2870 ("Certificate.not.imported.alias.alias.already.exists")); 2871 Object[] source = {alias}; 2872 throw new Exception(form.format(source)); 2873 } 2874 2875 // Read the certificate 2876 X509Certificate cert = null; 2877 try { 2878 cert = (X509Certificate)cf.generateCertificate(in); 2879 } catch (ClassCastException | CertificateException ce) { 2880 throw new Exception(rb.getString("Input.not.an.X.509.certificate")); 2881 } 2882 2883 if (noprompt) { 2884 checkWeak(rb.getString("the.input"), cert); 2885 keyStore.setCertificateEntry(alias, cert); 2886 return true; 2887 } 2888 2889 // if certificate is self-signed, make sure it verifies 2890 boolean selfSigned = false; 2891 if (KeyStoreUtil.isSelfSigned(cert)) { 2892 cert.verify(cert.getPublicKey()); 2893 selfSigned = true; 2894 } 2895 2896 // check if cert already exists in keystore 2897 String reply = null; 2898 String trustalias = keyStore.getCertificateAlias(cert); 2899 if (trustalias != null) { 2900 MessageFormat form = new MessageFormat(rb.getString 2901 ("Certificate.already.exists.in.keystore.under.alias.trustalias.")); 2902 Object[] source = {trustalias}; 2903 System.err.println(form.format(source)); 2904 checkWeak(rb.getString("the.input"), cert); 2905 printWeakWarnings(true); 2906 reply = getYesNoReply 2907 (rb.getString("Do.you.still.want.to.add.it.no.")); 2908 } else if (selfSigned) { 2909 if (trustcacerts && (caks != null) && 2910 ((trustalias=caks.getCertificateAlias(cert)) != null)) { 2911 MessageFormat form = new MessageFormat(rb.getString 2912 ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.")); 2913 Object[] source = {trustalias}; 2914 System.err.println(form.format(source)); 2915 checkWeak(rb.getString("the.input"), cert); 2916 printWeakWarnings(true); 2917 reply = getYesNoReply 2918 (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no.")); 2919 } 2920 if (trustalias == null) { 2921 // Print the cert and ask user if they really want to add 2922 // it to their keystore 2923 printX509Cert(cert, System.out); 2924 checkWeak(rb.getString("the.input"), cert); 2925 printWeakWarnings(true); 2926 reply = getYesNoReply 2927 (rb.getString("Trust.this.certificate.no.")); 2928 } 2929 } 2930 if (reply != null) { 2931 if ("YES".equals(reply)) { 2932 keyStore.setCertificateEntry(alias, cert); 2933 return true; 2934 } else { 2935 return false; 2936 } 2937 } 2938 2939 // Not found in this keystore and not self-signed 2940 // Try to establish trust chain 2941 try { 2942 Certificate[] chain = establishCertChain(null, cert); 2943 if (chain != null) { 2944 keyStore.setCertificateEntry(alias, cert); 2945 return true; 2946 } 2947 } catch (Exception e) { 2948 // Print the cert and ask user if they really want to add it to 2949 // their keystore 2950 printX509Cert(cert, System.out); 2951 checkWeak(rb.getString("the.input"), cert); 2952 printWeakWarnings(true); 2953 reply = getYesNoReply 2954 (rb.getString("Trust.this.certificate.no.")); 2955 if ("YES".equals(reply)) { 2956 keyStore.setCertificateEntry(alias, cert); 2957 return true; 2958 } else { 2959 return false; 2960 } 2961 } 2962 2963 return false; 2964 } 2965 2966 /** 2967 * Prompts user for new password. New password must be different from 2968 * old one. 2969 * 2970 * @param prompt the message that gets prompted on the screen 2971 * @param oldPasswd the current (i.e., old) password 2972 */ 2973 private char[] getNewPasswd(String prompt, char[] oldPasswd) 2974 throws Exception 2975 { 2976 char[] entered = null; 2977 char[] reentered = null; 2978 2979 for (int count = 0; count < 3; count++) { 2980 MessageFormat form = new MessageFormat 2981 (rb.getString("New.prompt.")); 2982 Object[] source = {prompt}; 2983 System.err.print(form.format(source)); 2984 entered = Password.readPassword(System.in); 2985 passwords.add(entered); 2986 if (entered == null || entered.length < 6) { 2987 System.err.println(rb.getString 2988 ("Password.is.too.short.must.be.at.least.6.characters")); 2989 } else if (Arrays.equals(entered, oldPasswd)) { 2990 System.err.println(rb.getString("Passwords.must.differ")); 2991 } else { 2992 form = new MessageFormat 2993 (rb.getString("Re.enter.new.prompt.")); 2994 Object[] src = {prompt}; 2995 System.err.print(form.format(src)); 2996 reentered = Password.readPassword(System.in); 2997 passwords.add(reentered); 2998 if (!Arrays.equals(entered, reentered)) { 2999 System.err.println 3000 (rb.getString("They.don.t.match.Try.again")); 3001 } else { 3002 Arrays.fill(reentered, ' '); 3003 return entered; 3004 } 3005 } 3006 if (entered != null) { 3007 Arrays.fill(entered, ' '); 3008 entered = null; 3009 } 3010 if (reentered != null) { 3011 Arrays.fill(reentered, ' '); 3012 reentered = null; 3013 } 3014 } 3015 throw new Exception(rb.getString("Too.many.failures.try.later")); 3016 } 3017 3018 /** 3019 * Prompts user for alias name. 3020 * @param prompt the {0} of "Enter {0} alias name: " in prompt line 3021 * @returns the string entered by the user, without the \n at the end 3022 */ 3023 private String getAlias(String prompt) throws Exception { 3024 if (prompt != null) { 3025 MessageFormat form = new MessageFormat 3026 (rb.getString("Enter.prompt.alias.name.")); 3027 Object[] source = {prompt}; 3028 System.err.print(form.format(source)); 3029 } else { 3030 System.err.print(rb.getString("Enter.alias.name.")); 3031 } 3032 return (new BufferedReader(new InputStreamReader( 3033 System.in))).readLine(); 3034 } 3035 3036 /** 3037 * Prompts user for an input string from the command line (System.in) 3038 * @prompt the prompt string printed 3039 * @returns the string entered by the user, without the \n at the end 3040 */ 3041 private String inputStringFromStdin(String prompt) throws Exception { 3042 System.err.print(prompt); 3043 return (new BufferedReader(new InputStreamReader( 3044 System.in))).readLine(); 3045 } 3046 3047 /** 3048 * Prompts user for key password. User may select to choose the same 3049 * password (<code>otherKeyPass</code>) as for <code>otherAlias</code>. 3050 */ 3051 private char[] getKeyPasswd(String alias, String otherAlias, 3052 char[] otherKeyPass) 3053 throws Exception 3054 { 3055 int count = 0; 3056 char[] keyPass = null; 3057 3058 do { 3059 if (otherKeyPass != null) { 3060 MessageFormat form = new MessageFormat(rb.getString 3061 ("Enter.key.password.for.alias.")); 3062 Object[] source = {alias}; 3063 System.err.println(form.format(source)); 3064 3065 form = new MessageFormat(rb.getString 3066 (".RETURN.if.same.as.for.otherAlias.")); 3067 Object[] src = {otherAlias}; 3068 System.err.print(form.format(src)); 3069 } else { 3070 MessageFormat form = new MessageFormat(rb.getString 3071 ("Enter.key.password.for.alias.")); 3072 Object[] source = {alias}; 3073 System.err.print(form.format(source)); 3074 } 3075 System.err.flush(); 3076 keyPass = Password.readPassword(System.in); 3077 passwords.add(keyPass); 3078 if (keyPass == null) { 3079 keyPass = otherKeyPass; 3080 } 3081 count++; 3082 } while ((keyPass == null) && count < 3); 3083 3084 if (keyPass == null) { 3085 throw new Exception(rb.getString("Too.many.failures.try.later")); 3086 } 3087 3088 return keyPass; 3089 } 3090 3091 private String withWeak(String alg) { 3092 if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) { 3093 if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) { 3094 return alg; 3095 } else { 3096 return String.format(rb.getString("with.weak"), alg); 3097 } 3098 } else { 3099 return String.format(rb.getString("with.disabled"), alg); 3100 } 3101 } 3102 3103 private String fullDisplayAlgName(Key key) { 3104 String result = key.getAlgorithm(); 3105 if (key instanceof ECKey) { 3106 ECParameterSpec paramSpec = ((ECKey) key).getParams(); 3107 if (paramSpec instanceof NamedCurve) { 3108 result += " (" + paramSpec.toString().split(" ")[0] + ")"; 3109 } 3110 } 3111 return result; 3112 } 3113 3114 private String withWeak(PublicKey key) { 3115 int kLen = KeyUtil.getKeySize(key); 3116 String displayAlg = fullDisplayAlgName(key); 3117 if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 3118 if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 3119 if (kLen >= 0) { 3120 return String.format(rb.getString("key.bit"), kLen, displayAlg); 3121 } else { 3122 return String.format(rb.getString("unknown.size.1"), displayAlg); 3123 } 3124 } else { 3125 return String.format(rb.getString("key.bit.weak"), kLen, displayAlg); 3126 } 3127 } else { 3128 return String.format(rb.getString("key.bit.disabled"), kLen, displayAlg); 3129 } 3130 } 3131 3132 /** 3133 * Prints a certificate in a human readable format. 3134 */ 3135 private void printX509Cert(X509Certificate cert, PrintStream out) 3136 throws Exception 3137 { 3138 3139 MessageFormat form = new MessageFormat 3140 (rb.getString(".PATTERN.printX509Cert.with.weak")); 3141 PublicKey pkey = cert.getPublicKey(); 3142 String sigName = cert.getSigAlgName(); 3143 // No need to warn about sigalg of a trust anchor 3144 if (!isTrustedCert(cert)) { 3145 sigName = withWeak(sigName); 3146 } 3147 Object[] source = {cert.getSubjectDN().toString(), 3148 cert.getIssuerDN().toString(), 3149 cert.getSerialNumber().toString(16), 3150 cert.getNotBefore().toString(), 3151 cert.getNotAfter().toString(), 3152 getCertFingerPrint("SHA-1", cert), 3153 getCertFingerPrint("SHA-256", cert), 3154 sigName, 3155 withWeak(pkey), 3156 cert.getVersion() 3157 }; 3158 out.println(form.format(source)); 3159 3160 if (cert instanceof X509CertImpl) { 3161 X509CertImpl impl = (X509CertImpl)cert; 3162 X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME 3163 + "." + 3164 X509CertImpl.INFO); 3165 CertificateExtensions exts = (CertificateExtensions) 3166 certInfo.get(X509CertInfo.EXTENSIONS); 3167 if (exts != null) { 3168 printExtensions(rb.getString("Extensions."), exts, out); 3169 } 3170 } 3171 } 3172 3173 private static void printExtensions(String title, CertificateExtensions exts, PrintStream out) 3174 throws Exception { 3175 int extnum = 0; 3176 Iterator<Extension> i1 = exts.getAllExtensions().iterator(); 3177 Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator(); 3178 while (i1.hasNext() || i2.hasNext()) { 3179 Extension ext = i1.hasNext()?i1.next():i2.next(); 3180 if (extnum == 0) { 3181 out.println(); 3182 out.println(title); 3183 out.println(); 3184 } 3185 out.print("#"+(++extnum)+": "+ ext); 3186 if (ext.getClass() == Extension.class) { 3187 byte[] v = ext.getExtensionValue(); 3188 if (v.length == 0) { 3189 out.println(rb.getString(".Empty.value.")); 3190 } else { 3191 new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out); 3192 out.println(); 3193 } 3194 } 3195 out.println(); 3196 } 3197 } 3198 3199 /** 3200 * Locates a signer for a given certificate from a given keystore and 3201 * returns the signer's certificate. 3202 * @param cert the certificate whose signer is searched, not null 3203 * @param ks the keystore to search with, not null 3204 * @return <code>cert</code> itself if it's already inside <code>ks</code>, 3205 * or a certificate inside <code>ks</code> who signs <code>cert</code>, 3206 * or null otherwise. A label is added. 3207 */ 3208 private static Pair<String,Certificate> 3209 getSigner(Certificate cert, KeyStore ks) throws Exception { 3210 if (ks.getCertificateAlias(cert) != null) { 3211 return new Pair<>("", cert); 3212 } 3213 for (Enumeration<String> aliases = ks.aliases(); 3214 aliases.hasMoreElements(); ) { 3215 String name = aliases.nextElement(); 3216 Certificate trustedCert = ks.getCertificate(name); 3217 if (trustedCert != null) { 3218 try { 3219 cert.verify(trustedCert.getPublicKey()); 3220 return new Pair<>(name, trustedCert); 3221 } catch (Exception e) { 3222 // Not verified, skip to the next one 3223 } 3224 } 3225 } 3226 return null; 3227 } 3228 3229 /** 3230 * Gets an X.500 name suitable for inclusion in a certification request. 3231 */ 3232 private X500Name getX500Name() throws IOException { 3233 BufferedReader in; 3234 in = new BufferedReader(new InputStreamReader(System.in)); 3235 String commonName = "Unknown"; 3236 String organizationalUnit = "Unknown"; 3237 String organization = "Unknown"; 3238 String city = "Unknown"; 3239 String state = "Unknown"; 3240 String country = "Unknown"; 3241 X500Name name; 3242 String userInput = null; 3243 3244 int maxRetry = 20; 3245 do { 3246 if (maxRetry-- < 0) { 3247 throw new RuntimeException(rb.getString( 3248 "Too.many.retries.program.terminated")); 3249 } 3250 commonName = inputString(in, 3251 rb.getString("What.is.your.first.and.last.name."), 3252 commonName); 3253 organizationalUnit = inputString(in, 3254 rb.getString 3255 ("What.is.the.name.of.your.organizational.unit."), 3256 organizationalUnit); 3257 organization = inputString(in, 3258 rb.getString("What.is.the.name.of.your.organization."), 3259 organization); 3260 city = inputString(in, 3261 rb.getString("What.is.the.name.of.your.City.or.Locality."), 3262 city); 3263 state = inputString(in, 3264 rb.getString("What.is.the.name.of.your.State.or.Province."), 3265 state); 3266 country = inputString(in, 3267 rb.getString 3268 ("What.is.the.two.letter.country.code.for.this.unit."), 3269 country); 3270 name = new X500Name(commonName, organizationalUnit, organization, 3271 city, state, country); 3272 MessageFormat form = new MessageFormat 3273 (rb.getString("Is.name.correct.")); 3274 Object[] source = {name}; 3275 userInput = inputString 3276 (in, form.format(source), rb.getString("no")); 3277 } while (collator.compare(userInput, rb.getString("yes")) != 0 && 3278 collator.compare(userInput, rb.getString("y")) != 0); 3279 3280 System.err.println(); 3281 return name; 3282 } 3283 3284 private String inputString(BufferedReader in, String prompt, 3285 String defaultValue) 3286 throws IOException 3287 { 3288 System.err.println(prompt); 3289 MessageFormat form = new MessageFormat 3290 (rb.getString(".defaultValue.")); 3291 Object[] source = {defaultValue}; 3292 System.err.print(form.format(source)); 3293 System.err.flush(); 3294 3295 String value = in.readLine(); 3296 if (value == null || collator.compare(value, "") == 0) { 3297 value = defaultValue; 3298 } 3299 return value; 3300 } 3301 3302 /** 3303 * Writes an X.509 certificate in base64 or binary encoding to an output 3304 * stream. 3305 */ 3306 private void dumpCert(Certificate cert, PrintStream out) 3307 throws IOException, CertificateException 3308 { 3309 if (rfc) { 3310 out.println(X509Factory.BEGIN_CERT); 3311 out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded())); 3312 out.println(X509Factory.END_CERT); 3313 } else { 3314 out.write(cert.getEncoded()); // binary 3315 } 3316 } 3317 3318 /** 3319 * Converts a byte to hex digit and writes to the supplied buffer 3320 */ 3321 private void byte2hex(byte b, StringBuffer buf) { 3322 char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', 3323 '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 3324 int high = ((b & 0xf0) >> 4); 3325 int low = (b & 0x0f); 3326 buf.append(hexChars[high]); 3327 buf.append(hexChars[low]); 3328 } 3329 3330 /** 3331 * Converts a byte array to hex string 3332 */ 3333 private String toHexString(byte[] block) { 3334 StringBuffer buf = new StringBuffer(); 3335 int len = block.length; 3336 for (int i = 0; i < len; i++) { 3337 byte2hex(block[i], buf); 3338 if (i < len-1) { 3339 buf.append(":"); 3340 } 3341 } 3342 return buf.toString(); 3343 } 3344 3345 /** 3346 * Recovers (private) key associated with given alias. 3347 * 3348 * @return an array of objects, where the 1st element in the array is the 3349 * recovered private key, and the 2nd element is the password used to 3350 * recover it. 3351 */ 3352 private Pair<Key,char[]> recoverKey(String alias, char[] storePass, 3353 char[] keyPass) 3354 throws Exception 3355 { 3356 Key key = null; 3357 3358 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { 3359 key = keyStore.getKey(alias, null); 3360 return Pair.of(key, null); 3361 } 3362 3363 if (keyStore.containsAlias(alias) == false) { 3364 MessageFormat form = new MessageFormat 3365 (rb.getString("Alias.alias.does.not.exist")); 3366 Object[] source = {alias}; 3367 throw new Exception(form.format(source)); 3368 } 3369 if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) && 3370 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { 3371 MessageFormat form = new MessageFormat 3372 (rb.getString("Alias.alias.has.no.key")); 3373 Object[] source = {alias}; 3374 throw new Exception(form.format(source)); 3375 } 3376 3377 if (keyPass == null) { 3378 // Try to recover the key using the keystore password 3379 try { 3380 key = keyStore.getKey(alias, storePass); 3381 3382 keyPass = storePass; 3383 passwords.add(keyPass); 3384 } catch (UnrecoverableKeyException e) { 3385 // Did not work out, so prompt user for key password 3386 if (!token) { 3387 keyPass = getKeyPasswd(alias, null, null); 3388 key = keyStore.getKey(alias, keyPass); 3389 } else { 3390 throw e; 3391 } 3392 } 3393 } else { 3394 key = keyStore.getKey(alias, keyPass); 3395 } 3396 3397 return Pair.of(key, keyPass); 3398 } 3399 3400 /** 3401 * Recovers entry associated with given alias. 3402 * 3403 * @return an array of objects, where the 1st element in the array is the 3404 * recovered entry, and the 2nd element is the password used to 3405 * recover it (null if no password). 3406 */ 3407 private Pair<Entry,char[]> recoverEntry(KeyStore ks, 3408 String alias, 3409 char[] pstore, 3410 char[] pkey) throws Exception { 3411 3412 if (ks.containsAlias(alias) == false) { 3413 MessageFormat form = new MessageFormat 3414 (rb.getString("Alias.alias.does.not.exist")); 3415 Object[] source = {alias}; 3416 throw new Exception(form.format(source)); 3417 } 3418 3419 PasswordProtection pp = null; 3420 Entry entry; 3421 3422 try { 3423 // First attempt to access entry without key password 3424 // (PKCS11 entry or trusted certificate entry, for example) 3425 3426 entry = ks.getEntry(alias, pp); 3427 pkey = null; 3428 } catch (UnrecoverableEntryException une) { 3429 3430 if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) || 3431 KeyStoreUtil.isWindowsKeyStore(ks.getType())) { 3432 // should not happen, but a possibility 3433 throw une; 3434 } 3435 3436 // entry is protected 3437 3438 if (pkey != null) { 3439 3440 // try provided key password 3441 3442 pp = new PasswordProtection(pkey); 3443 entry = ks.getEntry(alias, pp); 3444 3445 } else { 3446 3447 // try store pass 3448 3449 try { 3450 pp = new PasswordProtection(pstore); 3451 entry = ks.getEntry(alias, pp); 3452 pkey = pstore; 3453 } catch (UnrecoverableEntryException une2) { 3454 if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) { 3455 3456 // P12 keystore currently does not support separate 3457 // store and entry passwords 3458 3459 throw une2; 3460 } else { 3461 3462 // prompt for entry password 3463 3464 pkey = getKeyPasswd(alias, null, null); 3465 pp = new PasswordProtection(pkey); 3466 entry = ks.getEntry(alias, pp); 3467 } 3468 } 3469 } 3470 } 3471 3472 return Pair.of(entry, pkey); 3473 } 3474 /** 3475 * Gets the requested finger print of the certificate. 3476 */ 3477 private String getCertFingerPrint(String mdAlg, Certificate cert) 3478 throws Exception 3479 { 3480 byte[] encCertInfo = cert.getEncoded(); 3481 MessageDigest md = MessageDigest.getInstance(mdAlg); 3482 byte[] digest = md.digest(encCertInfo); 3483 return toHexString(digest); 3484 } 3485 3486 /** 3487 * Prints warning about missing integrity check. 3488 */ 3489 private void printNoIntegrityWarning() { 3490 System.err.println(); 3491 System.err.println(rb.getString 3492 (".WARNING.WARNING.WARNING.")); 3493 System.err.println(rb.getString 3494 (".The.integrity.of.the.information.stored.in.your.keystore.")); 3495 System.err.println(rb.getString 3496 (".WARNING.WARNING.WARNING.")); 3497 System.err.println(); 3498 } 3499 3500 /** 3501 * Validates chain in certification reply, and returns the ordered 3502 * elements of the chain (with user certificate first, and root 3503 * certificate last in the array). 3504 * 3505 * @param alias the alias name 3506 * @param userCert the user certificate of the alias 3507 * @param replyCerts the chain provided in the reply 3508 */ 3509 private Certificate[] validateReply(String alias, 3510 Certificate userCert, 3511 Certificate[] replyCerts) 3512 throws Exception 3513 { 3514 3515 checkWeak(rb.getString("reply"), replyCerts); 3516 3517 // order the certs in the reply (bottom-up). 3518 // we know that all certs in the reply are of type X.509, because 3519 // we parsed them using an X.509 certificate factory 3520 int i; 3521 PublicKey userPubKey = userCert.getPublicKey(); 3522 for (i=0; i<replyCerts.length; i++) { 3523 if (userPubKey.equals(replyCerts[i].getPublicKey())) { 3524 break; 3525 } 3526 } 3527 if (i == replyCerts.length) { 3528 MessageFormat form = new MessageFormat(rb.getString 3529 ("Certificate.reply.does.not.contain.public.key.for.alias.")); 3530 Object[] source = {alias}; 3531 throw new Exception(form.format(source)); 3532 } 3533 3534 Certificate tmpCert = replyCerts[0]; 3535 replyCerts[0] = replyCerts[i]; 3536 replyCerts[i] = tmpCert; 3537 3538 X509Certificate thisCert = (X509Certificate)replyCerts[0]; 3539 3540 for (i=1; i < replyCerts.length-1; i++) { 3541 // find a cert in the reply who signs thisCert 3542 int j; 3543 for (j=i; j<replyCerts.length; j++) { 3544 if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) { 3545 tmpCert = replyCerts[i]; 3546 replyCerts[i] = replyCerts[j]; 3547 replyCerts[j] = tmpCert; 3548 thisCert = (X509Certificate)replyCerts[i]; 3549 break; 3550 } 3551 } 3552 if (j == replyCerts.length) { 3553 throw new Exception 3554 (rb.getString("Incomplete.certificate.chain.in.reply")); 3555 } 3556 } 3557 3558 if (noprompt) { 3559 return replyCerts; 3560 } 3561 3562 // do we trust the cert at the top? 3563 Certificate topCert = replyCerts[replyCerts.length-1]; 3564 boolean fromKeyStore = true; 3565 Pair<String,Certificate> root = getSigner(topCert, keyStore); 3566 if (root == null && trustcacerts && caks != null) { 3567 root = getSigner(topCert, caks); 3568 fromKeyStore = false; 3569 } 3570 if (root == null) { 3571 System.err.println(); 3572 System.err.println 3573 (rb.getString("Top.level.certificate.in.reply.")); 3574 printX509Cert((X509Certificate)topCert, System.out); 3575 System.err.println(); 3576 System.err.print(rb.getString(".is.not.trusted.")); 3577 printWeakWarnings(true); 3578 String reply = getYesNoReply 3579 (rb.getString("Install.reply.anyway.no.")); 3580 if ("NO".equals(reply)) { 3581 return null; 3582 } 3583 } else { 3584 if (root.snd != topCert) { 3585 // append the root CA cert to the chain 3586 Certificate[] tmpCerts = 3587 new Certificate[replyCerts.length+1]; 3588 System.arraycopy(replyCerts, 0, tmpCerts, 0, 3589 replyCerts.length); 3590 tmpCerts[tmpCerts.length-1] = root.snd; 3591 replyCerts = tmpCerts; 3592 checkWeak(String.format(rb.getString(fromKeyStore ? 3593 "alias.in.keystore" : 3594 "alias.in.cacerts"), 3595 root.fst), 3596 root.snd); 3597 } 3598 } 3599 return replyCerts; 3600 } 3601 3602 /** 3603 * Establishes a certificate chain (using trusted certificates in the 3604 * keystore and cacerts), starting with the reply (certToVerify) 3605 * and ending at a self-signed certificate found in the keystore. 3606 * 3607 * @param userCert optional existing certificate, mostly likely be the 3608 * original self-signed cert created by -genkeypair. 3609 * It must have the same public key as certToVerify 3610 * but cannot be the same cert. 3611 * @param certToVerify the starting certificate to build the chain 3612 * @returns the established chain, might be null if user decides not 3613 */ 3614 private Certificate[] establishCertChain(Certificate userCert, 3615 Certificate certToVerify) 3616 throws Exception 3617 { 3618 if (userCert != null) { 3619 // Make sure that the public key of the certificate reply matches 3620 // the original public key in the keystore 3621 PublicKey origPubKey = userCert.getPublicKey(); 3622 PublicKey replyPubKey = certToVerify.getPublicKey(); 3623 if (!origPubKey.equals(replyPubKey)) { 3624 throw new Exception(rb.getString 3625 ("Public.keys.in.reply.and.keystore.don.t.match")); 3626 } 3627 3628 // If the two certs are identical, we're done: no need to import 3629 // anything 3630 if (certToVerify.equals(userCert)) { 3631 throw new Exception(rb.getString 3632 ("Certificate.reply.and.certificate.in.keystore.are.identical")); 3633 } 3634 } 3635 3636 // Build a hash table of all certificates in the keystore. 3637 // Use the subject distinguished name as the key into the hash table. 3638 // All certificates associated with the same subject distinguished 3639 // name are stored in the same hash table entry as a vector. 3640 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null; 3641 if (keyStore.size() > 0) { 3642 certs = new Hashtable<>(11); 3643 keystorecerts2Hashtable(keyStore, certs); 3644 } 3645 if (trustcacerts) { 3646 if (caks!=null && caks.size()>0) { 3647 if (certs == null) { 3648 certs = new Hashtable<>(11); 3649 } 3650 keystorecerts2Hashtable(caks, certs); 3651 } 3652 } 3653 3654 // start building chain 3655 Vector<Pair<String,X509Certificate>> chain = new Vector<>(2); 3656 if (buildChain( 3657 new Pair<>(rb.getString("the.input"), 3658 (X509Certificate) certToVerify), 3659 chain, certs)) { 3660 for (Pair<String,X509Certificate> p : chain) { 3661 checkWeak(p.fst, p.snd); 3662 } 3663 Certificate[] newChain = 3664 new Certificate[chain.size()]; 3665 // buildChain() returns chain with self-signed root-cert first and 3666 // user-cert last, so we need to invert the chain before we store 3667 // it 3668 int j=0; 3669 for (int i=chain.size()-1; i>=0; i--) { 3670 newChain[j] = chain.elementAt(i).snd; 3671 j++; 3672 } 3673 return newChain; 3674 } else { 3675 throw new Exception 3676 (rb.getString("Failed.to.establish.chain.from.reply")); 3677 } 3678 } 3679 3680 /** 3681 * Recursively tries to establish chain from pool of certs starting from 3682 * certToVerify until a self-signed cert is found, and fill the certs found 3683 * into chain. Each cert in the chain signs the next one. 3684 * 3685 * This method is able to recover from an error, say, if certToVerify 3686 * is signed by certA but certA has no issuer in certs and itself is not 3687 * self-signed, the method can try another certB that also signs 3688 * certToVerify and look for signer of certB, etc, etc. 3689 * 3690 * Each cert in chain comes with a label showing its origin. The label is 3691 * used in the warning message when the cert is considered a risk. 3692 * 3693 * @param certToVerify the cert that needs to be verified. 3694 * @param chain the chain that's being built. 3695 * @param certs the pool of trusted certs 3696 * 3697 * @return true if successful, false otherwise. 3698 */ 3699 private boolean buildChain(Pair<String,X509Certificate> certToVerify, 3700 Vector<Pair<String,X509Certificate>> chain, 3701 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) { 3702 if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) { 3703 // reached self-signed root cert; 3704 // no verification needed because it's trusted. 3705 chain.addElement(certToVerify); 3706 return true; 3707 } 3708 3709 Principal issuer = certToVerify.snd.getIssuerDN(); 3710 3711 // Get the issuer's certificate(s) 3712 Vector<Pair<String,X509Certificate>> vec = certs.get(issuer); 3713 if (vec == null) { 3714 return false; 3715 } 3716 3717 // Try out each certificate in the vector, until we find one 3718 // whose public key verifies the signature of the certificate 3719 // in question. 3720 for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements(); 3721 issuerCerts.hasMoreElements(); ) { 3722 Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement(); 3723 PublicKey issuerPubKey = issuerCert.snd.getPublicKey(); 3724 try { 3725 certToVerify.snd.verify(issuerPubKey); 3726 } catch (Exception e) { 3727 continue; 3728 } 3729 if (buildChain(issuerCert, chain, certs)) { 3730 chain.addElement(certToVerify); 3731 return true; 3732 } 3733 } 3734 return false; 3735 } 3736 3737 /** 3738 * Prompts user for yes/no decision. 3739 * 3740 * @return the user's decision, can only be "YES" or "NO" 3741 */ 3742 private String getYesNoReply(String prompt) 3743 throws IOException 3744 { 3745 String reply = null; 3746 int maxRetry = 20; 3747 do { 3748 if (maxRetry-- < 0) { 3749 throw new RuntimeException(rb.getString( 3750 "Too.many.retries.program.terminated")); 3751 } 3752 System.err.print(prompt); 3753 System.err.flush(); 3754 reply = (new BufferedReader(new InputStreamReader 3755 (System.in))).readLine(); 3756 if (collator.compare(reply, "") == 0 || 3757 collator.compare(reply, rb.getString("n")) == 0 || 3758 collator.compare(reply, rb.getString("no")) == 0) { 3759 reply = "NO"; 3760 } else if (collator.compare(reply, rb.getString("y")) == 0 || 3761 collator.compare(reply, rb.getString("yes")) == 0) { 3762 reply = "YES"; 3763 } else { 3764 System.err.println(rb.getString("Wrong.answer.try.again")); 3765 reply = null; 3766 } 3767 } while (reply == null); 3768 return reply; 3769 } 3770 3771 /** 3772 * Stores the (leaf) certificates of a keystore in a hashtable. 3773 * All certs belonging to the same CA are stored in a vector that 3774 * in turn is stored in the hashtable, keyed by the CA's subject DN. 3775 * Each cert comes with a string label that shows its origin and alias. 3776 */ 3777 private void keystorecerts2Hashtable(KeyStore ks, 3778 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash) 3779 throws Exception { 3780 3781 for (Enumeration<String> aliases = ks.aliases(); 3782 aliases.hasMoreElements(); ) { 3783 String alias = aliases.nextElement(); 3784 Certificate cert = ks.getCertificate(alias); 3785 if (cert != null) { 3786 Principal subjectDN = ((X509Certificate)cert).getSubjectDN(); 3787 Pair<String,X509Certificate> pair = new Pair<>( 3788 String.format( 3789 rb.getString(ks == caks ? 3790 "alias.in.cacerts" : 3791 "alias.in.keystore"), 3792 alias), 3793 (X509Certificate)cert); 3794 Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN); 3795 if (vec == null) { 3796 vec = new Vector<>(); 3797 vec.addElement(pair); 3798 } else { 3799 if (!vec.contains(pair)) { 3800 vec.addElement(pair); 3801 } 3802 } 3803 hash.put(subjectDN, vec); 3804 } 3805 } 3806 } 3807 3808 /** 3809 * Returns the issue time that's specified the -startdate option 3810 * @param s the value of -startdate option 3811 */ 3812 private static Date getStartDate(String s) throws IOException { 3813 Calendar c = new GregorianCalendar(); 3814 if (s != null) { 3815 IOException ioe = new IOException( 3816 rb.getString("Illegal.startdate.value")); 3817 int len = s.length(); 3818 if (len == 0) { 3819 throw ioe; 3820 } 3821 if (s.charAt(0) == '-' || s.charAt(0) == '+') { 3822 // Form 1: ([+-]nnn[ymdHMS])+ 3823 int start = 0; 3824 while (start < len) { 3825 int sign = 0; 3826 switch (s.charAt(start)) { 3827 case '+': sign = 1; break; 3828 case '-': sign = -1; break; 3829 default: throw ioe; 3830 } 3831 int i = start+1; 3832 for (; i<len; i++) { 3833 char ch = s.charAt(i); 3834 if (ch < '0' || ch > '9') break; 3835 } 3836 if (i == start+1) throw ioe; 3837 int number = Integer.parseInt(s.substring(start+1, i)); 3838 if (i >= len) throw ioe; 3839 int unit = 0; 3840 switch (s.charAt(i)) { 3841 case 'y': unit = Calendar.YEAR; break; 3842 case 'm': unit = Calendar.MONTH; break; 3843 case 'd': unit = Calendar.DATE; break; 3844 case 'H': unit = Calendar.HOUR; break; 3845 case 'M': unit = Calendar.MINUTE; break; 3846 case 'S': unit = Calendar.SECOND; break; 3847 default: throw ioe; 3848 } 3849 c.add(unit, sign * number); 3850 start = i + 1; 3851 } 3852 } else { 3853 // Form 2: [yyyy/mm/dd] [HH:MM:SS] 3854 String date = null, time = null; 3855 if (len == 19) { 3856 date = s.substring(0, 10); 3857 time = s.substring(11); 3858 if (s.charAt(10) != ' ') 3859 throw ioe; 3860 } else if (len == 10) { 3861 date = s; 3862 } else if (len == 8) { 3863 time = s; 3864 } else { 3865 throw ioe; 3866 } 3867 if (date != null) { 3868 if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) { 3869 c.set(Integer.valueOf(date.substring(0, 4)), 3870 Integer.valueOf(date.substring(5, 7))-1, 3871 Integer.valueOf(date.substring(8, 10))); 3872 } else { 3873 throw ioe; 3874 } 3875 } 3876 if (time != null) { 3877 if (time.matches("\\d\\d:\\d\\d:\\d\\d")) { 3878 c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2))); 3879 c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2))); 3880 c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2))); 3881 c.set(Calendar.MILLISECOND, 0); 3882 } else { 3883 throw ioe; 3884 } 3885 } 3886 } 3887 } 3888 return c.getTime(); 3889 } 3890 3891 /** 3892 * Match a command (may be abbreviated) with a command set. 3893 * @param s the command provided 3894 * @param list the legal command set. If there is a null, commands after it 3895 * are regarded experimental, which means they are supported but their 3896 * existence should not be revealed to user. 3897 * @return the position of a single match, or -1 if none matched 3898 * @throws Exception if s is ambiguous 3899 */ 3900 private static int oneOf(String s, String... list) throws Exception { 3901 int[] match = new int[list.length]; 3902 int nmatch = 0; 3903 int experiment = Integer.MAX_VALUE; 3904 for (int i = 0; i<list.length; i++) { 3905 String one = list[i]; 3906 if (one == null) { 3907 experiment = i; 3908 continue; 3909 } 3910 if (one.toLowerCase(Locale.ENGLISH) 3911 .startsWith(s.toLowerCase(Locale.ENGLISH))) { 3912 match[nmatch++] = i; 3913 } else { 3914 StringBuffer sb = new StringBuffer(); 3915 boolean first = true; 3916 for (char c: one.toCharArray()) { 3917 if (first) { 3918 sb.append(c); 3919 first = false; 3920 } else { 3921 if (!Character.isLowerCase(c)) { 3922 sb.append(c); 3923 } 3924 } 3925 } 3926 if (sb.toString().equalsIgnoreCase(s)) { 3927 match[nmatch++] = i; 3928 } 3929 } 3930 } 3931 if (nmatch == 0) { 3932 return -1; 3933 } else if (nmatch == 1) { 3934 return match[0]; 3935 } else { 3936 // If multiple matches is in experimental commands, ignore them 3937 if (match[1] > experiment) { 3938 return match[0]; 3939 } 3940 StringBuffer sb = new StringBuffer(); 3941 MessageFormat form = new MessageFormat(rb.getString 3942 ("command.{0}.is.ambiguous.")); 3943 Object[] source = {s}; 3944 sb.append(form.format(source)); 3945 sb.append("\n "); 3946 for (int i=0; i<nmatch && match[i]<experiment; i++) { 3947 sb.append(' '); 3948 sb.append(list[match[i]]); 3949 } 3950 throw new Exception(sb.toString()); 3951 } 3952 } 3953 3954 /** 3955 * Create a GeneralName object from known types 3956 * @param t one of 5 known types 3957 * @param v value 3958 * @return which one 3959 */ 3960 private GeneralName createGeneralName(String t, String v) 3961 throws Exception { 3962 GeneralNameInterface gn; 3963 int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID"); 3964 if (p < 0) { 3965 throw new Exception(rb.getString( 3966 "Unrecognized.GeneralName.type.") + t); 3967 } 3968 switch (p) { 3969 case 0: gn = new RFC822Name(v); break; 3970 case 1: gn = new URIName(v); break; 3971 case 2: gn = new DNSName(v); break; 3972 case 3: gn = new IPAddressName(v); break; 3973 default: gn = new OIDName(v); break; //4 3974 } 3975 return new GeneralName(gn); 3976 } 3977 3978 private static final String[] extSupported = { 3979 "BasicConstraints", 3980 "KeyUsage", 3981 "ExtendedKeyUsage", 3982 "SubjectAlternativeName", 3983 "IssuerAlternativeName", 3984 "SubjectInfoAccess", 3985 "AuthorityInfoAccess", 3986 null, 3987 "CRLDistributionPoints", 3988 }; 3989 3990 private ObjectIdentifier findOidForExtName(String type) 3991 throws Exception { 3992 switch (oneOf(type, extSupported)) { 3993 case 0: return PKIXExtensions.BasicConstraints_Id; 3994 case 1: return PKIXExtensions.KeyUsage_Id; 3995 case 2: return PKIXExtensions.ExtendedKeyUsage_Id; 3996 case 3: return PKIXExtensions.SubjectAlternativeName_Id; 3997 case 4: return PKIXExtensions.IssuerAlternativeName_Id; 3998 case 5: return PKIXExtensions.SubjectInfoAccess_Id; 3999 case 6: return PKIXExtensions.AuthInfoAccess_Id; 4000 case 8: return PKIXExtensions.CRLDistributionPoints_Id; 4001 default: return new ObjectIdentifier(type); 4002 } 4003 } 4004 4005 /** 4006 * Create X509v3 extensions from a string representation. Note that the 4007 * SubjectKeyIdentifierExtension will always be created non-critical besides 4008 * the extension requested in the <code>extstr</code> argument. 4009 * 4010 * @param reqex the requested extensions, can be null, used for -gencert 4011 * @param ext the original extensions, can be null, used for -selfcert 4012 * @param extstrs -ext values, Read keytool doc 4013 * @param pkey the public key for the certificate 4014 * @param akey the public key for the authority (issuer) 4015 * @return the created CertificateExtensions 4016 */ 4017 private CertificateExtensions createV3Extensions( 4018 CertificateExtensions reqex, 4019 CertificateExtensions ext, 4020 List <String> extstrs, 4021 PublicKey pkey, 4022 PublicKey akey) throws Exception { 4023 4024 if (ext != null && reqex != null) { 4025 // This should not happen 4026 throw new Exception("One of request and original should be null."); 4027 } 4028 if (ext == null) ext = new CertificateExtensions(); 4029 try { 4030 // name{:critical}{=value} 4031 // Honoring requested extensions 4032 if (reqex != null) { 4033 for(String extstr: extstrs) { 4034 if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) { 4035 List<String> list = Arrays.asList( 4036 extstr.toLowerCase(Locale.ENGLISH).substring(8).split(",")); 4037 // First check existence of "all" 4038 if (list.contains("all")) { 4039 ext = reqex; // we know ext was null 4040 } 4041 // one by one for others 4042 for (String item: list) { 4043 if (item.equals("all")) continue; 4044 4045 // add or remove 4046 boolean add = true; 4047 // -1, unchanged, 0 crtical, 1 non-critical 4048 int action = -1; 4049 String type = null; 4050 if (item.startsWith("-")) { 4051 add = false; 4052 type = item.substring(1); 4053 } else { 4054 int colonpos = item.indexOf(':'); 4055 if (colonpos >= 0) { 4056 type = item.substring(0, colonpos); 4057 action = oneOf(item.substring(colonpos+1), 4058 "critical", "non-critical"); 4059 if (action == -1) { 4060 throw new Exception(rb.getString 4061 ("Illegal.value.") + item); 4062 } 4063 } 4064 } 4065 String n = reqex.getNameByOid(findOidForExtName(type)); 4066 if (add) { 4067 Extension e = reqex.get(n); 4068 if (!e.isCritical() && action == 0 4069 || e.isCritical() && action == 1) { 4070 e = Extension.newExtension( 4071 e.getExtensionId(), 4072 !e.isCritical(), 4073 e.getExtensionValue()); 4074 ext.set(n, e); 4075 } 4076 } else { 4077 ext.delete(n); 4078 } 4079 } 4080 break; 4081 } 4082 } 4083 } 4084 for(String extstr: extstrs) { 4085 String name, value; 4086 boolean isCritical = false; 4087 4088 int eqpos = extstr.indexOf('='); 4089 if (eqpos >= 0) { 4090 name = extstr.substring(0, eqpos); 4091 value = extstr.substring(eqpos+1); 4092 } else { 4093 name = extstr; 4094 value = null; 4095 } 4096 4097 int colonpos = name.indexOf(':'); 4098 if (colonpos >= 0) { 4099 if (oneOf(name.substring(colonpos+1), "critical") == 0) { 4100 isCritical = true; 4101 } 4102 name = name.substring(0, colonpos); 4103 } 4104 4105 if (name.equalsIgnoreCase("honored")) { 4106 continue; 4107 } 4108 int exttype = oneOf(name, extSupported); 4109 switch (exttype) { 4110 case 0: // BC 4111 int pathLen = -1; 4112 boolean isCA = false; 4113 if (value == null) { 4114 isCA = true; 4115 } else { 4116 try { // the abbr format 4117 pathLen = Integer.parseInt(value); 4118 isCA = true; 4119 } catch (NumberFormatException ufe) { 4120 // ca:true,pathlen:1 4121 for (String part: value.split(",")) { 4122 String[] nv = part.split(":"); 4123 if (nv.length != 2) { 4124 throw new Exception(rb.getString 4125 ("Illegal.value.") + extstr); 4126 } else { 4127 if (nv[0].equalsIgnoreCase("ca")) { 4128 isCA = Boolean.parseBoolean(nv[1]); 4129 } else if (nv[0].equalsIgnoreCase("pathlen")) { 4130 pathLen = Integer.parseInt(nv[1]); 4131 } else { 4132 throw new Exception(rb.getString 4133 ("Illegal.value.") + extstr); 4134 } 4135 } 4136 } 4137 } 4138 } 4139 ext.set(BasicConstraintsExtension.NAME, 4140 new BasicConstraintsExtension(isCritical, isCA, 4141 pathLen)); 4142 break; 4143 case 1: // KU 4144 if(value != null) { 4145 boolean[] ok = new boolean[9]; 4146 for (String s: value.split(",")) { 4147 int p = oneOf(s, 4148 "digitalSignature", // (0), 4149 "nonRepudiation", // (1) 4150 "keyEncipherment", // (2), 4151 "dataEncipherment", // (3), 4152 "keyAgreement", // (4), 4153 "keyCertSign", // (5), 4154 "cRLSign", // (6), 4155 "encipherOnly", // (7), 4156 "decipherOnly", // (8) 4157 "contentCommitment" // also (1) 4158 ); 4159 if (p < 0) { 4160 throw new Exception(rb.getString("Unknown.keyUsage.type.") + s); 4161 } 4162 if (p == 9) p = 1; 4163 ok[p] = true; 4164 } 4165 KeyUsageExtension kue = new KeyUsageExtension(ok); 4166 // The above KeyUsageExtension constructor does not 4167 // allow isCritical value, so... 4168 ext.set(KeyUsageExtension.NAME, Extension.newExtension( 4169 kue.getExtensionId(), 4170 isCritical, 4171 kue.getExtensionValue())); 4172 } else { 4173 throw new Exception(rb.getString 4174 ("Illegal.value.") + extstr); 4175 } 4176 break; 4177 case 2: // EKU 4178 if(value != null) { 4179 Vector<ObjectIdentifier> v = new Vector<>(); 4180 for (String s: value.split(",")) { 4181 int p = oneOf(s, 4182 "anyExtendedKeyUsage", 4183 "serverAuth", //1 4184 "clientAuth", //2 4185 "codeSigning", //3 4186 "emailProtection", //4 4187 "", //5 4188 "", //6 4189 "", //7 4190 "timeStamping", //8 4191 "OCSPSigning" //9 4192 ); 4193 if (p < 0) { 4194 try { 4195 v.add(new ObjectIdentifier(s)); 4196 } catch (Exception e) { 4197 throw new Exception(rb.getString( 4198 "Unknown.extendedkeyUsage.type.") + s); 4199 } 4200 } else if (p == 0) { 4201 v.add(new ObjectIdentifier("2.5.29.37.0")); 4202 } else { 4203 v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p)); 4204 } 4205 } 4206 ext.set(ExtendedKeyUsageExtension.NAME, 4207 new ExtendedKeyUsageExtension(isCritical, v)); 4208 } else { 4209 throw new Exception(rb.getString 4210 ("Illegal.value.") + extstr); 4211 } 4212 break; 4213 case 3: // SAN 4214 case 4: // IAN 4215 if(value != null) { 4216 String[] ps = value.split(","); 4217 GeneralNames gnames = new GeneralNames(); 4218 for(String item: ps) { 4219 colonpos = item.indexOf(':'); 4220 if (colonpos < 0) { 4221 throw new Exception("Illegal item " + item + " in " + extstr); 4222 } 4223 String t = item.substring(0, colonpos); 4224 String v = item.substring(colonpos+1); 4225 gnames.add(createGeneralName(t, v)); 4226 } 4227 if (exttype == 3) { 4228 ext.set(SubjectAlternativeNameExtension.NAME, 4229 new SubjectAlternativeNameExtension( 4230 isCritical, gnames)); 4231 } else { 4232 ext.set(IssuerAlternativeNameExtension.NAME, 4233 new IssuerAlternativeNameExtension( 4234 isCritical, gnames)); 4235 } 4236 } else { 4237 throw new Exception(rb.getString 4238 ("Illegal.value.") + extstr); 4239 } 4240 break; 4241 case 5: // SIA, always non-critical 4242 case 6: // AIA, always non-critical 4243 if (isCritical) { 4244 throw new Exception(rb.getString( 4245 "This.extension.cannot.be.marked.as.critical.") + extstr); 4246 } 4247 if(value != null) { 4248 List<AccessDescription> accessDescriptions = 4249 new ArrayList<>(); 4250 String[] ps = value.split(","); 4251 for(String item: ps) { 4252 colonpos = item.indexOf(':'); 4253 int colonpos2 = item.indexOf(':', colonpos+1); 4254 if (colonpos < 0 || colonpos2 < 0) { 4255 throw new Exception(rb.getString 4256 ("Illegal.value.") + extstr); 4257 } 4258 String m = item.substring(0, colonpos); 4259 String t = item.substring(colonpos+1, colonpos2); 4260 String v = item.substring(colonpos2+1); 4261 int p = oneOf(m, 4262 "", 4263 "ocsp", //1 4264 "caIssuers", //2 4265 "timeStamping", //3 4266 "", 4267 "caRepository" //5 4268 ); 4269 ObjectIdentifier oid; 4270 if (p < 0) { 4271 try { 4272 oid = new ObjectIdentifier(m); 4273 } catch (Exception e) { 4274 throw new Exception(rb.getString( 4275 "Unknown.AccessDescription.type.") + m); 4276 } 4277 } else { 4278 oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p); 4279 } 4280 accessDescriptions.add(new AccessDescription( 4281 oid, createGeneralName(t, v))); 4282 } 4283 if (exttype == 5) { 4284 ext.set(SubjectInfoAccessExtension.NAME, 4285 new SubjectInfoAccessExtension(accessDescriptions)); 4286 } else { 4287 ext.set(AuthorityInfoAccessExtension.NAME, 4288 new AuthorityInfoAccessExtension(accessDescriptions)); 4289 } 4290 } else { 4291 throw new Exception(rb.getString 4292 ("Illegal.value.") + extstr); 4293 } 4294 break; 4295 case 8: // CRL, experimental, only support 1 distributionpoint 4296 if(value != null) { 4297 String[] ps = value.split(","); 4298 GeneralNames gnames = new GeneralNames(); 4299 for(String item: ps) { 4300 colonpos = item.indexOf(':'); 4301 if (colonpos < 0) { 4302 throw new Exception("Illegal item " + item + " in " + extstr); 4303 } 4304 String t = item.substring(0, colonpos); 4305 String v = item.substring(colonpos+1); 4306 gnames.add(createGeneralName(t, v)); 4307 } 4308 ext.set(CRLDistributionPointsExtension.NAME, 4309 new CRLDistributionPointsExtension( 4310 isCritical, Collections.singletonList( 4311 new DistributionPoint(gnames, null, null)))); 4312 } else { 4313 throw new Exception(rb.getString 4314 ("Illegal.value.") + extstr); 4315 } 4316 break; 4317 case -1: 4318 ObjectIdentifier oid = new ObjectIdentifier(name); 4319 byte[] data = null; 4320 if (value != null) { 4321 data = new byte[value.length() / 2 + 1]; 4322 int pos = 0; 4323 for (char c: value.toCharArray()) { 4324 int hex; 4325 if (c >= '0' && c <= '9') { 4326 hex = c - '0' ; 4327 } else if (c >= 'A' && c <= 'F') { 4328 hex = c - 'A' + 10; 4329 } else if (c >= 'a' && c <= 'f') { 4330 hex = c - 'a' + 10; 4331 } else { 4332 continue; 4333 } 4334 if (pos % 2 == 0) { 4335 data[pos/2] = (byte)(hex << 4); 4336 } else { 4337 data[pos/2] += hex; 4338 } 4339 pos++; 4340 } 4341 if (pos % 2 != 0) { 4342 throw new Exception(rb.getString( 4343 "Odd.number.of.hex.digits.found.") + extstr); 4344 } 4345 data = Arrays.copyOf(data, pos/2); 4346 } else { 4347 data = new byte[0]; 4348 } 4349 ext.set(oid.toString(), new Extension(oid, isCritical, 4350 new DerValue(DerValue.tag_OctetString, data) 4351 .toByteArray())); 4352 break; 4353 default: 4354 throw new Exception(rb.getString( 4355 "Unknown.extension.type.") + extstr); 4356 } 4357 } 4358 // always non-critical 4359 ext.set(SubjectKeyIdentifierExtension.NAME, 4360 new SubjectKeyIdentifierExtension( 4361 new KeyIdentifier(pkey).getIdentifier())); 4362 if (akey != null && !pkey.equals(akey)) { 4363 ext.set(AuthorityKeyIdentifierExtension.NAME, 4364 new AuthorityKeyIdentifierExtension( 4365 new KeyIdentifier(akey), null, null)); 4366 } 4367 } catch(IOException e) { 4368 throw new RuntimeException(e); 4369 } 4370 return ext; 4371 } 4372 4373 private boolean isTrustedCert(Certificate cert) throws KeyStoreException { 4374 if (caks != null && caks.getCertificateAlias(cert) != null) { 4375 return true; 4376 } else { 4377 String inKS = keyStore.getCertificateAlias(cert); 4378 return inKS != null && keyStore.isCertificateEntry(inKS); 4379 } 4380 } 4381 4382 private void checkWeak(String label, String sigAlg, Key key) { 4383 if (sigAlg != null) { 4384 if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) { 4385 weakWarnings.add(String.format( 4386 rb.getString("whose.sigalg.disabled"), label, sigAlg)); 4387 } else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) { 4388 weakWarnings.add(String.format( 4389 rb.getString("whose.sigalg.weak"), label, sigAlg)); 4390 } 4391 } 4392 4393 if (key != null) { 4394 if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 4395 weakWarnings.add(String.format( 4396 rb.getString("whose.key.disabled"), label, 4397 String.format(rb.getString("key.bit"), 4398 KeyUtil.getKeySize(key), fullDisplayAlgName(key)))); 4399 } else if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 4400 weakWarnings.add(String.format( 4401 rb.getString("whose.key.weak"), label, 4402 String.format(rb.getString("key.bit"), 4403 KeyUtil.getKeySize(key), fullDisplayAlgName(key)))); 4404 } 4405 } 4406 } 4407 4408 private void checkWeak(String label, Certificate[] certs) 4409 throws KeyStoreException { 4410 for (int i = 0; i < certs.length; i++) { 4411 Certificate cert = certs[i]; 4412 if (cert instanceof X509Certificate) { 4413 X509Certificate xc = (X509Certificate)cert; 4414 String fullLabel = label; 4415 if (certs.length > 1) { 4416 fullLabel = oneInMany(label, i, certs.length); 4417 } 4418 checkWeak(fullLabel, xc); 4419 } 4420 } 4421 } 4422 4423 private void checkWeak(String label, Certificate cert) 4424 throws KeyStoreException { 4425 if (cert instanceof X509Certificate) { 4426 X509Certificate xc = (X509Certificate)cert; 4427 // No need to check the sigalg of a trust anchor 4428 String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName(); 4429 checkWeak(label, sigAlg, xc.getPublicKey()); 4430 } 4431 } 4432 4433 private void checkWeak(String label, PKCS10 p10) { 4434 checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo()); 4435 } 4436 4437 private void checkWeak(String label, CRL crl, Key key) { 4438 if (crl instanceof X509CRLImpl) { 4439 X509CRLImpl impl = (X509CRLImpl)crl; 4440 checkWeak(label, impl.getSigAlgName(), key); 4441 } 4442 } 4443 4444 private void printWeakWarnings(boolean newLine) { 4445 if (!weakWarnings.isEmpty() && !nowarn) { 4446 System.err.println("\nWarning:"); 4447 for (String warning : weakWarnings) { 4448 System.err.println(warning); 4449 } 4450 if (newLine) { 4451 // When calling before a yes/no prompt, add a new line 4452 System.err.println(); 4453 } 4454 } 4455 weakWarnings.clear(); 4456 } 4457 4458 /** 4459 * Prints the usage of this tool. 4460 */ 4461 private void usage() { 4462 if (command != null) { 4463 System.err.println("keytool " + command + 4464 rb.getString(".OPTION.")); 4465 System.err.println(); 4466 System.err.println(rb.getString(command.description)); 4467 System.err.println(); 4468 System.err.println(rb.getString("Options.")); 4469 System.err.println(); 4470 4471 // Left and right sides of the options list 4472 String[] left = new String[command.options.length]; 4473 String[] right = new String[command.options.length]; 4474 4475 // Check if there's an unknown option 4476 boolean found = false; 4477 4478 // Length of left side of options list 4479 int lenLeft = 0; 4480 for (int j=0; j<left.length; j++) { 4481 Option opt = command.options[j]; 4482 left[j] = opt.toString(); 4483 if (opt.arg != null) left[j] += " " + opt.arg; 4484 if (left[j].length() > lenLeft) { 4485 lenLeft = left[j].length(); 4486 } 4487 right[j] = rb.getString(opt.description); 4488 } 4489 for (int j=0; j<left.length; j++) { 4490 System.err.printf(" %-" + lenLeft + "s %s\n", 4491 left[j], right[j]); 4492 } 4493 System.err.println(); 4494 System.err.println(rb.getString( 4495 "Use.keytool.help.for.all.available.commands")); 4496 } else { 4497 System.err.println(rb.getString( 4498 "Key.and.Certificate.Management.Tool")); 4499 System.err.println(); 4500 System.err.println(rb.getString("Commands.")); 4501 System.err.println(); 4502 for (Command c: Command.values()) { 4503 if (c == KEYCLONE) break; 4504 System.err.printf(" %-20s%s\n", c, rb.getString(c.description)); 4505 } 4506 System.err.println(); 4507 System.err.println(rb.getString( 4508 "Use.keytool.command.name.help.for.usage.of.command.name")); 4509 } 4510 } 4511 4512 private void tinyHelp() { 4513 usage(); 4514 if (debug) { 4515 throw new RuntimeException("NO BIG ERROR, SORRY"); 4516 } else { 4517 System.exit(1); 4518 } 4519 } 4520 4521 private void errorNeedArgument(String flag) { 4522 Object[] source = {flag}; 4523 System.err.println(new MessageFormat( 4524 rb.getString("Command.option.flag.needs.an.argument.")).format(source)); 4525 tinyHelp(); 4526 } 4527 4528 private char[] getPass(String modifier, String arg) { 4529 char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb); 4530 if (output != null) return output; 4531 tinyHelp(); 4532 return null; // Useless, tinyHelp() already exits. 4533 } 4534 } 4535 4536 // This class is exactly the same as com.sun.tools.javac.util.Pair, 4537 // it's copied here since the original one is not included in JRE. 4538 class Pair<A, B> { 4539 4540 public final A fst; 4541 public final B snd; 4542 4543 public Pair(A fst, B snd) { 4544 this.fst = fst; 4545 this.snd = snd; 4546 } 4547 4548 public String toString() { 4549 return "Pair[" + fst + "," + snd + "]"; 4550 } 4551 4552 public boolean equals(Object other) { 4553 return 4554 other instanceof Pair && 4555 Objects.equals(fst, ((Pair)other).fst) && 4556 Objects.equals(snd, ((Pair)other).snd); 4557 } 4558 4559 public int hashCode() { 4560 if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1; 4561 else if (snd == null) return fst.hashCode() + 2; 4562 else return fst.hashCode() * 17 + snd.hashCode(); 4563 } 4564 4565 public static <A,B> Pair<A,B> of(A a, B b) { 4566 return new Pair<>(a,b); 4567 } 4568 } 4569