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