001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2015 ForgeRock AS. 016 */ 017 018package org.opends.quicksetup.util; 019 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.security.KeyStore; 025import java.security.KeyStoreException; 026import java.security.NoSuchAlgorithmException; 027import java.security.cert.Certificate; 028import java.security.cert.CertificateException; 029import java.security.cert.X509Certificate; 030import java.util.Enumeration; 031 032import org.forgerock.i18n.LocalizableMessage; 033import org.forgerock.i18n.slf4j.LocalizedLogger; 034 035/** 036 * Class used to get the KeyStore that the graphical utilities use. 037 * 038 */ 039public class UIKeyStore extends KeyStore 040{ 041 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 042 private static KeyStore keyStore; 043 044 /** 045 * This should never be called. 046 */ 047 private UIKeyStore() 048 { 049 super(null, null, null); 050 } 051 /** 052 * Returns the KeyStore to be used by graphical applications. 053 * @return the KeyStore to be used by graphical applications. 054 * @throws IOException if there was a file system access error. 055 * @throws KeyStoreException if there was a problem while reading the key 056 * store. 057 * @throws CertificateException if an error with a certificate occurred. 058 * @throws NoSuchAlgorithmException if the used algorithm is not supported 059 * by the system. 060 */ 061 public static KeyStore getInstance() throws IOException, KeyStoreException, 062 CertificateException, NoSuchAlgorithmException 063 { 064 if (keyStore == null) 065 { 066 keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 067 String keyStorePath = getKeyStorePath(); 068 069 File f = new File(keyStorePath); 070 if (!f.exists()) 071 { 072 logger.info(LocalizableMessage.raw("Path "+keyStorePath+ " does not exist")); 073 keyStorePath = null; 074 } 075 else if (f.isDirectory()) 076 { 077 logger.error(LocalizableMessage.raw("Path "+keyStorePath+ " is a directory")); 078 keyStorePath = null; 079 } 080 else if (!f.canRead()) 081 { 082 logger.error(LocalizableMessage.raw("Path "+keyStorePath+ " is not readable")); 083 keyStorePath = null; 084 } 085 else if (!f.canWrite()) 086 { 087 logger.error(LocalizableMessage.raw("Path "+keyStorePath+ " is not writable")); 088 keyStorePath = null; 089 } 090 091 092 if (keyStorePath != null) 093 { 094 FileInputStream fos = new FileInputStream(keyStorePath); 095 try 096 { 097 keyStore.load(fos, null); 098 } 099 catch (Throwable t) 100 { 101 logger.error(LocalizableMessage.raw("Error reading key store on "+keyStorePath, t)); 102 keyStore.load(null, null); 103 } 104 fos.close(); 105 } 106 else 107 { 108 keyStore.load(null, null); 109 } 110 loadLocalAdminTrustStore(keyStore); 111 } 112 return keyStore; 113 } 114 115 /** 116 * Updates the Key Store with the provided certificate chain. 117 * @param chain the certificate chain to be accepted. 118 * @throws IOException if there was a file system access error. 119 * @throws KeyStoreException if there was a problem while reading or writing 120 * to the key store. 121 * @throws CertificateException if an error with a certificate occurred. 122 * @throws NoSuchAlgorithmException if the used algorithm is not supported 123 * by the system. 124 */ 125 public static void acceptCertificate(X509Certificate[] chain) 126 throws IOException,KeyStoreException, CertificateException, 127 NoSuchAlgorithmException 128 { 129 logger.info(LocalizableMessage.raw("Accepting certificate chain.")); 130 KeyStore k = getInstance(); 131 for (X509Certificate aChain : chain) { 132 if (!containsCertificate(aChain, k)) { 133 String alias = aChain.getSubjectDN().getName(); 134 int j = 1; 135 while (k.containsAlias(alias)) { 136 alias = aChain.getSubjectDN().getName() + "-" + j; 137 j++; 138 } 139 k.setCertificateEntry(alias, aChain); 140 } 141 } 142 String keyStorePath = getKeyStorePath(); 143 File f = new File(keyStorePath); 144 if (!f.exists()) 145 { 146 Utils.createFile(f); 147 } 148 FileOutputStream fos = new FileOutputStream(getKeyStorePath(), false); 149 k.store(fos, new char[]{}); 150 fos.close(); 151 } 152 153 /** 154 * Returns the path where we store the keystore for the graphical 155 * applications. 156 * @return the path where we store the keystore for the graphical 157 * applications. 158 */ 159 private static String getKeyStorePath() 160 { 161 return System.getProperty("user.home") + File.separator + 162 ".opendj" + File.separator + "gui-keystore"; 163 } 164 165 /** 166 * Loads the local admin truststore. 167 * @param keyStore the keystore where the admin truststore will be loaded. 168 */ 169 private static void loadLocalAdminTrustStore(KeyStore keyStore) 170 { 171 String adminTrustStorePath = getLocalAdminTrustStorePath(); 172 File f = new File(adminTrustStorePath); 173 if (!f.exists()) 174 { 175 logger.info(LocalizableMessage.raw("Path "+adminTrustStorePath+ " does not exist")); 176 adminTrustStorePath = null; 177 } 178 else if (f.isDirectory()) 179 { 180 logger.error(LocalizableMessage.raw("Path "+adminTrustStorePath+ " is a directory")); 181 adminTrustStorePath = null; 182 } 183 else if (!f.canRead()) 184 { 185 logger.error(LocalizableMessage.raw("Path "+adminTrustStorePath+ " is not readable")); 186 adminTrustStorePath = null; 187 } 188 189 if (adminTrustStorePath != null) 190 { 191 FileInputStream fos = null; 192 try 193 { 194 fos = new FileInputStream(adminTrustStorePath); 195 KeyStore adminKeyStore = 196 KeyStore.getInstance(KeyStore.getDefaultType()); 197 adminKeyStore.load(fos, null); 198 Enumeration<String> aliases = adminKeyStore.aliases(); 199 while (aliases.hasMoreElements()) 200 { 201 String alias = aliases.nextElement(); 202 if (adminKeyStore.isCertificateEntry(alias)) 203 { 204 keyStore.setCertificateEntry(alias, 205 adminKeyStore.getCertificate(alias)); 206 } 207 else 208 { 209 keyStore.setEntry(alias, adminKeyStore.getEntry(alias, null), null); 210 } 211 } 212 } 213 catch (Throwable t) 214 { 215 logger.error(LocalizableMessage.raw("Error reading admin key store on "+ 216 adminTrustStorePath, t)); 217 } 218 finally 219 { 220 try 221 { 222 if (fos != null) 223 { 224 fos.close(); 225 } 226 } 227 catch (Throwable t) 228 { 229 logger.error(LocalizableMessage.raw("Error closing admin key store on "+ 230 adminTrustStorePath, t)); 231 } 232 } 233 } 234 } 235 236 /** 237 * Returns the path where the local admin trust store is. 238 * @return the path where the local admin trust store is. 239 */ 240 private static String getLocalAdminTrustStorePath() 241 { 242 String instancePath = 243 Utils.getInstancePathFromInstallPath(Utils.getInstallPathFromClasspath()); 244 return instancePath + File.separator + "config" + 245 File.separator + "admin-truststore"; 246 } 247 248 /** 249 * Returns whether the key store contains the provided certificate or not. 250 * @param cert the certificate. 251 * @param keyStore the key store. 252 * @return whether the key store contains the provided certificate or not. 253 * @throws KeyStoreException if an error occurs reading the contents of the 254 * key store. 255 */ 256 private static boolean containsCertificate(X509Certificate cert, 257 KeyStore keyStore) throws KeyStoreException 258 { 259 boolean found = false; 260 Enumeration<String> aliases = keyStore.aliases(); 261 while (aliases.hasMoreElements() && !found) 262 { 263 String alias = aliases.nextElement(); 264 if (keyStore.isCertificateEntry(alias)) 265 { 266 Certificate c = keyStore.getCertificate(alias); 267 found = c.equals(cert); 268 } 269 } 270 return found; 271 } 272}