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