1 package org.jivesoftware.openfire.keystore; 2 3 import org.jivesoftware.openfire.XMPPServer; 4 import org.jivesoftware.openfire.container.BasicModule; 5 import org.jivesoftware.openfire.spi.ConnectionListener; 6 import org.jivesoftware.openfire.spi.ConnectionManagerImpl; 7 import org.jivesoftware.openfire.spi.ConnectionType; 8 import org.jivesoftware.util.CollectionUtils; 9 import org.jivesoftware.util.JiveGlobals; 10 import org.slf4j.Logger; 11 import org.slf4j.LoggerFactory; 12 13 import java.io.File; 14 import java.io.IOException; 15 import java.nio.file.Path; 16 import java.util.*; 17 import java.util.concurrent.ConcurrentHashMap; 18 import java.util.concurrent.ConcurrentMap; 19 import java.util.stream.Collectors; 20 21 /** 22 * A manager of certificate stores. 23 * 24 */ 25 // TODO Code duplication should be reduced. 26 // TODO Allow changing the store type. 27 public class CertificateStoreManager extends BasicModule 28 { 29 private final static Logger Log = LoggerFactory.getLogger( CertificateStoreManager.class ); 30 31 private final ConcurrentMap<ConnectionType, CertificateStoreConfiguration> typeToTrustStore = new ConcurrentHashMap<>(); 32 private final ConcurrentMap<ConnectionType, CertificateStoreConfiguration> typeToIdentityStore = new ConcurrentHashMap<>(); 33 private final ConcurrentMap<CertificateStoreConfiguration, IdentityStore> identityStores = new ConcurrentHashMap<>(); 34 private final ConcurrentMap<CertificateStoreConfiguration, TrustStore> trustStores = new ConcurrentHashMap<>(); 35 36 private CertificateStoreWatcher storeWatcher; 37 CertificateStoreManager( )38 public CertificateStoreManager( ) 39 { 40 super( "Certificate Store Manager" ); 41 } 42 43 @Override initialize( XMPPServer server )44 public synchronized void initialize( XMPPServer server ) 45 { 46 super.initialize( server ); 47 48 storeWatcher = new CertificateStoreWatcher(); 49 50 for ( final ConnectionType type : ConnectionType.values() ) 51 { 52 try 53 { 54 Log.debug( "(identity store for connection type '{}') Initializing store...", type ); 55 final CertificateStoreConfiguration identityStoreConfiguration = getIdentityStoreConfiguration( type ); 56 if ( !identityStores.containsKey( identityStoreConfiguration ) ) 57 { 58 final IdentityStore store = new IdentityStore( identityStoreConfiguration, false ); 59 identityStores.put( identityStoreConfiguration, store ); 60 storeWatcher.watch( store ); 61 } 62 typeToIdentityStore.put( type, identityStoreConfiguration ); 63 } 64 catch ( CertificateStoreConfigException | IOException e ) 65 { 66 Log.warn( "(identity store for connection type '{}') Unable to instantiate store ", type, e ); 67 } 68 69 try 70 { 71 Log.debug( "(trust store for connection type '{}') Initializing store...", type ); 72 final CertificateStoreConfiguration trustStoreConfiguration = getTrustStoreConfiguration( type ); 73 if ( !trustStores.containsKey( trustStoreConfiguration ) ) 74 { 75 final TrustStore store = new TrustStore( trustStoreConfiguration, false ); 76 trustStores.put( trustStoreConfiguration, store ); 77 storeWatcher.watch( store ); 78 } 79 typeToTrustStore.put( type, trustStoreConfiguration ); 80 } 81 catch ( CertificateStoreConfigException | IOException e ) 82 { 83 Log.warn( "(trust store for connection type '{}') Unable to instantiate store ", type, e ); 84 } 85 } 86 } 87 88 @Override destroy()89 public synchronized void destroy() 90 { 91 storeWatcher.destroy(); 92 typeToIdentityStore.clear(); 93 typeToTrustStore.clear(); 94 identityStores.clear(); 95 trustStores.clear(); 96 super.destroy(); 97 } 98 getIdentityStore( ConnectionType type )99 public IdentityStore getIdentityStore( ConnectionType type ) 100 { 101 final CertificateStoreConfiguration configuration = typeToIdentityStore.get( type ); 102 if (configuration == null) { 103 return null; 104 } 105 return identityStores.get( configuration ); 106 } 107 getTrustStore( ConnectionType type )108 public TrustStore getTrustStore( ConnectionType type ) 109 { 110 final CertificateStoreConfiguration configuration = typeToTrustStore.get( type ); 111 if (configuration == null) { 112 return null; 113 } 114 return trustStores.get( configuration ); 115 } 116 replaceIdentityStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent )117 public void replaceIdentityStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException 118 { 119 if ( type == null) 120 { 121 throw new IllegalArgumentException( "Argument 'type' cannot be null." ); 122 } 123 if ( configuration == null) 124 { 125 throw new IllegalArgumentException( "Argument 'configuration' cannot be null." ); 126 } 127 128 final CertificateStoreConfiguration oldConfig = typeToIdentityStore.get( type ); // can be null if persisted properties are invalid 129 130 if ( oldConfig == null || !oldConfig.equals( configuration ) ) 131 { 132 // If the new store is not already being used by any other type, it'll need to be registered. 133 if ( !identityStores.containsKey( configuration ) ) 134 { 135 // This constructor can throw an exception. If it does, the state of the manager should not have already changed. 136 final IdentityStore store = new IdentityStore( configuration, createIfAbsent ); 137 identityStores.put( configuration, store ); 138 storeWatcher.watch( store ); 139 } 140 141 typeToIdentityStore.put( type, configuration ); 142 143 144 // If the old store is not used by any other type, it can be shut down. 145 if ( oldConfig != null && !typeToIdentityStore.containsValue( oldConfig ) ) 146 { 147 final IdentityStore store = identityStores.remove( oldConfig ); 148 if ( store != null ) 149 { 150 storeWatcher.unwatch( store ); 151 } 152 } 153 154 // Update all connection listeners that were using the old configuration. 155 final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager()); 156 for ( ConnectionListener connectionListener : connectionManager.getListeners( type ) ) { 157 try { 158 connectionListener.setIdentityStoreConfiguration( configuration ); 159 } catch ( RuntimeException e ) { 160 Log.warn( "An exception occurred while trying to update the identity store configuration for connection type '" + type + "'", e ); 161 } 162 } 163 } 164 165 // Always store the new configuration in properties, to make sure that we override a potential fallback. 166 JiveGlobals.setProperty( type.getPrefix() + "keystore", configuration.getFile().getPath() ); // FIXME ensure that this is relative to Openfire home! 167 JiveGlobals.setProperty( type.getPrefix() + "keypass", new String( configuration.getPassword() ), true ); 168 } 169 replaceTrustStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent )170 public void replaceTrustStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException 171 { 172 if ( type == null) 173 { 174 throw new IllegalArgumentException( "Argument 'type' cannot be null." ); 175 } 176 if ( configuration == null) 177 { 178 throw new IllegalArgumentException( "Argument 'configuration' cannot be null." ); 179 } 180 181 final CertificateStoreConfiguration oldConfig = typeToTrustStore.get( type ); // can be null if persisted properties are invalid 182 183 if ( oldConfig == null || !oldConfig.equals( configuration ) ) 184 { 185 // If the new store is not already being used by any other type, it'll need to be registered. 186 if ( !trustStores.containsKey( configuration ) ) 187 { 188 // This constructor can throw an exception. If it does, the state of the manager should not have already changed. 189 final TrustStore store = new TrustStore( configuration, createIfAbsent ); 190 trustStores.put( configuration, store ); 191 storeWatcher.watch( store ); 192 } 193 194 typeToTrustStore.put( type, configuration ); 195 196 197 // If the old store is not used by any other type, it can be shut down. 198 if ( oldConfig != null && !typeToTrustStore.containsValue( oldConfig ) ) 199 { 200 final TrustStore store = trustStores.remove( oldConfig ); 201 if ( store != null ) 202 { 203 storeWatcher.unwatch( store ); 204 } 205 } 206 207 // Update all connection listeners that were using the old configuration. 208 final ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager()); 209 for ( ConnectionListener connectionListener : connectionManager.getListeners( type ) ) { 210 try { 211 connectionListener.setTrustStoreConfiguration( configuration ); 212 } catch ( RuntimeException e ) { 213 Log.warn( "An exception occurred while trying to update the trust store configuration for connection type '" + type + "'", e ); 214 } 215 } 216 217 } 218 219 // Always store the new configuration in properties, to make sure that we override a potential fallback. 220 JiveGlobals.setProperty( type.getPrefix() + "truststore", configuration.getFile().getPath() ); // FIXME ensure that this is relative to Openfire home! 221 JiveGlobals.setProperty( type.getPrefix() + "trustpass", new String( configuration.getPassword() ), true ); 222 } 223 getIdentityStoreConfiguration( ConnectionType type )224 public CertificateStoreConfiguration getIdentityStoreConfiguration( ConnectionType type ) throws IOException 225 { 226 // Getting individual properties might use fallbacks. It is assumed (but not asserted) that each property value 227 // is obtained from the same connectionType (which is either the argument to this method, or one of its 228 // fallbacks. 229 final String keyStoreType = getIdentityStoreType( type ); 230 final String password = getIdentityStorePassword( type ); 231 final String location = getIdentityStoreLocation( type ); 232 final String backupDirectory = getIdentityStoreBackupDirectory( type ); 233 final File file = canonicalize( location ); 234 final File dir = canonicalize( backupDirectory ); 235 236 return new CertificateStoreConfiguration( keyStoreType, file, password.toCharArray(), dir ); 237 } 238 getTrustStoreConfiguration( ConnectionType type )239 public CertificateStoreConfiguration getTrustStoreConfiguration( ConnectionType type ) throws IOException 240 { 241 // Getting individual properties might use fallbacks. It is assumed (but not asserted) that each property value 242 // is obtained from the same connectionType (which is either the argument to this method, or one of its 243 // fallbacks. 244 final String keyStoreType = getTrustStoreType( type ); 245 final String password = getTrustStorePassword( type ); 246 final String location = getTrustStoreLocation( type ); 247 final String backupDirectory = getTrustStoreBackupDirectory( type ); 248 final File file = canonicalize( location ); 249 final File dir = canonicalize( backupDirectory ); 250 251 return new CertificateStoreConfiguration( keyStoreType, file, password.toCharArray(), dir ); 252 } 253 254 /** 255 * Creates a backup of all files that back any of the certificate stores. 256 * 257 * Each certificate store can be configured to use a distinct file, as well as use a distinct backup location. 258 * In practise, there will be a lot of overlap. This implementation creates a backup (by copying the file) for 259 * each unique file/backup-location combination in the collection of all certificate stores. 260 * @return the paths the store was backed up to 261 * @throws IOException if the store could not be backed up 262 */ backup()263 public Collection<Path> backup() throws IOException 264 { 265 // Create a collection that holds all of the certificate stores. 266 final Collection<CertificateStore> allStores = new ArrayList<>(); 267 allStores.addAll( identityStores.values() ); 268 allStores.addAll( trustStores.values() ); 269 270 // Extract the set of unique file/backup-directory combinations. This prevents Openfire from creating duplicate backup files. 271 final Set<CertificateStore> unique = allStores.stream().filter( 272 CollectionUtils.distinctByKey( 273 store -> store.getConfiguration().getFile().getAbsolutePath() + "|" + store.configuration.backupDirectory.getAbsolutePath() ) ) 274 .collect( Collectors.toSet() ); 275 276 // Trigger a backup for each of the unique combinations, and record the unique backup locations 277 final Collection<Path> backups = unique.stream() 278 .map( CertificateStore::backup ) 279 .filter( Objects::nonNull ) 280 .distinct() 281 .sorted() 282 .collect( Collectors.toList() ); 283 284 // If the number of backups is smaller than the number of unique file/backup-directory combinations, something went wrong! 285 if ( unique.size() != backups.size() ) { 286 throw new IOException( "Unable to create (all) backups!" ); 287 } 288 289 return backups; 290 } 291 292 /** 293 * The KeyStore type (jks, jceks, pkcs12, etc) for the trust store for connections of a particular type. 294 * 295 * @param type the connection type 296 * @return a store type (never null). 297 * @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore">Java Cryptography Architecture Standard Algorithm Name Documentation</a> 298 */ getTrustStoreType( ConnectionType type )299 public static String getTrustStoreType( ConnectionType type ) 300 { 301 final String propertyName = type.getPrefix() + "trustStoreType"; 302 final String defaultValue = getKeyStoreType( type ); 303 304 if ( type.getFallback() == null ) 305 { 306 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 307 } 308 else 309 { 310 return JiveGlobals.getProperty( propertyName, getTrustStoreType( type.getFallback() ) ).trim(); 311 } 312 } 313 setTrustStoreType( ConnectionType type, String keyStoreType )314 static void setTrustStoreType( ConnectionType type, String keyStoreType ) 315 { 316 // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value). 317 JiveGlobals.setProperty( type.getPrefix() + "trustStoreType", keyStoreType ); 318 319 final String oldKeyStoreType = getTrustStoreType( type ); 320 if ( oldKeyStoreType.equals( keyStoreType ) ) 321 { 322 Log.debug( "Ignoring Trust Store type change request (to '{}'): listener already in this state.", keyStoreType ); 323 return; 324 } 325 326 Log.debug( "Changing Trust Store type from '{}' to '{}'.", oldKeyStoreType, keyStoreType ); 327 // TODO shouldn't this do something? 328 } 329 330 /** 331 * The KeyStore type (jks, jceks, pkcs12, etc) for the identity store for connections of a particular type. 332 * 333 * @param type the connection type 334 * @return a store type (never null). 335 * @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore">Java Cryptography Architecture Standard Algorithm Name Documentation</a> 336 */ getIdentityStoreType( ConnectionType type )337 public static String getIdentityStoreType( ConnectionType type ) 338 { 339 final String propertyName = type.getPrefix() + "identityStoreType"; 340 final String defaultValue = getKeyStoreType( type ); 341 342 if ( type.getFallback() == null ) 343 { 344 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 345 } 346 else 347 { 348 return JiveGlobals.getProperty( propertyName, getIdentityStoreType( type.getFallback() ) ).trim(); 349 } 350 } 351 setIdentityStoreType( ConnectionType type, String keyStoreType )352 static void setIdentityStoreType( ConnectionType type, String keyStoreType ) 353 { 354 // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value). 355 JiveGlobals.setProperty( type.getPrefix() + "identityStoreType", keyStoreType ); 356 357 final String oldKeyStoreType = getIdentityStoreType( type ); 358 if ( oldKeyStoreType.equals( keyStoreType ) ) 359 { 360 Log.debug( "Ignoring Identity Store type change request (to '{}'): listener already in this state.", keyStoreType ); 361 return; 362 } 363 364 Log.debug( "Changing Identity Store type from '{}' to '{}'.", oldKeyStoreType, keyStoreType ); 365 // TODO shouldn't this do something? 366 } 367 368 /** 369 * The KeyStore type (jks, jceks, pkcs12, etc) for the identity and trust store for connections of a particular type. 370 * 371 * @param type the connection type 372 * @return a store type (never null). 373 * @see <a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#KeyStore">Java Cryptography Architecture Standard Algorithm Name Documentation</a> 374 * @deprecated use either {@link #getTrustStoreType(ConnectionType)} or {@link #getIdentityStoreType(ConnectionType)} 375 */ 376 @Deprecated getKeyStoreType( ConnectionType type )377 public static String getKeyStoreType( ConnectionType type ) 378 { 379 final String propertyName = type.getPrefix() + "storeType"; 380 final String defaultValue = "jks"; 381 382 if ( type.getFallback() == null ) 383 { 384 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 385 } 386 else 387 { 388 return JiveGlobals.getProperty( propertyName, getKeyStoreType( type.getFallback() ) ).trim(); 389 } 390 } 391 setKeyStoreType( ConnectionType type, String keyStoreType )392 static void setKeyStoreType( ConnectionType type, String keyStoreType ) 393 { 394 // Always set the property explicitly even if it appears the equal to the old value (the old value might be a fallback value). 395 JiveGlobals.setProperty( type.getPrefix() + "storeType", keyStoreType ); 396 397 final String oldKeyStoreType = getKeyStoreType( type ); 398 if ( oldKeyStoreType.equals( keyStoreType ) ) 399 { 400 Log.debug( "Ignoring KeyStore type change request (to '{}'): listener already in this state.", keyStoreType ); 401 return; 402 } 403 404 Log.debug( "Changing KeyStore type from '{}' to '{}'.", oldKeyStoreType, keyStoreType ); 405 // TODO shouldn't this do something? 406 } 407 408 /** 409 * The password of the identity store for connection created by this listener. 410 * 411 * @return a password (never null). 412 */ getIdentityStorePassword( ConnectionType type )413 static String getIdentityStorePassword( ConnectionType type ) 414 { 415 final String propertyName = type.getPrefix() + "keypass"; 416 final String defaultValue = "changeit"; 417 418 if ( type.getFallback() == null ) 419 { 420 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 421 } 422 else 423 { 424 return JiveGlobals.getProperty( propertyName, getIdentityStorePassword( type.getFallback() ) ).trim(); 425 } 426 } 427 428 /** 429 * The password of the trust store for connections created by this listener. 430 * 431 * @return a password (never null). 432 */ getTrustStorePassword( ConnectionType type )433 static String getTrustStorePassword( ConnectionType type ) 434 { 435 final String propertyName = type.getPrefix() + "trustpass"; 436 final String defaultValue = "changeit"; 437 438 if ( type.getFallback() == null ) 439 { 440 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 441 } 442 else 443 { 444 return JiveGlobals.getProperty( propertyName, getTrustStorePassword( type.getFallback() ) ).trim(); 445 } 446 } 447 448 /** 449 * The location (relative to OPENFIRE_HOME) of the identity store for connections created by this listener. 450 * 451 * @return a path (never null). 452 */ getIdentityStoreLocation( ConnectionType type )453 static String getIdentityStoreLocation( ConnectionType type ) 454 { 455 final String propertyName = type.getPrefix() + "keystore"; 456 final String defaultValue = "resources" + File.separator + "security" + File.separator + "keystore"; 457 458 if ( type.getFallback() == null ) 459 { 460 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 461 } 462 else 463 { 464 return JiveGlobals.getProperty( propertyName, getIdentityStoreLocation( type.getFallback() ) ).trim(); 465 } 466 } 467 468 /** 469 * The location (relative to OPENFIRE_HOME) of the trust store for connections created by this listener. 470 * 471 * @return a path (never null). 472 */ getTrustStoreLocation( ConnectionType type )473 static String getTrustStoreLocation( ConnectionType type ) 474 { 475 final String propertyName = type.getPrefix() + "truststore"; 476 final String defaultValue; 477 478 // OF-1191: For client-oriented connection types, Openfire traditionally uses a different truststore. 479 if ( type.isClientOriented() ) 480 { 481 defaultValue = "resources" + File.separator + "security" + File.separator + "client.truststore"; 482 } 483 else 484 { 485 defaultValue = "resources" + File.separator + "security" + File.separator + "truststore"; 486 } 487 488 if ( type.getFallback() == null ) 489 { 490 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 491 } 492 else 493 { 494 return JiveGlobals.getProperty( propertyName, getTrustStoreLocation( type.getFallback() ) ).trim(); 495 } 496 } 497 498 /** 499 * The location (relative to OPENFIRE_HOME) of the directory that holds backups for identity stores. 500 * 501 * @param type the connection type 502 * @return a path (never null). 503 */ getIdentityStoreBackupDirectory( ConnectionType type )504 public static String getIdentityStoreBackupDirectory( ConnectionType type ) 505 { 506 final String propertyName = type.getPrefix() + "backup.keystore.location"; 507 final String defaultValue = "resources" + File.separator + "security" + File.separator + "archive" + File.separator; 508 509 if ( type.getFallback() == null ) 510 { 511 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 512 } 513 else 514 { 515 return JiveGlobals.getProperty( propertyName, getIdentityStoreBackupDirectory( type.getFallback() ) ).trim(); 516 } 517 } 518 519 /** 520 * The location (relative to OPENFIRE_HOME) of the directory that holds backups for trust stores. 521 * 522 * @param type the connection type 523 * @return a path (never null). 524 */ getTrustStoreBackupDirectory( ConnectionType type )525 public static String getTrustStoreBackupDirectory( ConnectionType type ) 526 { 527 final String propertyName = type.getPrefix() + "backup.truststore.location"; 528 final String defaultValue = "resources" + File.separator + "security" + File.separator + "archive" + File.separator; 529 530 if ( type.getFallback() == null ) 531 { 532 return JiveGlobals.getProperty( propertyName, defaultValue ).trim(); 533 } 534 else 535 { 536 return JiveGlobals.getProperty( propertyName, getTrustStoreBackupDirectory( type.getFallback() ) ).trim(); 537 } 538 } 539 540 /** 541 * Canonicalizes a path. When the provided path is a relative path, it is interpreted as to be relative to the home 542 * directory of Openfire. 543 * 544 * @param path A path (cannot be null) 545 * @return A canonical representation of the path. 546 */ canonicalize( String path )547 static File canonicalize( String path ) throws IOException 548 { 549 File file = new File( path ); 550 if (!file.isAbsolute()) { 551 file = new File( JiveGlobals.getHomeDirectory() + File.separator + path ); 552 } 553 554 return file; 555 } 556 557 /** 558 * Checks if Openfire is configured to use the same set of three keystore files for all connection types (one 559 * identity store, and two trust stores - one for client-based connections, and one for server/component-based 560 * connections). 561 * 562 * This method will return 'false' when running Openfire without changes to its default keystore configuration. If 563 * changes are made to use different keystores for at least one connection type, this method returns 'true'. 564 * 565 * @return true if Openfire is using different keystores based on the type of connection, false when running with the default store configuration. 566 * @throws IOException if there was an IO error 567 */ usesDistinctConfigurationForEachType()568 public boolean usesDistinctConfigurationForEachType() throws IOException 569 { 570 CertificateStoreConfiguration identityStoreConfiguration = null; 571 CertificateStoreConfiguration c2sTrustStoreConfiguration = null; 572 CertificateStoreConfiguration s2sTrustStoreConfiguration = null; 573 for ( ConnectionType connectionType : ConnectionType.values() ) 574 { 575 // Identity stores 576 if ( identityStoreConfiguration == null ) 577 { 578 identityStoreConfiguration = getIdentityStoreConfiguration( connectionType ); 579 } 580 if ( !identityStoreConfiguration.equals( getIdentityStoreConfiguration( connectionType ) ) ) 581 { 582 return true; 583 } 584 585 // Client-to-Server trust stores 586 if ( connectionType.isClientOriented() ) 587 { 588 if ( c2sTrustStoreConfiguration == null ) 589 { 590 c2sTrustStoreConfiguration = getTrustStoreConfiguration( connectionType ); 591 } 592 if ( !c2sTrustStoreConfiguration.equals( getTrustStoreConfiguration( connectionType ) ) ) 593 { 594 return true; 595 } 596 } 597 else 598 // Server-to-Server trust stores (includes component connections) 599 { 600 if ( s2sTrustStoreConfiguration == null ) 601 { 602 s2sTrustStoreConfiguration = getTrustStoreConfiguration( connectionType ); 603 } 604 if ( !s2sTrustStoreConfiguration.equals( getTrustStoreConfiguration( connectionType ) ) ) 605 { 606 return true; 607 } 608 } 609 } 610 611 return false; 612 } 613 } 614