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