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