001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.util; 028 029 030 031 import java.io.ByteArrayOutputStream; 032 import java.io.FileInputStream; 033 import java.io.InputStream; 034 import java.io.IOException; 035 import java.io.File; 036 import java.io.OutputStream; 037 import java.security.KeyStore; 038 import java.security.KeyStoreException; 039 import java.security.cert.Certificate; 040 import java.util.ArrayList; 041 import java.util.Enumeration; 042 043 import org.opends.server.types.OperatingSystem; 044 045 046 /** 047 * This class provides an interface for generating self-signed certificates and 048 * certificate signing requests, and for importing, exporting, and deleting 049 * certificates from a key store. It supports JKS, PKCS11, and PKCS12 key store 050 * types. 051 * <BR><BR> 052 * Note that for some operations, particularly those that require updating the 053 * contents of a key store (including generating certificates and/or certificate 054 * signing requests, importing certificates, or removing certificates), this 055 * class relies on the keytool utility provided with Sun's implementation of the 056 * Java runtime environment. It will perform the associated operations by 057 * invoking the appropriate command. It is possible that the keytool command 058 * will not exist in all Java runtime environments, especially those not created 059 * by Sun. In those cases, it will not be possible to invoke operations that 060 * require altering the contents of the key store. Therefore, it is strongly 061 * recommended that any code that may want to make use of this facility should 062 * first call {@code mayUseCertificateManager} and if it returns {@code false} 063 * the caller should gracefully degrade and suggest that the user perform the 064 * operation manually. 065 */ 066 @org.opends.server.types.PublicAPI( 067 stability=org.opends.server.types.StabilityLevel.VOLATILE, 068 mayInstantiate=true, 069 mayExtend=false, 070 mayInvoke=true) 071 public final class CertificateManager 072 { 073 /** 074 * The path to the keytool command, which will be required to perform 075 * operations that modify the contents of a key store. 076 */ 077 public static final String KEYTOOL_COMMAND; 078 079 080 081 /** 082 * The key store type value that should be used for the "JKS" key store. 083 */ 084 public static final String KEY_STORE_TYPE_JKS = "JKS"; 085 086 087 088 /** 089 * The key store type value that should be used for the "PKCS11" key store. 090 */ 091 public static final String KEY_STORE_TYPE_PKCS11 = "PKCS11"; 092 093 094 095 /** 096 * The key store type value that should be used for the "PKCS12" key store. 097 */ 098 public static final String KEY_STORE_TYPE_PKCS12 = "PKCS12"; 099 100 101 102 /** 103 * The key store path value that must be used in conjunction with the PKCS11 104 * key store type. 105 */ 106 public static final String KEY_STORE_PATH_PKCS11 = "NONE"; 107 108 109 110 // The parsed key store backing this certificate manager. 111 private KeyStore keyStore; 112 113 // The password that should be used to interact with the key store. 114 private String keyStorePIN; 115 116 // The path to the key store that we should be using. 117 private String keyStorePath; 118 119 // The name of the key store type we are using. 120 private String keyStoreType; 121 122 123 124 static 125 { 126 String keytoolCommand = null; 127 128 try 129 { 130 String cmd = System.getProperty("java.home") + File.separator + "bin" + 131 File.separator + "keytool"; 132 File cmdFile = new File(cmd); 133 if (cmdFile.exists()) 134 { 135 keytoolCommand = cmdFile.getAbsolutePath(); 136 } 137 else 138 { 139 cmd = cmd + ".exe"; 140 cmdFile = new File(cmd); 141 if (cmdFile.exists()) 142 { 143 keytoolCommand = cmdFile.getAbsolutePath(); 144 } 145 else 146 { 147 keytoolCommand = null; 148 } 149 } 150 } 151 catch (Exception e) 152 { 153 keytoolCommand = null; 154 } 155 156 KEYTOOL_COMMAND = SetupUtils.getScriptPath(keytoolCommand); 157 } 158 159 160 161 /** 162 * Indicates whether it is possible to use this certificate manager code to 163 * perform operations which may alter the contents of a key store. 164 * 165 * @return {@code true} if it appears that the keytool utility is available 166 * and may be used to execute commands that may alter the contents of 167 * a key store, or {@code false} if not. 168 */ 169 public static boolean mayUseCertificateManager() 170 { 171 return (KEYTOOL_COMMAND != null); 172 } 173 174 175 176 /** 177 * Creates a new certificate manager instance with the provided information. 178 * 179 * @param keyStorePath The path to the key store file, or "NONE" if the key 180 * store type is "PKCS11". For the other key store 181 * types, the file does not need to exist if a new 182 * self-signed certificate or certificate signing 183 * request is to be generated, although the directory 184 * containing the file must exist. The key store file 185 * must exist if import or export operations are to be 186 * performed. 187 * @param keyStoreType The key store type to use. It should be one of 188 * {@code KEY_STORE_TYPE_JKS}, 189 * {@code KEY_STORE_TYPE_PKCS11}, or 190 * {@code KEY_STORE_TYPE_PKCS12}. 191 * @param keyStorePIN The PIN required to access the key store. It must 192 * not be {@code null}. 193 * 194 * @throws IllegalArgumentException If any of the provided arguments is 195 * invalid. 196 * 197 * @throws NullPointerException If any of the provided arguments is 198 * {@code null}. 199 * 200 * @throws UnsupportedOperationException If it is not possible to use the 201 * certificate manager on the 202 * underlying platform. 203 */ 204 public CertificateManager(String keyStorePath, String keyStoreType, 205 String keyStorePIN) 206 throws IllegalArgumentException, NullPointerException, 207 UnsupportedOperationException 208 { 209 if ((keyStorePath == null) || (keyStorePath.length() == 0)) 210 { 211 throw new NullPointerException("keyStorePath"); 212 } 213 else if ((keyStoreType == null) || (keyStoreType.length() == 0)) 214 { 215 throw new NullPointerException("keyStoreType"); 216 } 217 else if ((keyStorePIN == null) || (keyStorePIN.length() == 0)) 218 { 219 throw new NullPointerException("keyStorePIN"); 220 } 221 222 223 if (keyStoreType.equals(KEY_STORE_TYPE_PKCS11)) 224 { 225 if (! keyStorePath.equals(KEY_STORE_PATH_PKCS11)) 226 { 227 // FIXME -- Make this an internationalizeable string. 228 throw new IllegalArgumentException("Invalid key store path for " + 229 "PKCS11 keystore -- it must be " + 230 KEY_STORE_PATH_PKCS11); 231 } 232 } 233 else if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || 234 keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) 235 { 236 File keyStoreFile = new File(keyStorePath); 237 if (keyStoreFile.exists()) 238 { 239 if (! keyStoreFile.isFile()) 240 { 241 // FIXME -- Make this an internationalizeable string. 242 throw new IllegalArgumentException("Key store path " + keyStorePath + 243 " exists but is not a file."); 244 } 245 } 246 else 247 { 248 File keyStoreDirectory = keyStoreFile.getParentFile(); 249 if ((keyStoreDirectory == null) || (! keyStoreDirectory.exists()) || 250 (! keyStoreDirectory.isDirectory())) 251 { 252 // FIXME -- Make this an internationalizeable string. 253 throw new IllegalArgumentException("Parent directory for key " + 254 "store path " + keyStorePath + " does not exist or " + 255 "is not a directory."); 256 } 257 } 258 } 259 else 260 { 261 // FIXME -- Make this an internationalizeable string. 262 throw new IllegalArgumentException("Invalid key store type -- it must " + 263 "be one of " + KEY_STORE_TYPE_JKS + ", " + 264 KEY_STORE_TYPE_PKCS11 + ", or " + KEY_STORE_TYPE_PKCS12); 265 } 266 267 268 this.keyStorePath = keyStorePath; 269 this.keyStoreType = keyStoreType; 270 this.keyStorePIN = keyStorePIN; 271 272 keyStore = null; 273 } 274 275 276 277 /** 278 * Indicates whether the provided alias is in use in the key store. 279 * 280 * @param alias The alias for which to make the determination. It must not 281 * be {@code null} or empty. 282 * 283 * @return {@code true} if the key store exist and already contains a 284 * certificate with the given alias, or {@code false} if not. 285 * 286 * @throws KeyStoreException If a problem occurs while attempting to 287 * interact with the key store. 288 * 289 * @throws NullPointerException If the provided alias is {@code null} or a 290 * zero-length string. 291 */ 292 public boolean aliasInUse(String alias) 293 throws KeyStoreException, NullPointerException 294 { 295 if ((alias == null) || (alias.length() == 0)) 296 { 297 throw new NullPointerException("alias"); 298 } 299 300 301 KeyStore keyStore = getKeyStore(); 302 if (keyStore == null) 303 { 304 return false; 305 } 306 307 return keyStore.containsAlias(alias); 308 } 309 310 311 312 /** 313 * Retrieves the aliases of the certificates in the specified key store. 314 * 315 * @return The aliases of the certificates in the specified key store, or 316 * {@code null} if the key store does not exist. 317 * 318 * @throws KeyStoreException If a problem occurs while attempting to 319 * interact with the key store. 320 */ 321 public String[] getCertificateAliases() 322 throws KeyStoreException 323 { 324 KeyStore keyStore = getKeyStore(); 325 if (keyStore == null) 326 { 327 return null; 328 } 329 330 Enumeration<String> aliasEnumeration = keyStore.aliases(); 331 if (aliasEnumeration == null) 332 { 333 return new String[0]; 334 } 335 336 ArrayList<String> aliasList = new ArrayList<String>(); 337 while (aliasEnumeration.hasMoreElements()) 338 { 339 aliasList.add(aliasEnumeration.nextElement()); 340 } 341 342 343 String[] aliases = new String[aliasList.size()]; 344 return aliasList.toArray(aliases); 345 } 346 347 348 349 /** 350 * Retrieves the certificate with the specified alias from the key store. 351 * 352 * @param alias The alias of the certificate to retrieve. It must not be 353 * {@code null} or empty. 354 * 355 * @return The requested certificate, or {@code null} if the specified 356 * certificate does not exist. 357 * 358 * @throws KeyStoreException If a problem occurs while interacting with the 359 * key store, or the key store does not exist. 360 * 361 * @throws NullPointerException If the provided alias is {@code null} or a 362 * zero-length string. 363 */ 364 public Certificate getCertificate(String alias) 365 throws KeyStoreException, NullPointerException 366 { 367 if ((alias == null) || (alias.length() == 0)) 368 { 369 throw new NullPointerException("alias"); 370 } 371 372 KeyStore keyStore = getKeyStore(); 373 if (keyStore == null) 374 { 375 // FIXME -- Make this an internationalizeable string. 376 throw new KeyStoreException("The key store does not exist."); 377 } 378 379 return keyStore.getCertificate(alias); 380 } 381 382 383 384 /** 385 * Generates a self-signed certificate using the provided information. 386 * 387 * @param alias The nickname to use for the certificate in the key 388 * store. For the server certificate, it should generally 389 * be "server-cert". It must not be {@code null} or empty. 390 * @param subjectDN The subject DN to use for the certificate. It must not 391 * be {@code null} or empty. 392 * @param validity The length of time in days that the certificate should 393 * be valid, starting from the time the certificate is 394 * generated. It must be a positive integer value. 395 * 396 * @throws IllegalArgumentException If the validity is not positive. 397 * 398 * @throws KeyStoreException If a problem occurs while actually attempting 399 * to generate the certificate in the key store. 400 * 401 * @throws NullPointerException If either the alias or subject DN is null or 402 * a zero-length string. 403 * 404 * @throws UnsupportedOperationException If it is not possible to use the 405 * keytool utility to alter the 406 * contents of the key store. 407 */ 408 public void generateSelfSignedCertificate(String alias, String subjectDN, 409 int validity) 410 throws KeyStoreException, IllegalArgumentException, 411 NullPointerException, UnsupportedOperationException 412 { 413 if ((alias == null) || (alias.length() == 0)) 414 { 415 throw new NullPointerException("alias"); 416 } 417 else if ((subjectDN == null) || (subjectDN.length() == 0)) 418 { 419 throw new NullPointerException("subjectDN"); 420 } 421 else if (validity <= 0) 422 { 423 // FIXME -- Make this an internationalizeable string. 424 throw new IllegalArgumentException("The validity must be positive."); 425 } 426 427 if (KEYTOOL_COMMAND == null) 428 { 429 // FIXME -- Make this an internationalizeable string. 430 throw new UnsupportedOperationException("The certificate manager may " + 431 "not be used to alter the contents of key stores on " + 432 "this system."); 433 } 434 435 if (aliasInUse(alias)) 436 { 437 // FIXME -- Make this an internationalizeable string. 438 throw new IllegalArgumentException("A certificate with alias " + alias + 439 " already exists in the key store."); 440 } 441 442 443 // Clear the reference to the key store, since it will be altered by 444 // invoking the KeyTool command. 445 keyStore = null; 446 447 // First, we need to run with the "-genkey" command to create the private 448 // key. 449 String[] commandElements = 450 { 451 KEYTOOL_COMMAND, 452 getGenKeyCommand(), 453 "-alias", alias, 454 "-dname", subjectDN, 455 "-keyalg", "rsa", 456 "-keystore", keyStorePath, 457 "-storetype", keyStoreType 458 }; 459 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 460 461 // Next, we need to run with the "-selfcert" command to self-sign the 462 // certificate. 463 commandElements = new String[] 464 { 465 KEYTOOL_COMMAND, 466 "-selfcert", 467 "-alias", alias, 468 "-validity", String.valueOf(validity), 469 "-keystore", keyStorePath, 470 "-storetype", keyStoreType 471 }; 472 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 473 } 474 475 476 477 /** 478 * Generates a certificate signing request (CSR) using the provided 479 * information. 480 * 481 * @param alias The nickname to use for the certificate in the key 482 * store. For the server certificate, it should generally 483 * be "server-cert". It must not be {@code null} or empty. 484 * @param subjectDN The subject DN to use for the certificate. It must not 485 * be {@code null} or empty. 486 * 487 * @return The file containing the generated certificate signing request. 488 * 489 * @throws KeyStoreException If a problem occurs while actually attempting 490 * to generate the private key in the key store or 491 * generate the certificate signing request based 492 * on that key. 493 * 494 * @throws IOException If a problem occurs while attempting to create the 495 * file to which the certificate signing request will be 496 * written. 497 * 498 * @throws NullPointerException If either the alias or subject DN is null or 499 * a zero-length string. 500 * 501 * @throws UnsupportedOperationException If it is not possible to use the 502 * keytool utility to alter the 503 * contents of the key store. 504 */ 505 public File generateCertificateSigningRequest(String alias, String subjectDN) 506 throws KeyStoreException, IOException, NullPointerException, 507 UnsupportedOperationException 508 { 509 if ((alias == null) || (alias.length() == 0)) 510 { 511 throw new NullPointerException("alias"); 512 } 513 else if ((subjectDN == null) || (subjectDN.length() == 0)) 514 { 515 throw new NullPointerException("subjectDN"); 516 } 517 518 if (KEYTOOL_COMMAND == null) 519 { 520 // FIXME -- Make this an internationalizeable string. 521 throw new UnsupportedOperationException("The certificate manager may " + 522 "not be used to alter the contents of key stores on " + 523 "this system."); 524 } 525 526 if (aliasInUse(alias)) 527 { 528 // FIXME -- Make this an internationalizeable string. 529 throw new IllegalArgumentException("A certificate with alias " + alias + 530 " already exists in the key store."); 531 } 532 533 534 // Clear the reference to the key store, since it will be altered by 535 // invoking the KeyTool command. 536 keyStore = null; 537 538 539 // First, we need to run with the "-genkey" command to create the private 540 // key. 541 String[] commandElements = 542 { 543 KEYTOOL_COMMAND, 544 getGenKeyCommand(), 545 "-alias", alias, 546 "-dname", subjectDN, 547 "-keyalg", "rsa", 548 "-keystore", keyStorePath, 549 "-storetype", keyStoreType 550 }; 551 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 552 553 // Next, we need to run with the "-certreq" command to generate the 554 // certificate signing request. 555 File csrFile = File.createTempFile("CertificateManager-", ".csr"); 556 csrFile.deleteOnExit(); 557 commandElements = new String[] 558 { 559 KEYTOOL_COMMAND, 560 "-certreq", 561 "-alias", alias, 562 "-file", csrFile.getAbsolutePath(), 563 "-keystore", keyStorePath, 564 "-storetype", keyStoreType 565 }; 566 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 567 568 return csrFile; 569 } 570 571 572 573 /** 574 * Adds the provided certificate to the key store. This may be used to 575 * associate an externally-signed certificate with an existing private key 576 * with the given alias. 577 * 578 * @param alias The alias to use for the certificate. It must not 579 * be {@code null} or empty. 580 * @param certificateFile The file containing the encoded certificate. It 581 * must not be {@code null}, and the file must exist. 582 * 583 * @throws IllegalArgumentException If the provided certificate file does 584 * not exist. 585 * 586 * @throws KeyStoreException If a problem occurs while interacting with the 587 * key store. 588 * 589 * @throws NullPointerException If the provided alias is {@code null} or a 590 * zero-length string, or the certificate file 591 * is {@code null}. 592 * 593 * @throws UnsupportedOperationException If it is not possible to use the 594 * keytool utility to alter the 595 * contents of the key store. 596 */ 597 public void addCertificate(String alias, File certificateFile) 598 throws IllegalArgumentException, KeyStoreException, 599 NullPointerException, UnsupportedOperationException 600 { 601 if ((alias == null) || (alias.length() == 0)) 602 { 603 throw new NullPointerException("alias"); 604 } 605 606 if (certificateFile == null) 607 { 608 throw new NullPointerException("certificateFile"); 609 } 610 else if ((! certificateFile.exists()) || 611 (! certificateFile.isFile())) 612 { 613 // FIXME -- Make this an internationalizeable string. 614 throw new IllegalArgumentException("Certificate file " + 615 certificateFile.getAbsolutePath() + 616 " does not exist or is not a file."); 617 } 618 619 if (KEYTOOL_COMMAND == null) 620 { 621 // FIXME -- Make this an internationalizeable string. 622 throw new UnsupportedOperationException("The certificate manager may " + 623 "not be used to alter the contents of key stores on " + 624 "this system."); 625 } 626 627 628 // Clear the reference to the key store, since it will be altered by 629 // invoking the KeyTool command. 630 keyStore = null; 631 632 633 String[] commandElements = 634 { 635 KEYTOOL_COMMAND, 636 "-import", 637 "-noprompt", 638 "-alias", alias, 639 "-file", certificateFile.getAbsolutePath(), 640 "-keystore", keyStorePath, 641 "-storetype", keyStoreType 642 }; 643 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 644 } 645 646 647 /** 648 * Removes the specified certificate from the key store. 649 * 650 * @param alias The alias to use for the certificate to remove. It must not 651 * be {@code null} or an empty string, and it must exist in 652 * the key store. 653 * 654 * @throws IllegalArgumentException If the specified certificate does not 655 * exist in the key store. 656 * 657 * @throws KeyStoreException If a problem occurs while interacting with the 658 * key store. 659 * 660 * @throws NullPointerException If the provided alias is {@code null} or a 661 * zero-length string, or the certificate file 662 * is {@code null}. 663 * 664 * @throws UnsupportedOperationException If it is not possible to use the 665 * keytool utility to alter the 666 * contents of the key store. 667 */ 668 public void removeCertificate(String alias) 669 throws IllegalArgumentException, KeyStoreException, 670 NullPointerException, UnsupportedOperationException 671 { 672 if ((alias == null) || (alias.length() == 0)) 673 { 674 throw new NullPointerException("alias"); 675 } 676 677 if (KEYTOOL_COMMAND == null) 678 { 679 // FIXME -- Make this an internationalizeable string. 680 throw new UnsupportedOperationException("The certificate manager may " + 681 "not be used to alter the contents of key stores on " + 682 "this system."); 683 } 684 685 if (! aliasInUse(alias)) 686 { 687 // FIXME -- Make this an internationalizeable string. 688 throw new IllegalArgumentException("There is no certificate with alias " + 689 alias + " in the key store."); 690 } 691 692 693 // Clear the reference to the key store, since it will be altered by 694 // invoking the KeyTool command. 695 keyStore = null; 696 697 698 String[] commandElements = 699 { 700 KEYTOOL_COMMAND, 701 "-delete", 702 "-alias", alias, 703 "-keystore", keyStorePath, 704 "-storetype", keyStoreType 705 }; 706 runKeyTool(commandElements, keyStorePIN, keyStorePIN, true); 707 } 708 709 710 711 /** 712 * Attempts to run the keytool utility with the provided arguments. 713 * 714 * @param commandElements The command and arguments to execute. The first 715 * element of the array must be the command, and the 716 * remaining elements must be the arguments. 717 * @param keyStorePassword The password of the key store. 718 * @param storePassword The password of the certificate. 719 * @param outputAcceptable Indicates whether it is acceptable for the 720 * command to generate output, as long as the exit 721 * code is zero. Some commands (like "keytool 722 * -import") may generate output even on successful 723 * completion. If the command generates output and 724 * this is {@code false}, then an exception will 725 * be thrown. 726 * 727 * @throws KeyStoreException If a problem occurs while attempting to invoke 728 * the keytool utility, if it does not exit with 729 * the expected exit code, or if any unexpected 730 * output is generated while running the tool. 731 */ 732 private void runKeyTool(String[] commandElements, String keyStorePassword, 733 String storePassword, boolean outputAcceptable) 734 throws KeyStoreException 735 { 736 String lineSeparator = System.getProperty("line.separator"); 737 if (lineSeparator == null) 738 { 739 lineSeparator = "\n"; 740 } 741 boolean keyStoreDefined; 742 File keyStoreFile = new File(keyStorePath); 743 keyStoreDefined = (keyStoreFile.exists() && (keyStoreFile.length() > 0)) || 744 KEY_STORE_TYPE_PKCS11.equals(keyStoreType); 745 746 boolean isNewKeyStorePassword = !keyStoreDefined && 747 (getGenKeyCommand().equalsIgnoreCase(commandElements[1]) || 748 "-import".equalsIgnoreCase(commandElements[1])); 749 750 boolean isNewStorePassword = 751 getGenKeyCommand().equalsIgnoreCase(commandElements[1]); 752 753 boolean askForStorePassword = 754 !"-import".equalsIgnoreCase(commandElements[1]); 755 756 try 757 { 758 ProcessBuilder processBuilder = new ProcessBuilder(commandElements); 759 processBuilder.redirectErrorStream(true); 760 761 ByteArrayOutputStream output = new ByteArrayOutputStream(); 762 byte[] buffer = new byte[1024]; 763 Process process = processBuilder.start(); 764 InputStream inputStream = process.getInputStream(); 765 OutputStream out = process.getOutputStream(); 766 if (!isJDK15() && 767 (SetupUtils.getOperatingSystem() == OperatingSystem.AIX)) 768 { 769 // This is required when using JDK 1.6 on AIX to be able to write 770 // on the OutputStream. 771 try 772 { 773 Thread.sleep(1500); 774 } catch (Throwable t) {} 775 } 776 out.write(keyStorePassword.getBytes()) ; 777 out.write(lineSeparator.getBytes()) ; 778 out.flush() ; 779 // With Java6 and above, keytool asks for the password twice. 780 if (!isJDK15() && isNewKeyStorePassword) 781 { 782 if (SetupUtils.getOperatingSystem() == OperatingSystem.AIX) 783 { 784 // This is required when using JDK 1.6 on AIX to be able to write 785 // on the OutputStream. 786 try 787 { 788 Thread.sleep(1500); 789 } catch (Throwable t) {} 790 } 791 out.write(keyStorePassword.getBytes()) ; 792 out.write(lineSeparator.getBytes()) ; 793 out.flush() ; 794 } 795 796 if (askForStorePassword) 797 { 798 out.write(storePassword.getBytes()) ; 799 out.write(lineSeparator.getBytes()) ; 800 out.flush() ; 801 802 // With Java6 and above, keytool asks for the password twice (if we 803 // are not running AIX). 804 if (!isJDK15() && isNewStorePassword && 805 (SetupUtils.getOperatingSystem() != OperatingSystem.AIX)) 806 { 807 out.write(storePassword.getBytes()) ; 808 out.write(lineSeparator.getBytes()) ; 809 out.flush() ; 810 } 811 } 812 // Close the output stream since it can generate a deadlock on IBM JVM 813 // (issue 2795). 814 out.close(); 815 while (true) 816 { 817 int bytesRead = inputStream.read(buffer); 818 if (bytesRead < 0) 819 { 820 break; 821 } 822 else if (bytesRead > 0) 823 { 824 output.write(buffer, 0, bytesRead); 825 } 826 } 827 process.waitFor(); 828 int exitValue = process.exitValue(); 829 byte[] outputBytes = output.toByteArray(); 830 if (exitValue != 0) 831 { 832 // FIXME -- Make this an internationalizeable string. 833 StringBuilder message = new StringBuilder(); 834 message.append("Unexpected exit code of "); 835 message.append(exitValue); 836 message.append(" returned from the keytool utility."); 837 838 if ((outputBytes != null) && (outputBytes.length > 0)) 839 { 840 message.append(" The generated output was: '"); 841 message.append(new String(outputBytes)); 842 message.append("'."); 843 } 844 845 throw new KeyStoreException(message.toString()); 846 } 847 else if ((! outputAcceptable) && (outputBytes != null) && 848 (outputBytes.length > 0)) 849 { 850 // FIXME -- Make this an internationalizeable string. 851 StringBuilder message = new StringBuilder(); 852 message.append("Unexpected output generated by the keytool " + 853 "utility: '"); 854 message.append(new String(outputBytes)); 855 message.append("'."); 856 857 throw new KeyStoreException(message.toString()); 858 } 859 } 860 catch (KeyStoreException kse) 861 { 862 throw kse; 863 } 864 catch (Exception e) 865 { 866 // FIXME -- Make this an internationalizeable string. 867 throw new KeyStoreException("Could not invoke the KeyTool.run method: " + 868 e, e); 869 } 870 } 871 872 873 874 /** 875 * Retrieves a handle to the key store. 876 * 877 * @return The handle to the key store, or {@code null} if the key store 878 * doesn't exist. 879 * 880 * @throws KeyStoreException If a problem occurs while trying to open the 881 * key store. 882 */ 883 private KeyStore getKeyStore() 884 throws KeyStoreException 885 { 886 if (keyStore != null) 887 { 888 return keyStore; 889 } 890 891 // For JKS and PKCS12 key stores, we should make sure the file exists, and 892 // we'll need an input stream that we can use to read it. For PKCS11 key 893 // stores there won't be a file and the input stream should be null. 894 FileInputStream keyStoreInputStream = null; 895 if (keyStoreType.equals(KEY_STORE_TYPE_JKS) || 896 keyStoreType.equals(KEY_STORE_TYPE_PKCS12)) 897 { 898 File keyStoreFile = new File(keyStorePath); 899 if (! keyStoreFile.exists()) 900 { 901 return null; 902 } 903 904 try 905 { 906 keyStoreInputStream = new FileInputStream(keyStoreFile); 907 } 908 catch (Exception e) 909 { 910 throw new KeyStoreException(String.valueOf(e), e); 911 } 912 } 913 914 915 KeyStore keyStore = KeyStore.getInstance(keyStoreType); 916 try 917 { 918 keyStore.load(keyStoreInputStream, keyStorePIN.toCharArray()); 919 return this.keyStore = keyStore; 920 } 921 catch (Exception e) 922 { 923 throw new KeyStoreException(String.valueOf(e), e); 924 } 925 finally 926 { 927 if (keyStoreInputStream != null) 928 { 929 try 930 { 931 keyStoreInputStream.close(); 932 } 933 catch (Throwable t) 934 { 935 } 936 } 937 } 938 } 939 940 /** 941 * Returns whether we are running JDK 1.5 or not. 942 * @return <CODE>true</CODE> if we are running JDK 1.5 and <CODE>false</CODE> 943 * otherwise. 944 */ 945 private boolean isJDK15() 946 { 947 boolean isJDK15 = false; 948 try 949 { 950 String javaRelease = System.getProperty ("java.version"); 951 isJDK15 = javaRelease.startsWith("1.5"); 952 } 953 catch (Throwable t) 954 { 955 System.err.println("Cannot get the java version: " + t); 956 } 957 return isJDK15; 958 } 959 960 private String getGenKeyCommand() 961 { 962 String genKeyCommand; 963 if (!isJDK15()) 964 { 965 genKeyCommand = "-genkeypair"; 966 } 967 else 968 { 969 genKeyCommand = "-genkey"; 970 } 971 return genKeyCommand; 972 } 973 } 974 975