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