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