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