1 19 20 package com.sslexplorer.boot; 21 22 import java.io.ByteArrayInputStream ; 23 import java.io.File ; 24 import java.io.FileInputStream ; 25 import java.io.FileOutputStream ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.OutputStream ; 29 import java.security.Key ; 30 import java.security.KeyPair ; 31 import java.security.KeyStore ; 32 import java.security.KeyStoreException ; 33 import java.security.Principal ; 34 import java.security.PrivateKey ; 35 import java.security.PublicKey ; 36 import java.security.cert.Certificate ; 37 import java.security.cert.X509Certificate ; 38 import java.util.ArrayList ; 39 import java.util.Arrays ; 40 import java.util.Date ; 41 import java.util.Enumeration ; 42 import java.util.HashMap ; 43 import java.util.Iterator ; 44 import java.util.List ; 45 import java.util.StringTokenizer ; 46 import java.util.Vector ; 47 48 import org.apache.commons.logging.Log; 49 import org.apache.commons.logging.LogFactory; 50 51 import com.maverick.crypto.asn1.ASN1Sequence; 52 import com.maverick.crypto.asn1.DERInputStream; 53 import com.maverick.crypto.asn1.x509.X509CertificateStructure; 54 import com.maverick.ssl.TrustedCACertStore; 55 56 84 public class KeyStoreManager { 85 86 88 92 public static final String DEFAULT_KEY_STORE = "default"; 93 94 98 public static final String SERVER_AUTHENTICATION_CERTIFICATES_KEY_STORE = "serverAuthentication"; 99 100 104 public static final String TRUSTED_SERVER_CERTIFICATES_KEY_STORE = "trustedServer"; 105 106 109 public static final String DEFAULT_KEY_PASSWORD = "sslexplorer"; 110 111 114 public static final String KEYSTORE_REPOSITORY = "keystore"; 115 116 118 private boolean keyStoreExists; 119 private Date keystoreLastModified; 120 private KeyStore keyStore; 121 private boolean keyStoreEmpty; 122 private File keyStoreFile; 123 private Throwable keyStoreException; 124 private String keyStoreName; 125 private KeyStoreType keyStoreType; 126 private String bundle; 127 private boolean removeable; 128 private String storePassword; 129 130 final static Log log = LogFactory.getLog(KeyStoreManager.class); 132 static String KEY_TOOL = System.getProperty("java.home") + File.separator + "bin" + File.separator + "keytool"; 133 private static HashMap <String ,KeyStoreManager> instances = new HashMap <String ,KeyStoreManager>(); 134 135 138 public static final KeyStoreType TYPE_JKS = new KeyStoreType("JKS", "jks"); 139 140 143 public static final KeyStoreType TYPE_PKCS12 = new KeyStoreType("PKCS12", "p12"); 144 145 private static final List keyStoreTypes = Arrays.asList(new KeyStoreType[] { TYPE_JKS, TYPE_PKCS12 }); 146 157 private KeyStoreManager(String keyStoreName, String bundle, boolean removeable, String storePassword, KeyStoreType type) { 158 super(); 159 160 this.keyStoreName = keyStoreName; 161 this.bundle = bundle; 162 this.removeable = removeable; 163 this.storePassword = storePassword; 164 this.keyStoreType = type; 165 166 initKeyStoreFile(); 167 168 170 try { 171 synchronizeWithRepository(); 172 } catch (IOException ex) { 173 log.error("The keystore could not be synchornized with the repository", ex); 174 } 175 } 176 177 178 184 public static KeyStoreManager getInstance(String keyStoreName) { 185 KeyStoreManager mgr = (KeyStoreManager) instances.get(keyStoreName); 186 if (mgr == null) { 187 throw new IllegalArgumentException ("No keystore named " + keyStoreName); 188 } 189 return mgr; 190 } 191 192 196 public InputStream getInputStream() throws IOException { 197 return RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY).getEntryInputStream(keyStoreFile.getName()); 198 } 199 200 205 public static List <KeyStoreManager> getKeyStores() { 206 List <KeyStoreManager> keyStoreList = new ArrayList <KeyStoreManager>(); 207 for (KeyStoreManager manager : instances.values()) { 208 keyStoreList.add(manager); 209 } 210 return keyStoreList; 211 } 212 213 223 public static void registerKeyStore(String name, String bundle, boolean removeable, String storePassword, KeyStoreType type) { 224 if (log.isInfoEnabled()) 225 log.info("Registering keystore " + name); 226 KeyStoreManager mgr = new KeyStoreManager(name, bundle, removeable, storePassword, type); 227 instances.put(name, mgr); 228 } 229 230 237 public void setStorePassword(String storePassword) { 238 this.storePassword = storePassword; 239 reloadKeystore(); 240 } 241 242 247 public boolean getRemoveable() { 248 return removeable; 249 } 250 251 256 public String getBundle() { 257 return bundle; 258 } 259 260 265 public String getName() { 266 return keyStoreName; 267 } 268 269 274 public boolean isKeyStoreExists() { 275 try { 276 checkKeyStore(); 277 } catch (Exception e) { 278 log.error("Could not determine if key store exists."); 279 } 280 return keyStoreExists; 281 } 282 283 288 public boolean isKeyStoreEmpty() { 289 try { 290 checkKeyStore(); 291 } catch (Exception e) { 292 log.error("Could not determine if key store exists."); 293 } 294 return keyStoreEmpty; 295 } 296 297 304 public boolean isCertificateTrusted(String alias) { 305 try { 306 checkKeyStore(); 307 if (isKeyStoreExists() && !isKeyStoreEmpty()) { 308 return doIsCertificateTrused(alias, keyStore); 309 } 310 } catch (Exception e) { 311 log.error("Could not determine if certificate " + alias + " is trusted.", e); 312 } 313 return false; 314 } 315 316 324 public Throwable getKeyStoreException() { 325 checkKeyStore(); 326 return keyStoreException; 327 } 328 329 334 public KeyStore getKeyStore() { 335 if(keyStore==null) 336 this.reloadKeystore(); 337 338 return keyStore; 339 } 340 341 347 public Certificate getCertificate(String alias) { 348 try { 349 checkKeyStore(); 350 if (isKeyStoreExists() && !isKeyStoreEmpty()) { 351 return keyStore.getCertificate(alias); 352 } 353 } catch (Exception e) { 354 log.error("Could not get certificate with alias " + alias + ".", e); 355 } 356 return null; 357 } 358 359 365 public Enumeration getCertificateAliases() { 366 checkKeyStore(); 367 try { 368 if (keyStore != null) { 369 return keyStore.aliases(); 370 } 371 } catch (Exception e) { 372 log.error("Could not get certificates.", e); 373 } 374 return null; 375 } 376 377 382 public int getSize() { 383 checkKeyStore(); 384 try { 385 return keyStore != null ? keyStore.size() : 0; 386 } catch (KeyStoreException e) { 387 log.error("Failed to determine size of key store.", e); 388 } 389 return 0; 390 } 391 392 399 public void changeKeystorePassword(String oldPassword, String password) throws Exception { 400 checkKeyStore(); 401 if (!isKeyStoreExists()) { 402 throw new Exception ("Key store doesn't exists. Password cannot be changed."); 403 } 404 CommandRunner runner = null; 405 try { 406 Vector <String > v = new Vector <String >(); 407 v.add(KEY_TOOL); 408 v.add("-storepasswd"); 409 v.add("-new"); 410 v.add(password); 411 v.add("-keystore"); 412 v.add(getKeyStoreFile().getAbsolutePath()); 413 v.add("-storepass"); 414 v.add(oldPassword); 415 runner = new CommandRunner(v); 416 runner.runCommand(); 417 this.storePassword = password; 418 } catch (Exception e) { 419 log.error("Failed to change keystore password.", e); 420 throw new Exception (runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); 421 } 422 423 } 424 425 432 public KeyPair getKeyPair(String alias, char[] password) { 433 try { 434 checkKeyStore(); 435 if (isKeyStoreExists() && !isKeyStoreEmpty()) { 436 Key key = keyStore.getKey(alias, password); 437 if (key instanceof PrivateKey ) { 438 Certificate cert = keyStore.getCertificate(alias); 439 PublicKey publicKey = cert.getPublicKey(); 440 return new KeyPair (publicKey, (PrivateKey ) key); 441 } 442 } 443 } catch (Exception e) { 444 log.error("Could not get key pair with alias " + alias + ".", e); 445 } 446 return null; 447 } 448 449 456 public PrivateKey getPrivateKey(String alias, char[] password) { 457 try { 458 checkKeyStore(); 459 if (isKeyStoreExists() && !isKeyStoreEmpty()) { 460 return (PrivateKey ) keyStore.getKey(alias, password); 461 } 462 } catch (Exception e) { 463 log.error("Could not get private key with alias " + alias + ".", e); 464 } 465 return null; 466 } 467 468 475 476 public Certificate [] getCertificateChain(String alias) { 477 Certificate [] chain = null; 478 try { 479 checkKeyStore(); 480 if (isKeyStoreExists() && !isKeyStoreEmpty()) { 481 chain = keyStore.getCertificateChain(alias); 482 } 483 } catch (Exception e) { 484 log.error(e); 485 } 486 if (chain == null) { 487 log.error("Could not get private key with alias " + alias + "."); 488 } 489 return chain; 490 } 491 492 499 public static String getX509CertificateEntity(X509Certificate c, String entity) { 500 Principal subjectPrincipal = c.getSubjectDN(); 503 StringTokenizer t = new StringTokenizer (subjectPrincipal.getName(), ","); 504 while (t.hasMoreTokens()) { 505 String e = t.nextToken().trim(); 506 String f = entity.trim() + "="; 507 if (e.toLowerCase().startsWith(f.toLowerCase())) { 508 return e.substring(f.length()).trim(); 509 } 510 } 511 return ""; 512 } 513 514 517 public void reloadKeystore() { 518 keyStoreExists = false; 519 keyStoreException = null; 520 keyStoreEmpty = true; 521 keyStore = null; 522 try { 523 File keystoreFile = getKeyStoreFile(); 524 InputStream in = null; 525 if (keystoreFile.exists() && keystoreFile.canRead()) { 526 keyStoreExists = true; 527 keyStoreException = null; 528 keyStoreEmpty = true; 529 keyStore = null; 530 try { 531 keyStore = KeyStore.getInstance(keyStoreType.getName()); 532 String keystorePassword = getKeyStorePassword(); 533 if (keystoreFile.length() != 0) { 534 in = new FileInputStream (keystoreFile); 535 keyStore.load(in, keystorePassword.toCharArray()); 536 keyStoreEmpty = keyStore.size() == 0; 537 } 538 } finally { 539 Util.closeStream(in); 540 } 541 } else { 542 } 544 } catch (Exception e) { 545 log.error("Failed to check key store.", e); 546 keyStoreException = e; 547 } 548 } 549 550 551 555 public void checkKeyStore() { 556 initKeyStoreFile(); 557 try { 558 File keystoreFile = getKeyStoreFile(); 559 if (keystoreFile.exists() && keystoreFile.canRead()) { 560 Date fileLastModified = new Date (keystoreFile.lastModified()); 561 if (keystoreLastModified == null || !keystoreLastModified.equals(fileLastModified)) { 562 keystoreLastModified = fileLastModified; 563 reloadKeystore(); 564 } else { 565 } 567 } else { 568 keyStore = null; 569 keyStoreExists = false; 570 keyStoreEmpty = true; 571 keyStoreException = null; 572 } 573 } catch (Exception e) { 574 log.error("Failed to check key store.", e); 575 keyStoreException = e; 576 } 577 } 578 579 589 public String importPKCS12Key(File keyFile, String password, String alias, String newAlias) throws Exception { 590 KeyStore kspkcs12 = KeyStore.getInstance("PKCS12"); 591 kspkcs12.load(new FileInputStream (keyFile), password == null ? null : password.toCharArray()); 592 boolean hasTemp = false; 593 if(isKeyStoreEmpty()) { 594 if(isKeyStoreExists()) { 595 deleteKeyStore(); 596 } 597 createKeyStore(); 598 String dname = "cn=tmp, ou=tmp, o=tmp, l=tmp, st=tmp, c=GB"; 599 createKey("temporary-key", dname); 600 hasTemp = true; 601 reloadKeystore(); 602 } 603 try { 604 605 String firstAlias = (String ) kspkcs12.aliases().nextElement(); 606 607 if(Util.isNullOrTrimmedBlank(alias)) { 608 log.info("Alias not specified, importing first alias " + firstAlias); 609 alias = firstAlias; 610 } 611 612 if(Util.isNullOrTrimmedBlank(newAlias)) { 613 log.info("New alias not specified, using imported alias " + alias); 614 newAlias = alias; 615 } 616 617 Certificate c[] = kspkcs12.getCertificateChain(alias); 618 if (c == null) 620 c = new Certificate [] {}; 621 Key key = kspkcs12.getKey(alias, password == null ? null : password.toCharArray()); 622 if(key == null) { 623 throw new Exception ("No alias of '" + alias + "' in imported PKCS12 key file."); 624 } 625 this.keyStore.setKeyEntry(newAlias, key, getKeyStorePassword().toCharArray(), c); 626 } finally { 627 if(hasTemp || keyStore.containsAlias("temporary-key")) 628 this.keyStore.deleteEntry("temporary-key"); 629 OutputStream out = null; 630 try { 631 out = new FileOutputStream (keyStoreFile.getAbsolutePath()); 632 getKeyStore().store(out, getKeyStorePassword().toCharArray()); 633 } finally { 634 Util.closeStream(out); 635 } 636 updateRepository(false); 637 } 638 639 return newAlias; 640 } 641 642 647 public File getKeyStoreFile() { 648 return keyStoreFile; 649 } 650 651 659 public void createKey(String alias, String dname) throws Exception { 660 checkKeyStore(); 661 if (!isKeyStoreExists()) { 662 throw new Exception ("Key store doesn't exists. Key cannot be created."); 663 } 664 668 if (isKeyStoreEmpty()) { 669 if (!getKeyStoreFile().delete()) { 670 throw new Exception ("Could not delete key store."); 671 } 672 } 673 CommandRunner runner = null; 674 try { 675 String keyStorePassword = getKeyStorePassword(); 676 Vector <String > v = new Vector <String >(); 677 v.add(KEY_TOOL); 678 v.add("-genkey"); 679 v.add("-alias"); 680 v.add(alias); 681 v.add("-keyalg"); 682 v.add("RSA"); 683 v.add("-keystore"); 684 v.add(keyStoreFile.getAbsolutePath()); 685 v.add("-dname"); 686 v.add(dname); 687 v.add("-storetype"); 688 v.add(keyStoreType.getName()); 689 v.add("-storepass"); 690 v.add(keyStorePassword); 691 v.add("-keypass"); 692 v.add(keyStorePassword); 693 v.add("-validity"); 694 v.add("365"); 695 runner = new CommandRunner(v); 696 runner.runCommand(); 697 698 updateRepository(false); 699 } catch (Exception e) { 700 log.error("Failed to create key.", e); 701 throw new Exception (runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); 702 } 703 } 704 705 714 public void importCert(String alias, File certFile, String keyPass) throws Exception { 715 checkKeyStore(); 716 if (!isKeyStoreExists()) { 717 createKeyStore(); 718 } 719 723 if (isKeyStoreEmpty()) { 724 if (!getKeyStoreFile().delete()) { 725 throw new Exception ("Could not delete key store."); 726 } 727 } 728 CommandRunner runner = null; 729 try { 730 if (log.isInfoEnabled()) 731 log.info("Importing certificate for " + alias + " from " + certFile.getAbsolutePath()); 732 String keyPassword = getKeyStorePassword(); 733 Vector <String > v = new Vector <String >(); 734 v.add(KEY_TOOL); 735 v.add("-import"); 736 v.add("-trustcacerts"); 737 738 v.add("-noprompt"); 739 v.add("-file"); 740 v.add(certFile.getAbsolutePath()); 741 v.add("-alias"); 742 v.add(alias); 743 v.add("-keystore"); 744 v.add(keyStoreFile.getAbsolutePath()); 745 v.add("-storepass"); 746 v.add(keyPassword); 747 v.add("-keypass"); 748 749 v.add(keyPass == null ? DEFAULT_KEY_PASSWORD : keyPass); 750 v.add("-storetype"); 751 v.add(keyStoreType.getName().toLowerCase()); 752 runner = new CommandRunner(v); 753 runner.runCommand(); 754 755 updateRepository(false); 756 } catch (Exception e) { 757 log.error("Failed to import certficate.", e); 758 throw new Exception (runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); 759 } 760 if (log.isInfoEnabled()) 761 log.info("Certificate for " + alias + " imported from " + certFile.getAbsolutePath()); 762 } 763 764 773 public String generateCSR(String alias, String keyPass) throws Exception { 774 checkKeyStore(); 775 if (!isKeyStoreExists()) { 776 throw new Exception ("Key store doesn't exists. CSR cannot be generated."); 777 } 778 CommandRunner runner = null; 779 InputStream in = null; 780 try { 781 String keyPassword = getKeyStorePassword(); 782 Vector <String > v = new Vector <String >(); 783 v.add(KEY_TOOL); 784 v.add("-certreq"); 785 v.add("-alias"); 786 v.add(alias); 787 v.add("-keyalg"); 788 v.add("RSA"); 789 v.add("-keystore"); 790 v.add(keyStoreFile.getAbsolutePath()); 791 v.add("-storepass"); 792 v.add(keyPassword); 793 v.add("-file"); 794 File csrFile = new File (ContextHolder.getContext().getConfDirectory(), "sslexplorer.csr"); 795 v.add(csrFile.getAbsolutePath()); 796 v.add("-keypass"); 797 v.add(keyPass == null ? DEFAULT_KEY_PASSWORD : keyPass); 798 runner = new CommandRunner(v); 799 runner.runCommand(); 800 in = new FileInputStream (csrFile); 801 return Util.loadStreamToString(in, null); 802 } catch (Exception e) { 803 log.error("Failed to create key.", e); 804 throw new Exception (runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); 805 } finally { 806 Util.closeStream(in); 807 } 808 } 809 810 819 public void createKeyStore() throws IOException { 820 if (isKeyStoreExists()) { 821 throw new IOException ("Key store already exists."); 822 } 823 FileOutputStream out = null; 824 try { 825 out = new FileOutputStream (getKeyStoreFile()); 826 } finally { 827 Util.closeStream(out); 828 } 829 } 830 831 832 837 public void deleteKeyStore() throws IOException { 838 if (!isKeyStoreExists()) { 839 throw new IOException ("Key store does not exist."); 840 } 841 if (!getKeyStoreFile().delete()) { 842 throw new IOException ("Failed to delete " + getKeyStoreFile().getAbsolutePath() + "."); 843 } 844 845 updateRepository(true); 846 } 847 848 854 public void deleteCertificate(String alias) throws Exception { 855 checkKeyStore(); 856 if (!isKeyStoreExists()) { 857 throw new Exception ("Key store doesn't exists. Certificate cannot be deleted."); 858 } 859 CommandRunner runner = null; 860 try { 861 if (log.isInfoEnabled()) 862 log.info("Deleting certificate for " + alias); 863 String keyPassword = getKeyStorePassword(); 864 Vector <String > v = new Vector <String >(); 865 v.add(KEY_TOOL); 866 v.add("-delete"); 867 v.add("-alias"); 868 v.add(alias); 869 v.add("-keystore"); 870 v.add(keyStoreFile.getAbsolutePath()); 871 v.add("-storepass"); 872 v.add(keyPassword); 873 runner = new CommandRunner(v); 874 runner.runCommand(); 875 876 updateRepository(false); 877 } catch (Exception e) { 878 log.error("Failed to delete certificate.", e); 879 throw new Exception (runner == null ? e.getMessage() : parseKeytoolOutput(runner.getOutput())); 880 } 881 if (log.isInfoEnabled()) 882 log.info("Deleted certificate for " + alias); 883 884 } 885 886 892 public static KeyStoreType getKeyStoreType(String name) { 893 for (Iterator i = keyStoreTypes.iterator(); i.hasNext();) { 894 KeyStoreType t = (KeyStoreType) i.next(); 895 if (t.getName().equals(name)) { 896 return t; 897 } 898 } 899 return null; 900 } 901 902 907 public List getSupportedKeyStoreTypes() { 908 return keyStoreTypes; 909 } 910 911 916 public void setKeyStoreType(KeyStoreType keyStoreType) { 917 this.keyStoreType = keyStoreType; 918 initKeyStoreFile(); 919 } 920 921 926 public KeyStoreType getKeyStoreType() { 927 return keyStoreType; 928 } 929 930 932 void initKeyStoreFile() { 933 this.keyStoreFile = new File (ContextHolder.getContext().getConfDirectory(), keyStoreName + ".keystore." + keyStoreType.getExtension()); 934 } 935 936 void synchronizeWithRepository() throws IOException { 937 938 RepositoryStore store = RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY); 939 940 if (!store.hasEntry(keyStoreFile.getName())) { 941 keyStoreFile.createNewFile(); 942 } else { 943 InputStream in = null; 944 OutputStream out = null; 945 try { 946 in = store.getEntryInputStream(keyStoreFile.getName()); 947 out = new FileOutputStream (keyStoreFile); 948 Util.copy(in, out); 949 } finally { 950 Util.closeStream(in); 951 Util.closeStream(out); 952 } 953 } 954 } 955 956 957 958 void updateRepository(boolean remove) throws IOException { 959 960 RepositoryStore store = RepositoryFactory.getRepository().getStore(KEYSTORE_REPOSITORY); 961 962 if (remove) { 963 store.removeEntry(keyStoreFile.getName()); 964 } else { 965 OutputStream out = null; 966 InputStream in = null; 967 try { 968 out = store.getEntryOutputStream(keyStoreFile.getName()); 969 in = new FileInputStream (keyStoreFile); 970 971 Util.copy(in, out); 972 } finally { 973 Util.closeStream(in); 974 Util.closeStream(out); 975 } 976 } 977 978 } 979 980 986 public String getKeyStorePassword() throws Exception { 987 return storePassword; 988 } 989 990 boolean doIsCertificateTrused(String alias, KeyStore keyStore) throws Exception { 991 992 Certificate [] certs = keyStore.getCertificateChain(alias); 993 994 995 if (certs == null) { 1000 if (log.isInfoEnabled()) 1001 log.info("No certs for " + alias + ", untrusted."); 1002 } else if (certs.length > 1) { 1003 X509Certificate x509cert = (X509Certificate ) certs[certs.length - 1]; 1004 TrustedCACertStore store = new TrustedCACertStore(); 1005 ByteArrayInputStream bin = new ByteArrayInputStream (x509cert.getEncoded()); 1006 DERInputStream der = null; 1007 try { 1008 der = new DERInputStream(bin); 1009 1010 ASN1Sequence certificate = (ASN1Sequence) der.readObject(); 1011 com.maverick.crypto.asn1.x509.X509Certificate x509 = new com.maverick.crypto.asn1.x509.X509Certificate( 1012 X509CertificateStructure.getInstance(certificate)); 1013 return store.isTrustedCertificate(x509, false, false); 1014 } finally { 1015 Util.closeStream(der); 1016 } 1017 } 1018 return false; 1020 1021 } 1022 1023 String parseKeytoolOutput(String output) { 1024 if (output.startsWith("keytool error: ")) { 1025 int idx = output.indexOf(':', 14); 1026 if (idx != -1) { 1027 output = output.substring(idx + 1); 1028 } 1029 } 1030 return output; 1031 } 1032 1033 1038 public static void deregisterKeyStore(String name) { 1039 if (log.isInfoEnabled()) 1040 log.info("Deregistering keystore " + name); 1041 instances.remove(name); 1042 } 1043 1044 1045 1046} 1047 | Popular Tags |