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