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