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