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