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