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