1/* 2 * Copyright (c) 2016, 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 26package sun.security.ssl; 27 28import java.lang.ref.WeakReference; 29import java.io.*; 30import java.util.*; 31 32import java.security.*; 33import java.security.cert.*; 34import java.security.cert.Certificate; 35 36import sun.security.action.*; 37import sun.security.validator.TrustStoreUtil; 38 39/** 40 * Collection of static utility methods to manage the default trusted KeyStores 41 * effectively. 42 */ 43final class TrustStoreManager { 44 private static final Debug debug = Debug.getInstance("ssl"); 45 46 // A singleton service to manage the default trusted KeyStores effectively. 47 private static final TrustAnchorManager tam = new TrustAnchorManager(); 48 49 // Restrict instantiation of this class. 50 private TrustStoreManager() { 51 // empty 52 } 53 54 /** 55 * Return an unmodifiable set of all trusted X509Certificates contained 56 * in the default trusted KeyStore. 57 */ 58 public static Set<X509Certificate> getTrustedCerts() throws Exception { 59 return tam.getTrustedCerts(TrustStoreDescriptor.createInstance()); 60 } 61 62 /** 63 * Return an instance of the default trusted KeyStore. 64 */ 65 public static KeyStore getTrustedKeyStore() throws Exception { 66 return tam.getKeyStore(TrustStoreDescriptor.createInstance()); 67 } 68 69 /** 70 * A descriptor of the default trusted KeyStore. 71 * 72 * The preference of the default trusted KeyStore is: 73 * javax.net.ssl.trustStore 74 * jssecacerts 75 * cacerts 76 */ 77 private static final class TrustStoreDescriptor { 78 private static final String fileSep = File.separator; 79 private static final String defaultStorePath = 80 GetPropertyAction.privilegedGetProperty("java.home") + 81 fileSep + "lib" + fileSep + "security"; 82 private static final String defaultStore = 83 defaultStorePath + fileSep + "cacerts"; 84 private static final String jsseDefaultStore = 85 defaultStorePath + fileSep + "jssecacerts"; 86 87 // the trust store name 88 private final String storeName; 89 90 // the trust store type, JKS/PKCS12 91 private final String storeType; 92 93 // the provider of the trust store 94 private final String storeProvider; 95 96 // the password used for the trust store 97 private final String storePassword; 98 99 // the File object of the trust store 100 private final File storeFile; 101 102 // the last modified time of the store 103 private final long lastModified; 104 105 private TrustStoreDescriptor(String storeName, String storeType, 106 String storeProvider, String storePassword, 107 File storeFile, long lastModified) { 108 this.storeName = storeName; 109 this.storeType = storeType; 110 this.storeProvider = storeProvider; 111 this.storePassword = storePassword; 112 this.storeFile = storeFile; 113 this.lastModified = lastModified; 114 115 if (debug != null && Debug.isOn("trustmanager")) { 116 System.out.println( 117 "trustStore is: " + storeName + "\n" + 118 "trustStore type is: " + storeType + "\n" + 119 "trustStore provider is: " + storeProvider + "\n" + 120 "the last modified time is: " + (new Date(lastModified))); 121 } 122 } 123 124 /** 125 * Create an instance of TrustStoreDescriptor for the default 126 * trusted KeyStore. 127 */ 128 static TrustStoreDescriptor createInstance() { 129 return AccessController.doPrivileged(new PrivilegedAction<>() { 130 131 @Override 132 public TrustStoreDescriptor run() { 133 // Get the system properties for trust store. 134 String storePropName = System.getProperty( 135 "javax.net.ssl.trustStore", jsseDefaultStore); 136 String storePropType = System.getProperty( 137 "javax.net.ssl.trustStoreType", 138 KeyStore.getDefaultType()); 139 String storePropProvider = System.getProperty( 140 "javax.net.ssl.trustStoreProvider", ""); 141 String storePropPassword = System.getProperty( 142 "javax.net.ssl.trustStorePassword", ""); 143 144 String temporaryName = ""; 145 File temporaryFile = null; 146 long temporaryTime = 0L; 147 if (!"NONE".equals(storePropName)) { 148 String[] fileNames = 149 new String[] {storePropName, defaultStore}; 150 for (String fileName : fileNames) { 151 File f = new File(fileName); 152 if (f.isFile() && f.canRead()) { 153 temporaryName = fileName;; 154 temporaryFile = f; 155 temporaryTime = f.lastModified(); 156 157 break; 158 } 159 160 // Not break, the file is inaccessible. 161 if (debug != null && 162 Debug.isOn("trustmanager")) { 163 System.out.println( 164 "Inaccessible trust store: " + 165 storePropName); 166 } 167 } 168 } else { 169 temporaryName = storePropName; 170 } 171 172 return new TrustStoreDescriptor( 173 temporaryName, storePropType, storePropProvider, 174 storePropPassword, temporaryFile, temporaryTime); 175 } 176 }); 177 } 178 179 @Override 180 public boolean equals(Object obj) { 181 if (obj == this) { 182 return true; 183 } 184 185 if (obj instanceof TrustStoreDescriptor) { 186 TrustStoreDescriptor that = (TrustStoreDescriptor)obj; 187 return ((this.lastModified == that.lastModified) && 188 Objects.equals(this.storeName, that.storeName) && 189 Objects.equals(this.storeType, that.storeType) && 190 Objects.equals(this.storeProvider, that.storeProvider)); 191 } 192 193 return false; 194 } 195 196 197 // Please be careful if computing security-sensitive attributes' 198 // hash code. For example the storePassword should not be computed. 199 @Override 200 public int hashCode() { 201 int result = 17; 202 203 if (storeName != null && !storeName.isEmpty()) { 204 result = 31 * result + storeName.hashCode(); 205 } 206 207 if (storeType != null && !storeType.isEmpty()) { 208 result = 31 * result + storeType.hashCode(); 209 } 210 211 if (storeProvider != null && !storeProvider.isEmpty()) { 212 result = 31 * result + storeProvider.hashCode(); 213 } 214 215 if (storeFile != null) { 216 result = 31 * result + storeFile.hashCode(); 217 } 218 219 if (lastModified != 0L) { 220 result = (int)(31 * result + lastModified); 221 } 222 223 return result; 224 } 225 } 226 227 /** 228 * The trust anchors manager used to expedite the performance. 229 * 230 * This class can be used to provide singleton services to access default 231 * trust KeyStore more effectively. 232 */ 233 private static final class TrustAnchorManager { 234 // Last trust store descriptor. 235 private TrustStoreDescriptor descriptor; 236 237 // The key store used for the trust anchors. 238 // 239 // Use weak reference so that the heavy loaded KeyStore object can 240 // be atomically cleared, and reloaded if needed. 241 private WeakReference<KeyStore> ksRef; 242 243 // The trusted X.509 certificates in the key store. 244 // 245 // Use weak reference so that the heavy loaded certificates collection 246 // objects can be atomically cleared, and reloaded if needed. 247 private WeakReference<Set<X509Certificate>> csRef; 248 249 private TrustAnchorManager() { 250 this.descriptor = null; 251 this.ksRef = new WeakReference<>(null); 252 this.csRef = new WeakReference<>(null); 253 } 254 255 /** 256 * Get the default trusted KeyStore with the specified descriptor. 257 * 258 * @return null if the underlying KeyStore is not available. 259 */ 260 synchronized KeyStore getKeyStore( 261 TrustStoreDescriptor descriptor) throws Exception { 262 263 TrustStoreDescriptor temporaryDesc = this.descriptor; 264 KeyStore ks = ksRef.get(); 265 if ((ks != null) && descriptor.equals(temporaryDesc)) { 266 return ks; 267 } 268 269 // Reload a new key store. 270 if ((debug != null) && Debug.isOn("trustmanager")) { 271 System.out.println("Reload the trust store"); 272 } 273 274 ks = loadKeyStore(descriptor); 275 this.descriptor = descriptor; 276 this.ksRef = new WeakReference<>(ks); 277 278 return ks; 279 } 280 281 /** 282 * Get trusted certificates in the default trusted KeyStore with 283 * the specified descriptor. 284 * 285 * @return empty collection if the underlying KeyStore is not available. 286 */ 287 synchronized Set<X509Certificate> getTrustedCerts( 288 TrustStoreDescriptor descriptor) throws Exception { 289 290 KeyStore ks = null; 291 TrustStoreDescriptor temporaryDesc = this.descriptor; 292 Set<X509Certificate> certs = csRef.get(); 293 if (certs != null) { 294 if (descriptor.equals(temporaryDesc)) { 295 return certs; 296 } else { 297 // Use the new descriptor. 298 this.descriptor = descriptor; 299 } 300 } else { 301 // Try to use the cached store at first. 302 if (descriptor.equals(temporaryDesc)) { 303 ks = ksRef.get(); 304 } else { 305 // Use the new descriptor. 306 this.descriptor = descriptor; 307 } 308 } 309 310 // Reload the trust store if needed. 311 if (ks == null) { 312 if ((debug != null) && Debug.isOn("trustmanager")) { 313 System.out.println("Reload the trust store"); 314 } 315 ks = loadKeyStore(descriptor); 316 } 317 318 // Reload trust certs from the key store. 319 if ((debug != null) && Debug.isOn("trustmanager")) { 320 System.out.println("Reload trust certs"); 321 } 322 323 certs = loadTrustedCerts(ks); 324 if ((debug != null) && Debug.isOn("trustmanager")) { 325 System.out.println("Reloaded " + certs.size() + " trust certs"); 326 } 327 328 // Note that as ks is a local variable, it is not 329 // necessary to add it to the ksRef weak reference. 330 this.csRef = new WeakReference<>(certs); 331 332 return certs; 333 } 334 335 /** 336 * Load the the KeyStore as described in the specified descriptor. 337 */ 338 private static KeyStore loadKeyStore( 339 TrustStoreDescriptor descriptor) throws Exception { 340 if (!"NONE".equals(descriptor.storeName) && 341 descriptor.storeFile == null) { 342 343 // No file available, no KeyStore available. 344 if (debug != null && Debug.isOn("trustmanager")) { 345 System.out.println("No available key store"); 346 } 347 348 return null; 349 } 350 351 KeyStore ks; 352 if (descriptor.storeProvider.isEmpty()) { 353 ks = KeyStore.getInstance(descriptor.storeType); 354 } else { 355 ks = KeyStore.getInstance( 356 descriptor.storeType, descriptor.storeProvider); 357 } 358 359 char[] password = null; 360 if (!descriptor.storePassword.isEmpty()) { 361 password = descriptor.storePassword.toCharArray(); 362 } 363 364 if (!"NONE".equals(descriptor.storeName)) { 365 try (FileInputStream fis = AccessController.doPrivileged( 366 new OpenFileInputStreamAction(descriptor.storeFile))) { 367 ks.load(fis, password); 368 } catch (FileNotFoundException fnfe) { 369 // No file available, no KeyStore available. 370 if (debug != null && Debug.isOn("trustmanager")) { 371 System.out.println( 372 "Not available key store: " + descriptor.storeName); 373 } 374 375 return null; 376 } 377 } else { 378 ks.load(null, password); 379 } 380 381 return ks; 382 } 383 384 /** 385 * Load trusted certificates from the specified KeyStore. 386 */ 387 private static Set<X509Certificate> loadTrustedCerts(KeyStore ks) { 388 if (ks == null) { 389 return Collections.<X509Certificate>emptySet(); 390 } 391 392 return TrustStoreUtil.getTrustedCerts(ks); 393 } 394 } 395} 396