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