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