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