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