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