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 return String.format(rb.getString("key.bit"), 3095 KeyUtil.getKeySize(key), key.getAlgorithm()); 3096 } else { 3097 return String.format(rb.getString("key.bit.weak"), 3098 KeyUtil.getKeySize(key), key.getAlgorithm()); 3099 } 3100 } 3101 3102 /** 3103 * Prints a certificate in a human readable format. 3104 */ 3105 private void printX509Cert(X509Certificate cert, PrintStream out) 3106 throws Exception 3107 { 3108 3109 MessageFormat form = new MessageFormat 3110 (rb.getString(".PATTERN.printX509Cert.with.weak")); 3111 PublicKey pkey = cert.getPublicKey(); 3112 String sigName = cert.getSigAlgName(); 3113 // No need to warn about sigalg of a trust anchor 3114 if (!isTrustedCert(cert)) { 3115 sigName = withWeak(sigName); 3116 } 3117 Object[] source = {cert.getSubjectDN().toString(), 3118 cert.getIssuerDN().toString(), 3119 cert.getSerialNumber().toString(16), 3120 cert.getNotBefore().toString(), 3121 cert.getNotAfter().toString(), 3122 getCertFingerPrint("SHA-1", cert), 3123 getCertFingerPrint("SHA-256", cert), 3124 sigName, 3125 withWeak(pkey), 3126 cert.getVersion() 3127 }; 3128 out.println(form.format(source)); 3129 3130 if (cert instanceof X509CertImpl) { 3131 X509CertImpl impl = (X509CertImpl)cert; 3132 X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME 3133 + "." + 3134 X509CertImpl.INFO); 3135 CertificateExtensions exts = (CertificateExtensions) 3136 certInfo.get(X509CertInfo.EXTENSIONS); 3137 if (exts != null) { 3138 printExtensions(rb.getString("Extensions."), exts, out); 3139 } 3140 } 3141 } 3142 3143 private static void printExtensions(String title, CertificateExtensions exts, PrintStream out) 3144 throws Exception { 3145 int extnum = 0; 3146 Iterator<Extension> i1 = exts.getAllExtensions().iterator(); 3147 Iterator<Extension> i2 = exts.getUnparseableExtensions().values().iterator(); 3148 while (i1.hasNext() || i2.hasNext()) { 3149 Extension ext = i1.hasNext()?i1.next():i2.next(); 3150 if (extnum == 0) { 3151 out.println(); 3152 out.println(title); 3153 out.println(); 3154 } 3155 out.print("#"+(++extnum)+": "+ ext); 3156 if (ext.getClass() == Extension.class) { 3157 byte[] v = ext.getExtensionValue(); 3158 if (v.length == 0) { 3159 out.println(rb.getString(".Empty.value.")); 3160 } else { 3161 new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out); 3162 out.println(); 3163 } 3164 } 3165 out.println(); 3166 } 3167 } 3168 3169 /** 3170 * Locates a signer for a given certificate from a given keystore and 3171 * returns the signer's certificate. 3172 * @param cert the certificate whose signer is searched, not null 3173 * @param ks the keystore to search with, not null 3174 * @return <code>cert</code> itself if it's already inside <code>ks</code>, 3175 * or a certificate inside <code>ks</code> who signs <code>cert</code>, 3176 * or null otherwise. A label is added. 3177 */ 3178 private static Pair<String,Certificate> 3179 getSigner(Certificate cert, KeyStore ks) throws Exception { 3180 if (ks.getCertificateAlias(cert) != null) { 3181 return new Pair<>("", cert); 3182 } 3183 for (Enumeration<String> aliases = ks.aliases(); 3184 aliases.hasMoreElements(); ) { 3185 String name = aliases.nextElement(); 3186 Certificate trustedCert = ks.getCertificate(name); 3187 if (trustedCert != null) { 3188 try { 3189 cert.verify(trustedCert.getPublicKey()); 3190 return new Pair<>(name, trustedCert); 3191 } catch (Exception e) { 3192 // Not verified, skip to the next one 3193 } 3194 } 3195 } 3196 return null; 3197 } 3198 3199 /** 3200 * Gets an X.500 name suitable for inclusion in a certification request. 3201 */ 3202 private X500Name getX500Name() throws IOException { 3203 BufferedReader in; 3204 in = new BufferedReader(new InputStreamReader(System.in)); 3205 String commonName = "Unknown"; 3206 String organizationalUnit = "Unknown"; 3207 String organization = "Unknown"; 3208 String city = "Unknown"; 3209 String state = "Unknown"; 3210 String country = "Unknown"; 3211 X500Name name; 3212 String userInput = null; 3213 3214 int maxRetry = 20; 3215 do { 3216 if (maxRetry-- < 0) { 3217 throw new RuntimeException(rb.getString( 3218 "Too.many.retries.program.terminated")); 3219 } 3220 commonName = inputString(in, 3221 rb.getString("What.is.your.first.and.last.name."), 3222 commonName); 3223 organizationalUnit = inputString(in, 3224 rb.getString 3225 ("What.is.the.name.of.your.organizational.unit."), 3226 organizationalUnit); 3227 organization = inputString(in, 3228 rb.getString("What.is.the.name.of.your.organization."), 3229 organization); 3230 city = inputString(in, 3231 rb.getString("What.is.the.name.of.your.City.or.Locality."), 3232 city); 3233 state = inputString(in, 3234 rb.getString("What.is.the.name.of.your.State.or.Province."), 3235 state); 3236 country = inputString(in, 3237 rb.getString 3238 ("What.is.the.two.letter.country.code.for.this.unit."), 3239 country); 3240 name = new X500Name(commonName, organizationalUnit, organization, 3241 city, state, country); 3242 MessageFormat form = new MessageFormat 3243 (rb.getString("Is.name.correct.")); 3244 Object[] source = {name}; 3245 userInput = inputString 3246 (in, form.format(source), rb.getString("no")); 3247 } while (collator.compare(userInput, rb.getString("yes")) != 0 && 3248 collator.compare(userInput, rb.getString("y")) != 0); 3249 3250 System.err.println(); 3251 return name; 3252 } 3253 3254 private String inputString(BufferedReader in, String prompt, 3255 String defaultValue) 3256 throws IOException 3257 { 3258 System.err.println(prompt); 3259 MessageFormat form = new MessageFormat 3260 (rb.getString(".defaultValue.")); 3261 Object[] source = {defaultValue}; 3262 System.err.print(form.format(source)); 3263 System.err.flush(); 3264 3265 String value = in.readLine(); 3266 if (value == null || collator.compare(value, "") == 0) { 3267 value = defaultValue; 3268 } 3269 return value; 3270 } 3271 3272 /** 3273 * Writes an X.509 certificate in base64 or binary encoding to an output 3274 * stream. 3275 */ 3276 private void dumpCert(Certificate cert, PrintStream out) 3277 throws IOException, CertificateException 3278 { 3279 if (rfc) { 3280 out.println(X509Factory.BEGIN_CERT); 3281 out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(cert.getEncoded())); 3282 out.println(X509Factory.END_CERT); 3283 } else { 3284 out.write(cert.getEncoded()); // binary 3285 } 3286 } 3287 3288 /** 3289 * Converts a byte to hex digit and writes to the supplied buffer 3290 */ 3291 private void byte2hex(byte b, StringBuffer buf) { 3292 char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', 3293 '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 3294 int high = ((b & 0xf0) >> 4); 3295 int low = (b & 0x0f); 3296 buf.append(hexChars[high]); 3297 buf.append(hexChars[low]); 3298 } 3299 3300 /** 3301 * Converts a byte array to hex string 3302 */ 3303 private String toHexString(byte[] block) { 3304 StringBuffer buf = new StringBuffer(); 3305 int len = block.length; 3306 for (int i = 0; i < len; i++) { 3307 byte2hex(block[i], buf); 3308 if (i < len-1) { 3309 buf.append(":"); 3310 } 3311 } 3312 return buf.toString(); 3313 } 3314 3315 /** 3316 * Recovers (private) key associated with given alias. 3317 * 3318 * @return an array of objects, where the 1st element in the array is the 3319 * recovered private key, and the 2nd element is the password used to 3320 * recover it. 3321 */ 3322 private Pair<Key,char[]> recoverKey(String alias, char[] storePass, 3323 char[] keyPass) 3324 throws Exception 3325 { 3326 Key key = null; 3327 3328 if (KeyStoreUtil.isWindowsKeyStore(storetype)) { 3329 key = keyStore.getKey(alias, null); 3330 return Pair.of(key, null); 3331 } 3332 3333 if (keyStore.containsAlias(alias) == false) { 3334 MessageFormat form = new MessageFormat 3335 (rb.getString("Alias.alias.does.not.exist")); 3336 Object[] source = {alias}; 3337 throw new Exception(form.format(source)); 3338 } 3339 if (!keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) && 3340 !keyStore.entryInstanceOf(alias, KeyStore.SecretKeyEntry.class)) { 3341 MessageFormat form = new MessageFormat 3342 (rb.getString("Alias.alias.has.no.key")); 3343 Object[] source = {alias}; 3344 throw new Exception(form.format(source)); 3345 } 3346 3347 if (keyPass == null) { 3348 // Try to recover the key using the keystore password 3349 try { 3350 key = keyStore.getKey(alias, storePass); 3351 3352 keyPass = storePass; 3353 passwords.add(keyPass); 3354 } catch (UnrecoverableKeyException e) { 3355 // Did not work out, so prompt user for key password 3356 if (!token) { 3357 keyPass = getKeyPasswd(alias, null, null); 3358 key = keyStore.getKey(alias, keyPass); 3359 } else { 3360 throw e; 3361 } 3362 } 3363 } else { 3364 key = keyStore.getKey(alias, keyPass); 3365 } 3366 3367 return Pair.of(key, keyPass); 3368 } 3369 3370 /** 3371 * Recovers entry associated with given alias. 3372 * 3373 * @return an array of objects, where the 1st element in the array is the 3374 * recovered entry, and the 2nd element is the password used to 3375 * recover it (null if no password). 3376 */ 3377 private Pair<Entry,char[]> recoverEntry(KeyStore ks, 3378 String alias, 3379 char[] pstore, 3380 char[] pkey) throws Exception { 3381 3382 if (ks.containsAlias(alias) == false) { 3383 MessageFormat form = new MessageFormat 3384 (rb.getString("Alias.alias.does.not.exist")); 3385 Object[] source = {alias}; 3386 throw new Exception(form.format(source)); 3387 } 3388 3389 PasswordProtection pp = null; 3390 Entry entry; 3391 3392 try { 3393 // First attempt to access entry without key password 3394 // (PKCS11 entry or trusted certificate entry, for example) 3395 3396 entry = ks.getEntry(alias, pp); 3397 pkey = null; 3398 } catch (UnrecoverableEntryException une) { 3399 3400 if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) || 3401 KeyStoreUtil.isWindowsKeyStore(ks.getType())) { 3402 // should not happen, but a possibility 3403 throw une; 3404 } 3405 3406 // entry is protected 3407 3408 if (pkey != null) { 3409 3410 // try provided key password 3411 3412 pp = new PasswordProtection(pkey); 3413 entry = ks.getEntry(alias, pp); 3414 3415 } else { 3416 3417 // try store pass 3418 3419 try { 3420 pp = new PasswordProtection(pstore); 3421 entry = ks.getEntry(alias, pp); 3422 pkey = pstore; 3423 } catch (UnrecoverableEntryException une2) { 3424 if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) { 3425 3426 // P12 keystore currently does not support separate 3427 // store and entry passwords 3428 3429 throw une2; 3430 } else { 3431 3432 // prompt for entry password 3433 3434 pkey = getKeyPasswd(alias, null, null); 3435 pp = new PasswordProtection(pkey); 3436 entry = ks.getEntry(alias, pp); 3437 } 3438 } 3439 } 3440 } 3441 3442 return Pair.of(entry, pkey); 3443 } 3444 /** 3445 * Gets the requested finger print of the certificate. 3446 */ 3447 private String getCertFingerPrint(String mdAlg, Certificate cert) 3448 throws Exception 3449 { 3450 byte[] encCertInfo = cert.getEncoded(); 3451 MessageDigest md = MessageDigest.getInstance(mdAlg); 3452 byte[] digest = md.digest(encCertInfo); 3453 return toHexString(digest); 3454 } 3455 3456 /** 3457 * Prints warning about missing integrity check. 3458 */ 3459 private void printNoIntegrityWarning() { 3460 System.err.println(); 3461 System.err.println(rb.getString 3462 (".WARNING.WARNING.WARNING.")); 3463 System.err.println(rb.getString 3464 (".The.integrity.of.the.information.stored.in.your.keystore.")); 3465 System.err.println(rb.getString 3466 (".WARNING.WARNING.WARNING.")); 3467 System.err.println(); 3468 } 3469 3470 /** 3471 * Validates chain in certification reply, and returns the ordered 3472 * elements of the chain (with user certificate first, and root 3473 * certificate last in the array). 3474 * 3475 * @param alias the alias name 3476 * @param userCert the user certificate of the alias 3477 * @param replyCerts the chain provided in the reply 3478 */ 3479 private Certificate[] validateReply(String alias, 3480 Certificate userCert, 3481 Certificate[] replyCerts) 3482 throws Exception 3483 { 3484 3485 checkWeak(rb.getString("reply"), replyCerts); 3486 3487 // order the certs in the reply (bottom-up). 3488 // we know that all certs in the reply are of type X.509, because 3489 // we parsed them using an X.509 certificate factory 3490 int i; 3491 PublicKey userPubKey = userCert.getPublicKey(); 3492 for (i=0; i<replyCerts.length; i++) { 3493 if (userPubKey.equals(replyCerts[i].getPublicKey())) { 3494 break; 3495 } 3496 } 3497 if (i == replyCerts.length) { 3498 MessageFormat form = new MessageFormat(rb.getString 3499 ("Certificate.reply.does.not.contain.public.key.for.alias.")); 3500 Object[] source = {alias}; 3501 throw new Exception(form.format(source)); 3502 } 3503 3504 Certificate tmpCert = replyCerts[0]; 3505 replyCerts[0] = replyCerts[i]; 3506 replyCerts[i] = tmpCert; 3507 3508 X509Certificate thisCert = (X509Certificate)replyCerts[0]; 3509 3510 for (i=1; i < replyCerts.length-1; i++) { 3511 // find a cert in the reply who signs thisCert 3512 int j; 3513 for (j=i; j<replyCerts.length; j++) { 3514 if (KeyStoreUtil.signedBy(thisCert, (X509Certificate)replyCerts[j])) { 3515 tmpCert = replyCerts[i]; 3516 replyCerts[i] = replyCerts[j]; 3517 replyCerts[j] = tmpCert; 3518 thisCert = (X509Certificate)replyCerts[i]; 3519 break; 3520 } 3521 } 3522 if (j == replyCerts.length) { 3523 throw new Exception 3524 (rb.getString("Incomplete.certificate.chain.in.reply")); 3525 } 3526 } 3527 3528 if (noprompt) { 3529 return replyCerts; 3530 } 3531 3532 // do we trust the cert at the top? 3533 Certificate topCert = replyCerts[replyCerts.length-1]; 3534 boolean fromKeyStore = true; 3535 Pair<String,Certificate> root = getSigner(topCert, keyStore); 3536 if (root == null && trustcacerts && caks != null) { 3537 root = getSigner(topCert, caks); 3538 fromKeyStore = false; 3539 } 3540 if (root == null) { 3541 System.err.println(); 3542 System.err.println 3543 (rb.getString("Top.level.certificate.in.reply.")); 3544 printX509Cert((X509Certificate)topCert, System.out); 3545 System.err.println(); 3546 System.err.print(rb.getString(".is.not.trusted.")); 3547 printWeakWarnings(true); 3548 String reply = getYesNoReply 3549 (rb.getString("Install.reply.anyway.no.")); 3550 if ("NO".equals(reply)) { 3551 return null; 3552 } 3553 } else { 3554 if (root.snd != topCert) { 3555 // append the root CA cert to the chain 3556 Certificate[] tmpCerts = 3557 new Certificate[replyCerts.length+1]; 3558 System.arraycopy(replyCerts, 0, tmpCerts, 0, 3559 replyCerts.length); 3560 tmpCerts[tmpCerts.length-1] = root.snd; 3561 replyCerts = tmpCerts; 3562 checkWeak(String.format(rb.getString(fromKeyStore ? 3563 "alias.in.keystore" : 3564 "alias.in.cacerts"), 3565 root.fst), 3566 root.snd); 3567 } 3568 } 3569 return replyCerts; 3570 } 3571 3572 /** 3573 * Establishes a certificate chain (using trusted certificates in the 3574 * keystore and cacerts), starting with the reply (certToVerify) 3575 * and ending at a self-signed certificate found in the keystore. 3576 * 3577 * @param userCert optional existing certificate, mostly likely be the 3578 * original self-signed cert created by -genkeypair. 3579 * It must have the same public key as certToVerify 3580 * but cannot be the same cert. 3581 * @param certToVerify the starting certificate to build the chain 3582 * @returns the established chain, might be null if user decides not 3583 */ 3584 private Certificate[] establishCertChain(Certificate userCert, 3585 Certificate certToVerify) 3586 throws Exception 3587 { 3588 if (userCert != null) { 3589 // Make sure that the public key of the certificate reply matches 3590 // the original public key in the keystore 3591 PublicKey origPubKey = userCert.getPublicKey(); 3592 PublicKey replyPubKey = certToVerify.getPublicKey(); 3593 if (!origPubKey.equals(replyPubKey)) { 3594 throw new Exception(rb.getString 3595 ("Public.keys.in.reply.and.keystore.don.t.match")); 3596 } 3597 3598 // If the two certs are identical, we're done: no need to import 3599 // anything 3600 if (certToVerify.equals(userCert)) { 3601 throw new Exception(rb.getString 3602 ("Certificate.reply.and.certificate.in.keystore.are.identical")); 3603 } 3604 } 3605 3606 // Build a hash table of all certificates in the keystore. 3607 // Use the subject distinguished name as the key into the hash table. 3608 // All certificates associated with the same subject distinguished 3609 // name are stored in the same hash table entry as a vector. 3610 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null; 3611 if (keyStore.size() > 0) { 3612 certs = new Hashtable<>(11); 3613 keystorecerts2Hashtable(keyStore, certs); 3614 } 3615 if (trustcacerts) { 3616 if (caks!=null && caks.size()>0) { 3617 if (certs == null) { 3618 certs = new Hashtable<>(11); 3619 } 3620 keystorecerts2Hashtable(caks, certs); 3621 } 3622 } 3623 3624 // start building chain 3625 Vector<Pair<String,X509Certificate>> chain = new Vector<>(2); 3626 if (buildChain( 3627 new Pair<>(rb.getString("the.input"), 3628 (X509Certificate) certToVerify), 3629 chain, certs)) { 3630 for (Pair<String,X509Certificate> p : chain) { 3631 checkWeak(p.fst, p.snd); 3632 } 3633 Certificate[] newChain = 3634 new Certificate[chain.size()]; 3635 // buildChain() returns chain with self-signed root-cert first and 3636 // user-cert last, so we need to invert the chain before we store 3637 // it 3638 int j=0; 3639 for (int i=chain.size()-1; i>=0; i--) { 3640 newChain[j] = chain.elementAt(i).snd; 3641 j++; 3642 } 3643 return newChain; 3644 } else { 3645 throw new Exception 3646 (rb.getString("Failed.to.establish.chain.from.reply")); 3647 } 3648 } 3649 3650 /** 3651 * Recursively tries to establish chain from pool of certs starting from 3652 * certToVerify until a self-signed cert is found, and fill the certs found 3653 * into chain. Each cert in the chain signs the next one. 3654 * 3655 * This method is able to recover from an error, say, if certToVerify 3656 * is signed by certA but certA has no issuer in certs and itself is not 3657 * self-signed, the method can try another certB that also signs 3658 * certToVerify and look for signer of certB, etc, etc. 3659 * 3660 * Each cert in chain comes with a label showing its origin. The label is 3661 * used in the warning message when the cert is considered a risk. 3662 * 3663 * @param certToVerify the cert that needs to be verified. 3664 * @param chain the chain that's being built. 3665 * @param certs the pool of trusted certs 3666 * 3667 * @return true if successful, false otherwise. 3668 */ 3669 private boolean buildChain(Pair<String,X509Certificate> certToVerify, 3670 Vector<Pair<String,X509Certificate>> chain, 3671 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) { 3672 if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) { 3673 // reached self-signed root cert; 3674 // no verification needed because it's trusted. 3675 chain.addElement(certToVerify); 3676 return true; 3677 } 3678 3679 Principal issuer = certToVerify.snd.getIssuerDN(); 3680 3681 // Get the issuer's certificate(s) 3682 Vector<Pair<String,X509Certificate>> vec = certs.get(issuer); 3683 if (vec == null) { 3684 return false; 3685 } 3686 3687 // Try out each certificate in the vector, until we find one 3688 // whose public key verifies the signature of the certificate 3689 // in question. 3690 for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements(); 3691 issuerCerts.hasMoreElements(); ) { 3692 Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement(); 3693 PublicKey issuerPubKey = issuerCert.snd.getPublicKey(); 3694 try { 3695 certToVerify.snd.verify(issuerPubKey); 3696 } catch (Exception e) { 3697 continue; 3698 } 3699 if (buildChain(issuerCert, chain, certs)) { 3700 chain.addElement(certToVerify); 3701 return true; 3702 } 3703 } 3704 return false; 3705 } 3706 3707 /** 3708 * Prompts user for yes/no decision. 3709 * 3710 * @return the user's decision, can only be "YES" or "NO" 3711 */ 3712 private String getYesNoReply(String prompt) 3713 throws IOException 3714 { 3715 String reply = null; 3716 int maxRetry = 20; 3717 do { 3718 if (maxRetry-- < 0) { 3719 throw new RuntimeException(rb.getString( 3720 "Too.many.retries.program.terminated")); 3721 } 3722 System.err.print(prompt); 3723 System.err.flush(); 3724 reply = (new BufferedReader(new InputStreamReader 3725 (System.in))).readLine(); 3726 if (collator.compare(reply, "") == 0 || 3727 collator.compare(reply, rb.getString("n")) == 0 || 3728 collator.compare(reply, rb.getString("no")) == 0) { 3729 reply = "NO"; 3730 } else if (collator.compare(reply, rb.getString("y")) == 0 || 3731 collator.compare(reply, rb.getString("yes")) == 0) { 3732 reply = "YES"; 3733 } else { 3734 System.err.println(rb.getString("Wrong.answer.try.again")); 3735 reply = null; 3736 } 3737 } while (reply == null); 3738 return reply; 3739 } 3740 3741 /** 3742 * Stores the (leaf) certificates of a keystore in a hashtable. 3743 * All certs belonging to the same CA are stored in a vector that 3744 * in turn is stored in the hashtable, keyed by the CA's subject DN. 3745 * Each cert comes with a string label that shows its origin and alias. 3746 */ 3747 private void keystorecerts2Hashtable(KeyStore ks, 3748 Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash) 3749 throws Exception { 3750 3751 for (Enumeration<String> aliases = ks.aliases(); 3752 aliases.hasMoreElements(); ) { 3753 String alias = aliases.nextElement(); 3754 Certificate cert = ks.getCertificate(alias); 3755 if (cert != null) { 3756 Principal subjectDN = ((X509Certificate)cert).getSubjectDN(); 3757 Pair<String,X509Certificate> pair = new Pair<>( 3758 String.format( 3759 rb.getString(ks == caks ? 3760 "alias.in.cacerts" : 3761 "alias.in.keystore"), 3762 alias), 3763 (X509Certificate)cert); 3764 Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN); 3765 if (vec == null) { 3766 vec = new Vector<>(); 3767 vec.addElement(pair); 3768 } else { 3769 if (!vec.contains(pair)) { 3770 vec.addElement(pair); 3771 } 3772 } 3773 hash.put(subjectDN, vec); 3774 } 3775 } 3776 } 3777 3778 /** 3779 * Returns the issue time that's specified the -startdate option 3780 * @param s the value of -startdate option 3781 */ 3782 private static Date getStartDate(String s) throws IOException { 3783 Calendar c = new GregorianCalendar(); 3784 if (s != null) { 3785 IOException ioe = new IOException( 3786 rb.getString("Illegal.startdate.value")); 3787 int len = s.length(); 3788 if (len == 0) { 3789 throw ioe; 3790 } 3791 if (s.charAt(0) == '-' || s.charAt(0) == '+') { 3792 // Form 1: ([+-]nnn[ymdHMS])+ 3793 int start = 0; 3794 while (start < len) { 3795 int sign = 0; 3796 switch (s.charAt(start)) { 3797 case '+': sign = 1; break; 3798 case '-': sign = -1; break; 3799 default: throw ioe; 3800 } 3801 int i = start+1; 3802 for (; i<len; i++) { 3803 char ch = s.charAt(i); 3804 if (ch < '0' || ch > '9') break; 3805 } 3806 if (i == start+1) throw ioe; 3807 int number = Integer.parseInt(s.substring(start+1, i)); 3808 if (i >= len) throw ioe; 3809 int unit = 0; 3810 switch (s.charAt(i)) { 3811 case 'y': unit = Calendar.YEAR; break; 3812 case 'm': unit = Calendar.MONTH; break; 3813 case 'd': unit = Calendar.DATE; break; 3814 case 'H': unit = Calendar.HOUR; break; 3815 case 'M': unit = Calendar.MINUTE; break; 3816 case 'S': unit = Calendar.SECOND; break; 3817 default: throw ioe; 3818 } 3819 c.add(unit, sign * number); 3820 start = i + 1; 3821 } 3822 } else { 3823 // Form 2: [yyyy/mm/dd] [HH:MM:SS] 3824 String date = null, time = null; 3825 if (len == 19) { 3826 date = s.substring(0, 10); 3827 time = s.substring(11); 3828 if (s.charAt(10) != ' ') 3829 throw ioe; 3830 } else if (len == 10) { 3831 date = s; 3832 } else if (len == 8) { 3833 time = s; 3834 } else { 3835 throw ioe; 3836 } 3837 if (date != null) { 3838 if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) { 3839 c.set(Integer.valueOf(date.substring(0, 4)), 3840 Integer.valueOf(date.substring(5, 7))-1, 3841 Integer.valueOf(date.substring(8, 10))); 3842 } else { 3843 throw ioe; 3844 } 3845 } 3846 if (time != null) { 3847 if (time.matches("\\d\\d:\\d\\d:\\d\\d")) { 3848 c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2))); 3849 c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2))); 3850 c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2))); 3851 c.set(Calendar.MILLISECOND, 0); 3852 } else { 3853 throw ioe; 3854 } 3855 } 3856 } 3857 } 3858 return c.getTime(); 3859 } 3860 3861 /** 3862 * Match a command (may be abbreviated) with a command set. 3863 * @param s the command provided 3864 * @param list the legal command set. If there is a null, commands after it 3865 * are regarded experimental, which means they are supported but their 3866 * existence should not be revealed to user. 3867 * @return the position of a single match, or -1 if none matched 3868 * @throws Exception if s is ambiguous 3869 */ 3870 private static int oneOf(String s, String... list) throws Exception { 3871 int[] match = new int[list.length]; 3872 int nmatch = 0; 3873 int experiment = Integer.MAX_VALUE; 3874 for (int i = 0; i<list.length; i++) { 3875 String one = list[i]; 3876 if (one == null) { 3877 experiment = i; 3878 continue; 3879 } 3880 if (one.toLowerCase(Locale.ENGLISH) 3881 .startsWith(s.toLowerCase(Locale.ENGLISH))) { 3882 match[nmatch++] = i; 3883 } else { 3884 StringBuffer sb = new StringBuffer(); 3885 boolean first = true; 3886 for (char c: one.toCharArray()) { 3887 if (first) { 3888 sb.append(c); 3889 first = false; 3890 } else { 3891 if (!Character.isLowerCase(c)) { 3892 sb.append(c); 3893 } 3894 } 3895 } 3896 if (sb.toString().equalsIgnoreCase(s)) { 3897 match[nmatch++] = i; 3898 } 3899 } 3900 } 3901 if (nmatch == 0) { 3902 return -1; 3903 } else if (nmatch == 1) { 3904 return match[0]; 3905 } else { 3906 // If multiple matches is in experimental commands, ignore them 3907 if (match[1] > experiment) { 3908 return match[0]; 3909 } 3910 StringBuffer sb = new StringBuffer(); 3911 MessageFormat form = new MessageFormat(rb.getString 3912 ("command.{0}.is.ambiguous.")); 3913 Object[] source = {s}; 3914 sb.append(form.format(source)); 3915 sb.append("\n "); 3916 for (int i=0; i<nmatch && match[i]<experiment; i++) { 3917 sb.append(' '); 3918 sb.append(list[match[i]]); 3919 } 3920 throw new Exception(sb.toString()); 3921 } 3922 } 3923 3924 /** 3925 * Create a GeneralName object from known types 3926 * @param t one of 5 known types 3927 * @param v value 3928 * @return which one 3929 */ 3930 private GeneralName createGeneralName(String t, String v) 3931 throws Exception { 3932 GeneralNameInterface gn; 3933 int p = oneOf(t, "EMAIL", "URI", "DNS", "IP", "OID"); 3934 if (p < 0) { 3935 throw new Exception(rb.getString( 3936 "Unrecognized.GeneralName.type.") + t); 3937 } 3938 switch (p) { 3939 case 0: gn = new RFC822Name(v); break; 3940 case 1: gn = new URIName(v); break; 3941 case 2: gn = new DNSName(v); break; 3942 case 3: gn = new IPAddressName(v); break; 3943 default: gn = new OIDName(v); break; //4 3944 } 3945 return new GeneralName(gn); 3946 } 3947 3948 private static final String[] extSupported = { 3949 "BasicConstraints", 3950 "KeyUsage", 3951 "ExtendedKeyUsage", 3952 "SubjectAlternativeName", 3953 "IssuerAlternativeName", 3954 "SubjectInfoAccess", 3955 "AuthorityInfoAccess", 3956 null, 3957 "CRLDistributionPoints", 3958 }; 3959 3960 private ObjectIdentifier findOidForExtName(String type) 3961 throws Exception { 3962 switch (oneOf(type, extSupported)) { 3963 case 0: return PKIXExtensions.BasicConstraints_Id; 3964 case 1: return PKIXExtensions.KeyUsage_Id; 3965 case 2: return PKIXExtensions.ExtendedKeyUsage_Id; 3966 case 3: return PKIXExtensions.SubjectAlternativeName_Id; 3967 case 4: return PKIXExtensions.IssuerAlternativeName_Id; 3968 case 5: return PKIXExtensions.SubjectInfoAccess_Id; 3969 case 6: return PKIXExtensions.AuthInfoAccess_Id; 3970 case 8: return PKIXExtensions.CRLDistributionPoints_Id; 3971 default: return new ObjectIdentifier(type); 3972 } 3973 } 3974 3975 /** 3976 * Create X509v3 extensions from a string representation. Note that the 3977 * SubjectKeyIdentifierExtension will always be created non-critical besides 3978 * the extension requested in the <code>extstr</code> argument. 3979 * 3980 * @param reqex the requested extensions, can be null, used for -gencert 3981 * @param ext the original extensions, can be null, used for -selfcert 3982 * @param extstrs -ext values, Read keytool doc 3983 * @param pkey the public key for the certificate 3984 * @param akey the public key for the authority (issuer) 3985 * @return the created CertificateExtensions 3986 */ 3987 private CertificateExtensions createV3Extensions( 3988 CertificateExtensions reqex, 3989 CertificateExtensions ext, 3990 List <String> extstrs, 3991 PublicKey pkey, 3992 PublicKey akey) throws Exception { 3993 3994 if (ext != null && reqex != null) { 3995 // This should not happen 3996 throw new Exception("One of request and original should be null."); 3997 } 3998 if (ext == null) ext = new CertificateExtensions(); 3999 try { 4000 // name{:critical}{=value} 4001 // Honoring requested extensions 4002 if (reqex != null) { 4003 for(String extstr: extstrs) { 4004 if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) { 4005 List<String> list = Arrays.asList( 4006 extstr.toLowerCase(Locale.ENGLISH).substring(8).split(",")); 4007 // First check existence of "all" 4008 if (list.contains("all")) { 4009 ext = reqex; // we know ext was null 4010 } 4011 // one by one for others 4012 for (String item: list) { 4013 if (item.equals("all")) continue; 4014 4015 // add or remove 4016 boolean add = true; 4017 // -1, unchanged, 0 crtical, 1 non-critical 4018 int action = -1; 4019 String type = null; 4020 if (item.startsWith("-")) { 4021 add = false; 4022 type = item.substring(1); 4023 } else { 4024 int colonpos = item.indexOf(':'); 4025 if (colonpos >= 0) { 4026 type = item.substring(0, colonpos); 4027 action = oneOf(item.substring(colonpos+1), 4028 "critical", "non-critical"); 4029 if (action == -1) { 4030 throw new Exception(rb.getString 4031 ("Illegal.value.") + item); 4032 } 4033 } 4034 } 4035 String n = reqex.getNameByOid(findOidForExtName(type)); 4036 if (add) { 4037 Extension e = reqex.get(n); 4038 if (!e.isCritical() && action == 0 4039 || e.isCritical() && action == 1) { 4040 e = Extension.newExtension( 4041 e.getExtensionId(), 4042 !e.isCritical(), 4043 e.getExtensionValue()); 4044 ext.set(n, e); 4045 } 4046 } else { 4047 ext.delete(n); 4048 } 4049 } 4050 break; 4051 } 4052 } 4053 } 4054 for(String extstr: extstrs) { 4055 String name, value; 4056 boolean isCritical = false; 4057 4058 int eqpos = extstr.indexOf('='); 4059 if (eqpos >= 0) { 4060 name = extstr.substring(0, eqpos); 4061 value = extstr.substring(eqpos+1); 4062 } else { 4063 name = extstr; 4064 value = null; 4065 } 4066 4067 int colonpos = name.indexOf(':'); 4068 if (colonpos >= 0) { 4069 if (oneOf(name.substring(colonpos+1), "critical") == 0) { 4070 isCritical = true; 4071 } 4072 name = name.substring(0, colonpos); 4073 } 4074 4075 if (name.equalsIgnoreCase("honored")) { 4076 continue; 4077 } 4078 int exttype = oneOf(name, extSupported); 4079 switch (exttype) { 4080 case 0: // BC 4081 int pathLen = -1; 4082 boolean isCA = false; 4083 if (value == null) { 4084 isCA = true; 4085 } else { 4086 try { // the abbr format 4087 pathLen = Integer.parseInt(value); 4088 isCA = true; 4089 } catch (NumberFormatException ufe) { 4090 // ca:true,pathlen:1 4091 for (String part: value.split(",")) { 4092 String[] nv = part.split(":"); 4093 if (nv.length != 2) { 4094 throw new Exception(rb.getString 4095 ("Illegal.value.") + extstr); 4096 } else { 4097 if (nv[0].equalsIgnoreCase("ca")) { 4098 isCA = Boolean.parseBoolean(nv[1]); 4099 } else if (nv[0].equalsIgnoreCase("pathlen")) { 4100 pathLen = Integer.parseInt(nv[1]); 4101 } else { 4102 throw new Exception(rb.getString 4103 ("Illegal.value.") + extstr); 4104 } 4105 } 4106 } 4107 } 4108 } 4109 ext.set(BasicConstraintsExtension.NAME, 4110 new BasicConstraintsExtension(isCritical, isCA, 4111 pathLen)); 4112 break; 4113 case 1: // KU 4114 if(value != null) { 4115 boolean[] ok = new boolean[9]; 4116 for (String s: value.split(",")) { 4117 int p = oneOf(s, 4118 "digitalSignature", // (0), 4119 "nonRepudiation", // (1) 4120 "keyEncipherment", // (2), 4121 "dataEncipherment", // (3), 4122 "keyAgreement", // (4), 4123 "keyCertSign", // (5), 4124 "cRLSign", // (6), 4125 "encipherOnly", // (7), 4126 "decipherOnly", // (8) 4127 "contentCommitment" // also (1) 4128 ); 4129 if (p < 0) { 4130 throw new Exception(rb.getString("Unknown.keyUsage.type.") + s); 4131 } 4132 if (p == 9) p = 1; 4133 ok[p] = true; 4134 } 4135 KeyUsageExtension kue = new KeyUsageExtension(ok); 4136 // The above KeyUsageExtension constructor does not 4137 // allow isCritical value, so... 4138 ext.set(KeyUsageExtension.NAME, Extension.newExtension( 4139 kue.getExtensionId(), 4140 isCritical, 4141 kue.getExtensionValue())); 4142 } else { 4143 throw new Exception(rb.getString 4144 ("Illegal.value.") + extstr); 4145 } 4146 break; 4147 case 2: // EKU 4148 if(value != null) { 4149 Vector<ObjectIdentifier> v = new Vector<>(); 4150 for (String s: value.split(",")) { 4151 int p = oneOf(s, 4152 "anyExtendedKeyUsage", 4153 "serverAuth", //1 4154 "clientAuth", //2 4155 "codeSigning", //3 4156 "emailProtection", //4 4157 "", //5 4158 "", //6 4159 "", //7 4160 "timeStamping", //8 4161 "OCSPSigning" //9 4162 ); 4163 if (p < 0) { 4164 try { 4165 v.add(new ObjectIdentifier(s)); 4166 } catch (Exception e) { 4167 throw new Exception(rb.getString( 4168 "Unknown.extendedkeyUsage.type.") + s); 4169 } 4170 } else if (p == 0) { 4171 v.add(new ObjectIdentifier("2.5.29.37.0")); 4172 } else { 4173 v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p)); 4174 } 4175 } 4176 ext.set(ExtendedKeyUsageExtension.NAME, 4177 new ExtendedKeyUsageExtension(isCritical, v)); 4178 } else { 4179 throw new Exception(rb.getString 4180 ("Illegal.value.") + extstr); 4181 } 4182 break; 4183 case 3: // SAN 4184 case 4: // IAN 4185 if(value != null) { 4186 String[] ps = value.split(","); 4187 GeneralNames gnames = new GeneralNames(); 4188 for(String item: ps) { 4189 colonpos = item.indexOf(':'); 4190 if (colonpos < 0) { 4191 throw new Exception("Illegal item " + item + " in " + extstr); 4192 } 4193 String t = item.substring(0, colonpos); 4194 String v = item.substring(colonpos+1); 4195 gnames.add(createGeneralName(t, v)); 4196 } 4197 if (exttype == 3) { 4198 ext.set(SubjectAlternativeNameExtension.NAME, 4199 new SubjectAlternativeNameExtension( 4200 isCritical, gnames)); 4201 } else { 4202 ext.set(IssuerAlternativeNameExtension.NAME, 4203 new IssuerAlternativeNameExtension( 4204 isCritical, gnames)); 4205 } 4206 } else { 4207 throw new Exception(rb.getString 4208 ("Illegal.value.") + extstr); 4209 } 4210 break; 4211 case 5: // SIA, always non-critical 4212 case 6: // AIA, always non-critical 4213 if (isCritical) { 4214 throw new Exception(rb.getString( 4215 "This.extension.cannot.be.marked.as.critical.") + extstr); 4216 } 4217 if(value != null) { 4218 List<AccessDescription> accessDescriptions = 4219 new ArrayList<>(); 4220 String[] ps = value.split(","); 4221 for(String item: ps) { 4222 colonpos = item.indexOf(':'); 4223 int colonpos2 = item.indexOf(':', colonpos+1); 4224 if (colonpos < 0 || colonpos2 < 0) { 4225 throw new Exception(rb.getString 4226 ("Illegal.value.") + extstr); 4227 } 4228 String m = item.substring(0, colonpos); 4229 String t = item.substring(colonpos+1, colonpos2); 4230 String v = item.substring(colonpos2+1); 4231 int p = oneOf(m, 4232 "", 4233 "ocsp", //1 4234 "caIssuers", //2 4235 "timeStamping", //3 4236 "", 4237 "caRepository" //5 4238 ); 4239 ObjectIdentifier oid; 4240 if (p < 0) { 4241 try { 4242 oid = new ObjectIdentifier(m); 4243 } catch (Exception e) { 4244 throw new Exception(rb.getString( 4245 "Unknown.AccessDescription.type.") + m); 4246 } 4247 } else { 4248 oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p); 4249 } 4250 accessDescriptions.add(new AccessDescription( 4251 oid, createGeneralName(t, v))); 4252 } 4253 if (exttype == 5) { 4254 ext.set(SubjectInfoAccessExtension.NAME, 4255 new SubjectInfoAccessExtension(accessDescriptions)); 4256 } else { 4257 ext.set(AuthorityInfoAccessExtension.NAME, 4258 new AuthorityInfoAccessExtension(accessDescriptions)); 4259 } 4260 } else { 4261 throw new Exception(rb.getString 4262 ("Illegal.value.") + extstr); 4263 } 4264 break; 4265 case 8: // CRL, experimental, only support 1 distributionpoint 4266 if(value != null) { 4267 String[] ps = value.split(","); 4268 GeneralNames gnames = new GeneralNames(); 4269 for(String item: ps) { 4270 colonpos = item.indexOf(':'); 4271 if (colonpos < 0) { 4272 throw new Exception("Illegal item " + item + " in " + extstr); 4273 } 4274 String t = item.substring(0, colonpos); 4275 String v = item.substring(colonpos+1); 4276 gnames.add(createGeneralName(t, v)); 4277 } 4278 ext.set(CRLDistributionPointsExtension.NAME, 4279 new CRLDistributionPointsExtension( 4280 isCritical, Collections.singletonList( 4281 new DistributionPoint(gnames, null, null)))); 4282 } else { 4283 throw new Exception(rb.getString 4284 ("Illegal.value.") + extstr); 4285 } 4286 break; 4287 case -1: 4288 ObjectIdentifier oid = new ObjectIdentifier(name); 4289 byte[] data = null; 4290 if (value != null) { 4291 data = new byte[value.length() / 2 + 1]; 4292 int pos = 0; 4293 for (char c: value.toCharArray()) { 4294 int hex; 4295 if (c >= '0' && c <= '9') { 4296 hex = c - '0' ; 4297 } else if (c >= 'A' && c <= 'F') { 4298 hex = c - 'A' + 10; 4299 } else if (c >= 'a' && c <= 'f') { 4300 hex = c - 'a' + 10; 4301 } else { 4302 continue; 4303 } 4304 if (pos % 2 == 0) { 4305 data[pos/2] = (byte)(hex << 4); 4306 } else { 4307 data[pos/2] += hex; 4308 } 4309 pos++; 4310 } 4311 if (pos % 2 != 0) { 4312 throw new Exception(rb.getString( 4313 "Odd.number.of.hex.digits.found.") + extstr); 4314 } 4315 data = Arrays.copyOf(data, pos/2); 4316 } else { 4317 data = new byte[0]; 4318 } 4319 ext.set(oid.toString(), new Extension(oid, isCritical, 4320 new DerValue(DerValue.tag_OctetString, data) 4321 .toByteArray())); 4322 break; 4323 default: 4324 throw new Exception(rb.getString( 4325 "Unknown.extension.type.") + extstr); 4326 } 4327 } 4328 // always non-critical 4329 ext.set(SubjectKeyIdentifierExtension.NAME, 4330 new SubjectKeyIdentifierExtension( 4331 new KeyIdentifier(pkey).getIdentifier())); 4332 if (akey != null && !pkey.equals(akey)) { 4333 ext.set(AuthorityKeyIdentifierExtension.NAME, 4334 new AuthorityKeyIdentifierExtension( 4335 new KeyIdentifier(akey), null, null)); 4336 } 4337 } catch(IOException e) { 4338 throw new RuntimeException(e); 4339 } 4340 return ext; 4341 } 4342 4343 private boolean isTrustedCert(Certificate cert) throws KeyStoreException { 4344 if (caks != null && caks.getCertificateAlias(cert) != null) { 4345 return true; 4346 } else { 4347 String inKS = keyStore.getCertificateAlias(cert); 4348 return inKS != null && keyStore.isCertificateEntry(inKS); 4349 } 4350 } 4351 4352 private void checkWeak(String label, String sigAlg, Key key) { 4353 4354 if (sigAlg != null && !DISABLED_CHECK.permits( 4355 SIG_PRIMITIVE_SET, sigAlg, null)) { 4356 weakWarnings.add(String.format( 4357 rb.getString("whose.sigalg.risk"), label, sigAlg)); 4358 } 4359 if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 4360 weakWarnings.add(String.format( 4361 rb.getString("whose.key.risk"), 4362 label, 4363 String.format(rb.getString("key.bit"), 4364 KeyUtil.getKeySize(key), key.getAlgorithm()))); 4365 } 4366 } 4367 4368 private void checkWeak(String label, Certificate[] certs) 4369 throws KeyStoreException { 4370 for (int i = 0; i < certs.length; i++) { 4371 Certificate cert = certs[i]; 4372 if (cert instanceof X509Certificate) { 4373 X509Certificate xc = (X509Certificate)cert; 4374 String fullLabel = label; 4375 if (certs.length > 1) { 4376 fullLabel = oneInMany(label, i, certs.length); 4377 } 4378 checkWeak(fullLabel, xc); 4379 } 4380 } 4381 } 4382 4383 private void checkWeak(String label, Certificate cert) 4384 throws KeyStoreException { 4385 if (cert instanceof X509Certificate) { 4386 X509Certificate xc = (X509Certificate)cert; 4387 // No need to check the sigalg of a trust anchor 4388 String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName(); 4389 checkWeak(label, sigAlg, xc.getPublicKey()); 4390 } 4391 } 4392 4393 private void checkWeak(String label, PKCS10 p10) { 4394 checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo()); 4395 } 4396 4397 private void checkWeak(String label, CRL crl, Key key) { 4398 if (crl instanceof X509CRLImpl) { 4399 X509CRLImpl impl = (X509CRLImpl)crl; 4400 checkWeak(label, impl.getSigAlgName(), key); 4401 } 4402 } 4403 4404 private void printWeakWarnings(boolean newLine) { 4405 if (!weakWarnings.isEmpty() && !nowarn) { 4406 System.err.println("\nWarning:"); 4407 for (String warning : weakWarnings) { 4408 System.err.println(warning); 4409 } 4410 if (newLine) { 4411 // When calling before a yes/no prompt, add a new line 4412 System.err.println(); 4413 } 4414 } 4415 weakWarnings.clear(); 4416 } 4417 4418 /** 4419 * Prints the usage of this tool. 4420 */ 4421 private void usage() { 4422 if (command != null) { 4423 System.err.println("keytool " + command + 4424 rb.getString(".OPTION.")); 4425 System.err.println(); 4426 System.err.println(rb.getString(command.description)); 4427 System.err.println(); 4428 System.err.println(rb.getString("Options.")); 4429 System.err.println(); 4430 4431 // Left and right sides of the options list 4432 String[] left = new String[command.options.length]; 4433 String[] right = new String[command.options.length]; 4434 4435 // Check if there's an unknown option 4436 boolean found = false; 4437 4438 // Length of left side of options list 4439 int lenLeft = 0; 4440 for (int j=0; j<left.length; j++) { 4441 Option opt = command.options[j]; 4442 left[j] = opt.toString(); 4443 if (opt.arg != null) left[j] += " " + opt.arg; 4444 if (left[j].length() > lenLeft) { 4445 lenLeft = left[j].length(); 4446 } 4447 right[j] = rb.getString(opt.description); 4448 } 4449 for (int j=0; j<left.length; j++) { 4450 System.err.printf(" %-" + lenLeft + "s %s\n", 4451 left[j], right[j]); 4452 } 4453 System.err.println(); 4454 System.err.println(rb.getString( 4455 "Use.keytool.help.for.all.available.commands")); 4456 } else { 4457 System.err.println(rb.getString( 4458 "Key.and.Certificate.Management.Tool")); 4459 System.err.println(); 4460 System.err.println(rb.getString("Commands.")); 4461 System.err.println(); 4462 for (Command c: Command.values()) { 4463 if (c == KEYCLONE) break; 4464 System.err.printf(" %-20s%s\n", c, rb.getString(c.description)); 4465 } 4466 System.err.println(); 4467 System.err.println(rb.getString( 4468 "Use.keytool.command.name.help.for.usage.of.command.name")); 4469 } 4470 } 4471 4472 private void tinyHelp() { 4473 usage(); 4474 if (debug) { 4475 throw new RuntimeException("NO BIG ERROR, SORRY"); 4476 } else { 4477 System.exit(1); 4478 } 4479 } 4480 4481 private void errorNeedArgument(String flag) { 4482 Object[] source = {flag}; 4483 System.err.println(new MessageFormat( 4484 rb.getString("Command.option.flag.needs.an.argument.")).format(source)); 4485 tinyHelp(); 4486 } 4487 4488 private char[] getPass(String modifier, String arg) { 4489 char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb); 4490 if (output != null) return output; 4491 tinyHelp(); 4492 return null; // Useless, tinyHelp() already exits. 4493 } 4494 } 4495 4496 // This class is exactly the same as com.sun.tools.javac.util.Pair, 4497 // it's copied here since the original one is not included in JRE. 4498 class Pair<A, B> { 4499 4500 public final A fst; 4501 public final B snd; 4502 4503 public Pair(A fst, B snd) { 4504 this.fst = fst; 4505 this.snd = snd; 4506 } 4507 4508 public String toString() { 4509 return "Pair[" + fst + "," + snd + "]"; 4510 } 4511 4512 public boolean equals(Object other) { 4513 return 4514 other instanceof Pair && 4515 Objects.equals(fst, ((Pair)other).fst) && 4516 Objects.equals(snd, ((Pair)other).snd); 4517 } 4518 4519 public int hashCode() { 4520 if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1; 4521 else if (snd == null) return fst.hashCode() + 2; 4522 else return fst.hashCode() * 17 + snd.hashCode(); 4523 } 4524 4525 public static <A,B> Pair<A,B> of(A a, B b) { 4526 return new Pair<>(a,b); 4527 } 4528 } 4529