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