Commit fc648e2b authored by Guus der Kinderen's avatar Guus der Kinderen

Merge pull request #325 from guusdk/OF-946

OF-946: Openfire should allow for more than one set of key stores.
parents 06876d60 25c09adb
...@@ -606,12 +606,26 @@ tab.server.descr=Click to manage server settings ...@@ -606,12 +606,26 @@ tab.server.descr=Click to manage server settings
sidebar.transfer-proxy=File Transfer Settings sidebar.transfer-proxy=File Transfer Settings
sidebar.transfer-proxy.descr=Click to view file tranfer settings sidebar.transfer-proxy.descr=Click to view file tranfer settings
sidebar.sidebar-certificates=TLS/SSL Certificates sidebar.sidebar-certificates=TLS/SSL Certificates
sidebar.security-keystore=Openfire Certificates sidebar.sidebar-certificates-keys=Openfire Certificates
sidebar.security-keystore.descr=Click to manage certificates for this Openfire server. sidebar.sidebar-certificates-keys-submenu=Connectivity Type
sidebar.security-truststore-c2s=Client Truststore sidebar.security-keystore-socket=Socket
sidebar.security-truststore-c2s.descr=Click to manage certificates used in client-to-server communication sidebar.security-keystore-socket.descr=Click to manage certificates used for socket-based connections on this Openfire server.
sidebar.security-truststore-s2s=Server Truststore sidebar.security-keystore-bosh=BOSH
sidebar.security-truststore-s2s.descr=Click to manage certificates used in server-to-server communication sidebar.security-keystore-bosh.descr=Click to manage certificates used for BOSH-based (HTTP binding) on this Openfire server.
sidebar.security-keystore-administrative=Administrative
sidebar.security-keystore-administrative.descr=Click to manage certificates used for administrative interfaces on this Openfire server.
sidebar.security-truststore-socket-c2s=Client (socket) Truststore
sidebar.security-truststore-socket-c2s.descr=Click to manage certificates used for socket-based, client-to-server communication.
sidebar.security-truststore-socket-s2s=Server (socket) Truststore
sidebar.security-truststore-socket-s2s.descr=Click to manage certificates used for socket-based, server-to-server communication.
sidebar.security-truststore-bosh-c2s=Client (BOSH) Truststore
sidebar.security-truststore-bosh-c2s.descr=Click to manage certificates used for BOSH-based (HTTP binding), client-to-server communication.
sidebar.security-truststore-bosh-s2s=Server (BOSH) Truststore
sidebar.security-truststore-bosh-s2s.descr=Click to manage certificates used for BOSH-based (HTTP binding), server-to-server communication.
sidebar.security-truststore-administrative-c2s=Client (administrative) Truststore
sidebar.security-truststore-administrative-c2s.descr=Click to manage certificates used for administrative, client-to-server communication.
sidebar.security-truststore-administrative-s2s=Server (administrative) Truststore
sidebar.security-truststore-administrative-s2s.descr=Click to manage certificates used for administrative, server-to-server communication.
sidebar.sidebar-media-services=Media Services sidebar.sidebar-media-services=Media Services
sidebar.media-proxy=Media Proxy sidebar.media-proxy=Media Proxy
sidebar.media-proxy.descr=Click to view media proxy settings. sidebar.media-proxy.descr=Click to view media proxy settings.
...@@ -2291,7 +2305,6 @@ ssl.settings.server.dialback=Server Dialback: ...@@ -2291,7 +2305,6 @@ ssl.settings.server.dialback=Server Dialback:
ssl.settings.server.customTLS=TLS method: ssl.settings.server.customTLS=TLS method:
# Generic certificate-related messages # Generic certificate-related messages
ssl.certificates.added_updated=Certificate added or modified successfully. ssl.certificates.added_updated=Certificate added or modified successfully.
ssl.certificates.deleted=Certificate deleted successfully. ssl.certificates.deleted=Certificate deleted successfully.
ssl.certificates.generated=Certificates generated successfully. ssl.certificates.generated=Certificates generated successfully.
...@@ -2409,6 +2422,27 @@ ssl.signing-request.requests_info=Below you will find the signing requests gener ...@@ -2409,6 +2422,27 @@ ssl.signing-request.requests_info=Below you will find the signing requests gener
ssl.signing-request.alias=Alias ssl.signing-request.alias=Alias
ssl.signing-request.signing-request=Signing Request ssl.signing-request.signing-request=Signing Request
# Certificate management
certificate-management.purpose.SOCKETBASED_IDENTITYSTORE.title=Identity Store (socket)
certificate-management.purpose.SOCKETBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for plain socket-based connections.
certificate-management.purpose.SOCKETBASED_S2S_TRUSTSTORE.title=Server-to-Server Trust Store (socket)
certificate-management.purpose.SOCKETBASED_S2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify other XMPP servers. These certificates are used during server-to-server federation via plain socket-based connections.
certificate-management.purpose.SOCKETBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (socket)
certificate-management.purpose.SOCKETBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via plain socket-based connections.
certificate-management.purpose.BOSHBASED_IDENTITYSTORE.title=Identity Store (BOSH/HTTP-bind)
certificate-management.purpose.BOSHBASED_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for BOSH (HTTP-bind) connections.
certificate-management.purpose.BOSHBASED_C2S_TRUSTSTORE.title=Client-to-Server Trust Store (BOSH/HTTP-bind)
certificate-management.purpose.BOSHBASED_C2S_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify XMPP clients. These certificates are used during mutual authentication via BOSH (HTTP-bind) connections.
certificate-management.purpose.ADMINISTRATIVE_IDENTITYSTORE.title=Administrative Identity Store
certificate-management.purpose.ADMINISTRATIVE_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used for connections to administrative services (eg: user providers).
certificate-management.purpose.ADMINISTRATIVE_TRUSTSTORE.title=Administrative Trust Store
certificate-management.purpose.ADMINISTRATIVE_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify applications/servers that provide administrative functionality (eg: user providers).
certificate-management.purpose.WEBADMIN_IDENTITYSTORE.title=Admin Panel Identity Store
certificate-management.purpose.WEBADMIN_IDENTITYSTORE.description=This store contains certificates that identify this Openfire instance, used by the Web-Admin panel (when accessed via HTTPS).
certificate-management.purpose.WEBADMIN_TRUSTSTORE.title=Admin Panel Trust Store
certificate-management.purpose.WEBADMIN_TRUSTSTORE.description=This store contains certificates of security authorities that are trusted to identify parties that wish to interact with the Openfire Web-Admin.
# Restart HTTP server # Restart HTTP server
server-restart.title=HTTP Server Restart server-restart.title=HTTP Server Restart
...@@ -3071,7 +3105,9 @@ mediaproxy.summary.stopbutton = Stop Active Sessions ...@@ -3071,7 +3105,9 @@ mediaproxy.summary.stopbutton = Stop Active Sessions
# Import keystore certificate page # Import keystore certificate page
ssl.import.certificate.keystore.title=Import Signed Certificate ssl.import.certificate.keystore.socket.title=Import Signed Certificate for Socket-based Communication
ssl.import.certificate.keystore.bosh.title=Import Signed Certificate for BOSH-based Communication
ssl.import.certificate.keystore.administrative.title=Import Signed Certificate for Administrative Purposes
ssl.import.certificate.keystore.info=Use the form below to import a private key and certificate that was provided by a \ ssl.import.certificate.keystore.info=Use the form below to import a private key and certificate that was provided by a \
Certificate Authority. Make sure that root certificates of the CA signing the certificate are present in the \ Certificate Authority. Make sure that root certificates of the CA signing the certificate are present in the \
truststore. Otherwise you will need to manually import them using the "keytool" command line tool. If you are \ truststore. Otherwise you will need to manually import them using the "keytool" command line tool. If you are \
......
...@@ -4,6 +4,7 @@ import org.bouncycastle.asn1.*; ...@@ -4,6 +4,7 @@ import org.bouncycastle.asn1.*;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import java.io.IOException; import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
......
...@@ -17,30 +17,6 @@ ...@@ -17,30 +17,6 @@
package org.jivesoftware.openfire; package org.jivesoftware.openfire;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.Node; import org.dom4j.Node;
...@@ -50,7 +26,6 @@ import org.jivesoftware.openfire.admin.AdminManager; ...@@ -50,7 +26,6 @@ import org.jivesoftware.openfire.admin.AdminManager;
import org.jivesoftware.openfire.audit.AuditManager; import org.jivesoftware.openfire.audit.AuditManager;
import org.jivesoftware.openfire.audit.spi.AuditManagerImpl; import org.jivesoftware.openfire.audit.spi.AuditManagerImpl;
import org.jivesoftware.openfire.auth.ScramUtils; import org.jivesoftware.openfire.auth.ScramUtils;
import org.jivesoftware.openfire.clearspace.ClearspaceManager;
import org.jivesoftware.openfire.cluster.ClusterManager; import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.cluster.NodeID; import org.jivesoftware.openfire.cluster.NodeID;
import org.jivesoftware.openfire.commands.AdHocCommandHandler; import org.jivesoftware.openfire.commands.AdHocCommandHandler;
...@@ -58,69 +33,43 @@ import org.jivesoftware.openfire.component.InternalComponentManager; ...@@ -58,69 +33,43 @@ import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.container.AdminConsolePlugin; import org.jivesoftware.openfire.container.AdminConsolePlugin;
import org.jivesoftware.openfire.container.Module; import org.jivesoftware.openfire.container.Module;
import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.disco.IQDiscoInfoHandler; import org.jivesoftware.openfire.disco.*;
import org.jivesoftware.openfire.disco.IQDiscoItemsHandler;
import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
import org.jivesoftware.openfire.disco.ServerIdentitiesProvider;
import org.jivesoftware.openfire.disco.ServerItemsProvider;
import org.jivesoftware.openfire.disco.UserIdentitiesProvider;
import org.jivesoftware.openfire.disco.UserItemsProvider;
import org.jivesoftware.openfire.filetransfer.DefaultFileTransferManager; import org.jivesoftware.openfire.filetransfer.DefaultFileTransferManager;
import org.jivesoftware.openfire.filetransfer.FileTransferManager; import org.jivesoftware.openfire.filetransfer.FileTransferManager;
import org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy; import org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy;
import org.jivesoftware.openfire.handler.IQAuthHandler; import org.jivesoftware.openfire.handler.*;
import org.jivesoftware.openfire.handler.IQBindHandler; import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.handler.IQEntityTimeHandler; import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.handler.IQHandler;
import org.jivesoftware.openfire.handler.IQLastActivityHandler;
import org.jivesoftware.openfire.handler.IQMessageCarbonsHandler;
import org.jivesoftware.openfire.handler.IQOfflineMessagesHandler;
import org.jivesoftware.openfire.handler.IQPingHandler;
import org.jivesoftware.openfire.handler.IQPrivacyHandler;
import org.jivesoftware.openfire.handler.IQPrivateHandler;
import org.jivesoftware.openfire.handler.IQRegisterHandler;
import org.jivesoftware.openfire.handler.IQRosterHandler;
import org.jivesoftware.openfire.handler.IQSessionEstablishmentHandler;
import org.jivesoftware.openfire.handler.IQSharedGroupHandler;
import org.jivesoftware.openfire.handler.IQTimeHandler;
import org.jivesoftware.openfire.handler.IQVersionHandler;
import org.jivesoftware.openfire.handler.IQvCardHandler;
import org.jivesoftware.openfire.handler.PresenceSubscribeHandler;
import org.jivesoftware.openfire.handler.PresenceUpdateHandler;
import org.jivesoftware.openfire.lockout.LockOutManager; import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.mediaproxy.MediaProxyService; import org.jivesoftware.openfire.mediaproxy.MediaProxyService;
import org.jivesoftware.openfire.muc.MultiUserChatManager; import org.jivesoftware.openfire.muc.MultiUserChatManager;
import org.jivesoftware.openfire.net.MulticastDNSService;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.ServerTrafficCounter; import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.pep.IQPEPHandler; import org.jivesoftware.openfire.pep.IQPEPHandler;
import org.jivesoftware.openfire.pep.IQPEPOwnerHandler;
import org.jivesoftware.openfire.pubsub.PubSubModule; import org.jivesoftware.openfire.pubsub.PubSubModule;
import org.jivesoftware.openfire.roster.RosterManager; import org.jivesoftware.openfire.roster.RosterManager;
import org.jivesoftware.openfire.session.RemoteSessionLocator; import org.jivesoftware.openfire.session.RemoteSessionLocator;
import org.jivesoftware.openfire.spi.ConnectionManagerImpl;
import org.jivesoftware.openfire.spi.PacketDelivererImpl;
import org.jivesoftware.openfire.spi.PacketRouterImpl;
import org.jivesoftware.openfire.spi.PacketTransporterImpl;
import org.jivesoftware.openfire.spi.PresenceManagerImpl;
import org.jivesoftware.openfire.spi.RoutingTableImpl;
import org.jivesoftware.openfire.spi.XMPPServerInfoImpl; import org.jivesoftware.openfire.spi.XMPPServerInfoImpl;
import org.jivesoftware.openfire.transport.TransportHandler; import org.jivesoftware.openfire.transport.TransportHandler;
import org.jivesoftware.openfire.update.UpdateManager; import org.jivesoftware.openfire.update.UpdateManager;
import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.vcard.VCardManager; import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.util.CertificateManager; import org.jivesoftware.util.*;
import org.jivesoftware.util.InitializationException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.TaskEngine;
import org.jivesoftware.util.Version;
import org.jivesoftware.util.cache.CacheFactory; import org.jivesoftware.util.cache.CacheFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.io.*;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* The main XMPP server that will load, initialize and start all the server's * The main XMPP server that will load, initialize and start all the server's
* modules. The server is unique in the JVM and could be obtained by using the * modules. The server is unique in the JVM and could be obtained by using the
...@@ -421,31 +370,14 @@ public class XMPPServer { ...@@ -421,31 +370,14 @@ public class XMPPServer {
JiveGlobals.setProperty(propName, JiveGlobals.getXMLProperty(propName)); JiveGlobals.setProperty(propName, JiveGlobals.getXMLProperty(propName));
} }
} }
// Set default SASL SCRAM-SHA-1 iteration count // Set default SASL SCRAM-SHA-1 iteration count
JiveGlobals.setProperty("sasl.scram-sha-1.iteration-count", Integer.toString(ScramUtils.DEFAULT_ITERATION_COUNT)); JiveGlobals.setProperty("sasl.scram-sha-1.iteration-count", Integer.toString(ScramUtils.DEFAULT_ITERATION_COUNT));
// Update certificates (if required) // Update certificates (if required)
try { try {
// Check if keystore already has certificates for current domain // Check if keystore already has certificates for current domain
KeyStore ksKeys = SSLConfig.getKeyStore(); final IdentityStoreConfig storeConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
boolean dsaFound = CertificateManager.isDSACertificate(ksKeys, name); storeConfig.ensureDomainCertificates( "DSA", "RSA" );
boolean rsaFound = CertificateManager.isRSACertificate(ksKeys, name);
// No certificates were found so create new self-signed certificates
if (!dsaFound) {
CertificateManager.createDSACert(ksKeys, SSLConfig.getKeyPassword(),
name + "_dsa", "cn=" + name, "cn=" + name, "*." + name);
}
if (!rsaFound) {
CertificateManager.createRSACert(ksKeys, SSLConfig.getKeyPassword(),
name + "_rsa", "cn=" + name, "cn=" + name, "*." + name);
}
// Save new certificates into the key store
if (!dsaFound || !rsaFound) {
SSLConfig.saveStores();
}
} catch (Exception e) { } catch (Exception e) {
logger.error("Error generating self-signed certificates", e); logger.error("Error generating self-signed certificates", e);
} }
...@@ -580,7 +512,7 @@ public class XMPPServer { ...@@ -580,7 +512,7 @@ public class XMPPServer {
/** /**
* Loads a module. * Loads a module.
* *
* @param module the name of the class that implements the Module interface. * @param moduleName the name of the class that implements the Module interface.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void loadModule(String moduleName, String moduleImpl) { private void loadModule(String moduleName, String moduleImpl) {
......
...@@ -35,6 +35,7 @@ import org.apache.commons.httpclient.ConnectTimeoutException; ...@@ -35,6 +35,7 @@ import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError; import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -65,7 +66,12 @@ public class SSLProtocolSocketFactory implements SecureProtocolSocketFactory { ...@@ -65,7 +66,12 @@ public class SSLProtocolSocketFactory implements SecureProtocolSocketFactory {
SSLContext context = SSLContext.getInstance("SSL"); SSLContext context = SSLContext.getInstance("SSL");
context.init( context.init(
null, null,
new TrustManager[]{new ClearspaceX509TrustManager(host, manager.getProperties(), SSLConfig.gets2sTrustStore())}, new TrustManager[] {
new ClearspaceX509TrustManager(
host,
manager.getProperties(),
SSLConfig.getStore( Purpose.ADMINISTRATIVE_TRUSTSTORE ) )
},
null); null);
return context; return context;
} catch (Exception e) { } catch (Exception e) {
......
...@@ -42,6 +42,9 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; ...@@ -42,6 +42,9 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.jivesoftware.openfire.JMXManager; import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreConfig;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.util.CertificateEventListener; import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.CertificateManager; import org.jivesoftware.util.CertificateManager;
...@@ -113,18 +116,14 @@ public class AdminConsolePlugin implements Plugin { ...@@ -113,18 +116,14 @@ public class AdminConsolePlugin implements Plugin {
adminServer.addBean(jmx.getContainer()); adminServer.addBean(jmx.getContainer());
} }
ServerConnector httpConnector = null;
ServerConnector httpsConnector = null;
HttpConfiguration httpConfig = null;
// Create connector for http traffic if it's enabled. // Create connector for http traffic if it's enabled.
if (adminPort > 0) { if (adminPort > 0) {
httpConfig = new HttpConfiguration(); final HttpConfiguration httpConfig = new HttpConfiguration();
// Do not send Jetty info in HTTP headers // Do not send Jetty info in HTTP headers
httpConfig.setSendServerVersion( false ); httpConfig.setSendServerVersion( false );
httpConnector = new ServerConnector(adminServer, null, null, null, -1, serverThreads,
new HttpConnectionFactory(httpConfig)); final ServerConnector httpConnector = new ServerConnector(adminServer, null, null, null, -1, serverThreads, new HttpConnectionFactory(httpConfig));
// Listen on a specific network interface if it has been set. // Listen on a specific network interface if it has been set.
String bindInterface = getBindInterface(); String bindInterface = getBindInterface();
...@@ -136,24 +135,29 @@ public class AdminConsolePlugin implements Plugin { ...@@ -136,24 +135,29 @@ public class AdminConsolePlugin implements Plugin {
// Create a connector for https traffic if it's enabled. // Create a connector for https traffic if it's enabled.
sslEnabled = false; sslEnabled = false;
try { try {
if (adminSecurePort > 0 && CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), "*")) final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.WEBADMIN_IDENTITYSTORE );
if (adminSecurePort > 0 && identityStoreConfig.getStore().aliases().hasMoreElements() )
{ {
if (!CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), if ( !identityStoreConfig.containsDomainCertificate( "RSA" )) {
XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
Log.warn("Admin console: Using RSA certificates but they are not valid for the hosted domain"); Log.warn("Admin console: Using RSA certificates but they are not valid for the hosted domain");
} }
final CertificateStoreConfig trustStoreConfig = SSLConfig.getInstance().getStoreConfig( Purpose.WEBADMIN_TRUSTSTORE );
final SslContextFactory sslContextFactory = new SslContextFactory(); final SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.addExcludeProtocols("SSLv3"); sslContextFactory.setTrustStorePath( trustStoreConfig.getCanonicalPath() );
sslContextFactory.setTrustStorePassword(SSLConfig.gets2sTrustPassword()); sslContextFactory.setTrustStorePassword( trustStoreConfig.getPassword() );
sslContextFactory.setTrustStoreType(SSLConfig.getStoreType()); sslContextFactory.setTrustStoreType( trustStoreConfig.getType() );
sslContextFactory.setKeyStorePath(SSLConfig.getKeystoreLocation()); sslContextFactory.setKeyStorePath( identityStoreConfig.getCanonicalPath() );
sslContextFactory.setNeedClientAuth(false); sslContextFactory.setKeyStorePassword( identityStoreConfig.getPassword() );
sslContextFactory.setWantClientAuth(false); sslContextFactory.setKeyStoreType( identityStoreConfig.getType() );
sslContextFactory.setKeyStorePassword(SSLConfig.getKeyPassword());
sslContextFactory.setKeyStoreType(SSLConfig.getStoreType()); sslContextFactory.addExcludeProtocols( "SSLv3" );
sslContextFactory.setNeedClientAuth( false );
if ("npn".equals(JiveGlobals.getXMLProperty("spdy.protocol", ""))) sslContextFactory.setWantClientAuth( false );
final ServerConnector httpsConnector;
if ("npn".equals(JiveGlobals.getXMLProperty("spdy.protocol", "")))
{ {
httpsConnector = new HTTPSPDYServerConnector(adminServer, sslContextFactory); httpsConnector = new HTTPSPDYServerConnector(adminServer, sslContextFactory);
......
...@@ -57,6 +57,9 @@ import org.eclipse.jetty.webapp.WebAppContext; ...@@ -57,6 +57,9 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.jivesoftware.openfire.Connection; import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.JMXManager; import org.jivesoftware.openfire.JMXManager;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.CertificateStoreConfig;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.session.ConnectionSettings; import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.util.CertificateEventListener; import org.jivesoftware.util.CertificateEventListener;
...@@ -243,21 +246,26 @@ public final class HttpBindManager { ...@@ -243,21 +246,26 @@ public final class HttpBindManager {
private void createSSLConnector(int securePort, int bindThreads) { private void createSSLConnector(int securePort, int bindThreads) {
httpsConnector = null; httpsConnector = null;
try { try {
if (securePort > 0 && CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), "*")) { final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.BOSHBASED_IDENTITYSTORE );
if (!CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), final KeyStore keyStore = identityStoreConfig.getStore();
XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
if (securePort > 0 && identityStoreConfig.getStore().aliases().hasMoreElements() ) {
if ( !identityStoreConfig.containsDomainCertificate( "RSA" ) ) {
Log.warn("HTTP binding: Using RSA certificates but they are not valid for " + Log.warn("HTTP binding: Using RSA certificates but they are not valid for " +
"the hosted domain"); "the hosted domain");
} }
final CertificateStoreConfig trustStoreConfig = SSLConfig.getInstance().getStoreConfig( Purpose.BOSHBASED_C2S_TRUSTSTORE );
final SslContextFactory sslContextFactory = new SslContextFactory(); final SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.addExcludeProtocols("SSLv3"); sslContextFactory.setTrustStorePath( trustStoreConfig.getCanonicalPath() );
sslContextFactory.setTrustStorePath(SSLConfig.getc2sTruststoreLocation()); sslContextFactory.setTrustStorePassword( trustStoreConfig.getPassword() );
sslContextFactory.setTrustStorePassword(SSLConfig.getc2sTrustPassword()); sslContextFactory.setTrustStoreType( trustStoreConfig.getType() );
sslContextFactory.setTrustStoreType(SSLConfig.getStoreType()); sslContextFactory.setKeyStorePath( identityStoreConfig.getCanonicalPath() );
sslContextFactory.setKeyStorePath(SSLConfig.getKeystoreLocation()); sslContextFactory.setKeyStorePassword( identityStoreConfig.getPassword() );
sslContextFactory.setKeyStorePassword(SSLConfig.getKeyPassword()); sslContextFactory.setKeyStoreType( identityStoreConfig.getType() );
sslContextFactory.setKeyStoreType(SSLConfig.getStoreType());
sslContextFactory.addExcludeProtocols( "SSLv3" );
// Set policy for checking client certificates // Set policy for checking client certificates
String certPol = JiveGlobals.getProperty(HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY, "disabled"); String certPol = JiveGlobals.getProperty(HTTP_BIND_AUTH_PER_CLIENTCERT_POLICY, "disabled");
......
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* A wrapper class for a Java store of certificates, its metadata (password, location) and related functionality.
*
* A subclass of this class exists for each of the two distinct types of key store.
* <ul>
* <li>one that is used to provide credentials, an <em>identity store</em>, in {@link IdentityStoreConfig}</li>
* <li>one that is used to verify credentials, a <em>trust store</em>, in {@link TrustStoreConfig}</li>
* </ul>
*
* Note that in Java terminology, an identity store is commonly referred to as a 'key store', while the same name is
* also used to identify the generic certificate store. To have clear distinction between common denominator and each of
* the specific types, this implementation uses the terms "certificate store", "identity store" and "trust store".
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public abstract class CertificateStoreConfig
{
private static final Logger Log = LoggerFactory.getLogger( CertificateStoreConfig.class );
protected static final Provider PROVIDER = new BouncyCastleProvider();
static
{
// Add the BC provider to the list of security providers
Security.addProvider( PROVIDER );
}
protected final KeyStore store;
protected final char[] password;
protected final String canonicalPath;
public CertificateStoreConfig( String path, String password, String type, boolean createIfAbsent ) throws CertificateStoreConfigException
{
try
{
this.canonicalPath = SSLConfig.canonicalize( path );
final File file = new File( canonicalPath );
if ( createIfAbsent && !file.exists() )
{
try ( final FileOutputStream os = new FileOutputStream( canonicalPath ) )
{
store = KeyStore.getInstance( type );
store.load( null, password.toCharArray() );
store.store( os, password.toCharArray() );
this.password = password.toCharArray();
}
}
else
{
try ( final FileInputStream is = new FileInputStream( canonicalPath ) )
{
store = KeyStore.getInstance( type );
store.load( is, password.toCharArray() );
this.password = password.toCharArray();
}
}
}
catch ( IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException ex )
{
throw new CertificateStoreConfigException( "Unable to load store of type '" + type + "' from location '" + path + "'", ex );
}
}
/**
* Reloads the content of the store from disk. Useful when the store content has been modified outside of the
* Openfire process, or when changes that have not been persisted need to be undone.
*/
public void reload() throws CertificateStoreConfigException
{
try ( final FileInputStream is = new FileInputStream( canonicalPath ) )
{
store.load( is, password );
}
catch ( IOException | NoSuchAlgorithmException | CertificateException ex )
{
throw new CertificateStoreConfigException( "Unable to reload store in location '" + canonicalPath + "'", ex );
}
}
/**
* Saves the current state of the store to disk. Useful when certificates have been added or removed from the
* store.
*/
public void persist() throws CertificateStoreConfigException
{
try ( final FileOutputStream os = new FileOutputStream( canonicalPath ) )
{
store.store( os, password );
}
catch ( NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException ex )
{
throw new CertificateStoreConfigException( "Unable to save changes to store in location '" + canonicalPath + "'", ex );
}
}
/**
* Returns a collection of all x.509 certificates in this store. Certificates returned by this method can be of any
* state (eg: invalid, on a revocation list, etc).
*
* @return A collection (possibly empty, never null) of all certificates in this store, mapped by their alias.
*/
public Map<String, X509Certificate> getAllCertificates() throws KeyStoreException
{
final Map<String, X509Certificate> results = new HashMap<>();
for ( final String alias : Collections.list( store.aliases() ) )
{
final Certificate certificate = store.getCertificate( alias );
if ( !( certificate instanceof X509Certificate ) )
{
continue;
}
results.put( alias, (X509Certificate) certificate );
}
return results;
}
/**
* Deletes an entry (by entry) in this store. All information related to this entry will be removed, including
* certificates and keys.
*
* When the store does not contain an entry that matches the provided alias, this method does nothing.
*
* @param alias The alias for which to delete an entry (cannot be null or empty).
* @throws CertificateStoreConfigException
*/
public void delete( String alias ) throws CertificateStoreConfigException
{
// Input validation
if ( alias == null || alias.trim().isEmpty() )
{
throw new IllegalArgumentException( "Argument 'alias' cannot be null or an empty String." );
}
try
{
if ( !store.containsAlias( alias ) )
{
Log.info( "Unable to delete certificate for alias '" + alias + "' from store, as the store does not contain a certificate for that alias." );
return;
}
store.deleteEntry( alias );
persist();
}
catch ( CertificateStoreConfigException | KeyStoreException e )
{
reload(); // reset state of the store.
throw new CertificateStoreConfigException( "Unable to install a certificate into an identity store.", e );
}
// TODO: Notify listeners that a new certificate has been removed.
}
public String getType()
{
return store.getType();
}
public KeyStore getStore()
{
return store;
}
public String getPassword()
{
return String.valueOf( password );
}
public String getCanonicalPath()
{
return canonicalPath;
}
public String getPath()
{
final Path path = Paths.get( canonicalPath );
final Path home = Paths.get( JiveGlobals.getHomeDirectory() );
final Path corrected = path.startsWith( home ) ? home.relativize( path ) : path;
return corrected.toString();
}
}
package org.jivesoftware.openfire.keystore;
/**
* A checked exception that indicates problems related to Certificate Store functionality.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class CertificateStoreConfigException extends Exception
{
public CertificateStoreConfigException()
{
}
public CertificateStoreConfigException( String message )
{
super( message );
}
public CertificateStoreConfigException( String message, Throwable cause )
{
super( message, cause );
}
public CertificateStoreConfigException( Throwable cause )
{
super( cause );
}
public CertificateStoreConfigException( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace )
{
super( message, cause, enableSuppression, writableStackTrace );
}
}
package org.jivesoftware.openfire.keystore;
/**
* Potential intended usages for keystores
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public enum Purpose
{
/**
* Identification of this Openfire instance used by regular socket-based connections.
*/
SOCKETBASED_IDENTITYSTORE( false ),
/**
* Identification of remote servers that you choose to trust, applies to server-to-server federation via regular socket-based connections.
*/
SOCKETBASED_S2S_TRUSTSTORE( true ),
/**
* Identification of clients that you choose to trust, applies to mutual authentication via regular socket-based connections.
*/
SOCKETBASED_C2S_TRUSTSTORE( true ),
/**
* Identification of this Openfire instance used by regular BOSH (HTTP-bind) connections.
*/
BOSHBASED_IDENTITYSTORE( false ),
/**
* Identification of clients that you choose to trust, applies to mutual authentication via BOSH (HTTP-bind) connections.
*/
BOSHBASED_C2S_TRUSTSTORE( true ),
/**
* Identification of this Openfire instance used by connections to administrative services (eg: user providers).
*/
ADMINISTRATIVE_IDENTITYSTORE( false ),
/**
* Identification of remote applications/servers that provide administrative functionality (eg: user providers).
*/
ADMINISTRATIVE_TRUSTSTORE( true ),
/**
* Openfire web-admin console.
*/
WEBADMIN_IDENTITYSTORE( false ),
/**
* Openfire web-admin console.
*/
WEBADMIN_TRUSTSTORE( true );
private final boolean isTrustStore;
Purpose( boolean isTrustStore )
{
this.isTrustStore = isTrustStore;
}
public boolean isIdentityStore()
{
return !isTrustStore;
}
public boolean isTrustStore()
{
return isTrustStore;
}
}
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jivesoftware.util.CertificateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.cert.*;
import java.util.*;
/**
* A wrapper class for a store of certificates, its metadata (password, location) and related functionality that is
* used to <em>verify</em> credentials, a <em>trust store</em>
*
* The trust store should only contain certificates for the "most-trusted" Certificate Authorities (the store should not
* contain Intermediates"). These certificates are referred to as "Trust Anchors".
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class TrustStoreConfig extends CertificateStoreConfig
{
private static final Logger Log = LoggerFactory.getLogger( TrustStoreConfig.class );
private final TrustManagerFactory trustFactory;
private final CertPathValidator certPathValidator; // not thread safe
private final CertificateFactory certificateFactory; // not thread safe.
public TrustStoreConfig( String path, String password, String type, boolean createIfAbsent ) throws CertificateStoreConfigException
{
super( path, password, type, createIfAbsent );
try
{
certPathValidator = CertPathValidator.getInstance( "PKIX", PROVIDER );
certificateFactory = CertificateFactory.getInstance( "X.509", PROVIDER );
trustFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
trustFactory.init( store );
}
catch ( CertificateException | NoSuchAlgorithmException | KeyStoreException ex )
{
throw new CertificateStoreConfigException( "Unable to load store of type '" + type + "' from location '" + path + "'", ex );
}
}
public TrustManager[] getTrustManagers()
{
return trustFactory.getTrustManagers();
}
/**
* Returns all valid certificates from the store.
*
* @return A collection of certificates (possibly empty, but never null).
*/
protected Set<TrustAnchor> getAllValidTrustAnchors() throws KeyStoreException
{
final Set<TrustAnchor> results = new HashSet<>();
for ( X509Certificate certificate : getAllCertificates().values() )
{
try
{
certificate.checkValidity();
}
catch ( CertificateExpiredException | CertificateNotYetValidException e )
{
// Not yet or no longer valid. Don't include in result.
continue;
}
final TrustAnchor trustAnchor = new TrustAnchor( certificate, null );
results.add( trustAnchor );
}
return results;
}
/**
* Validates the provided certificate chain, by verifying (among others):
* <ul>
* <li>The validity of each certificate in the chain</li>
* <li>chain integrity (matching issuer/subject)</li>
* <li>the root of the chain is validated by a trust anchor that is in this store.</li>
* </ul>
*
* @param chain A chain of certificates (cannot be null)
* @return true when the validity of the chain could be verified, otherwise false.
*/
public synchronized boolean canTrust( Collection<X509Certificate> chain )
{
// Input validation
if ( chain == null )
{
throw new IllegalArgumentException( "Argument 'chain' cannot be null." );
}
if (chain.isEmpty() )
{
return false;
}
try
{
final Set<TrustAnchor> trustAnchors = getAllValidTrustAnchors();
final CertPath certPath = getCertPath( chain );
final PKIXParameters pkixp = new PKIXParameters( trustAnchors );
pkixp.setRevocationEnabled( false ); // TODO: enable revocation list validation.
certPathValidator.validate( certPath, pkixp );
}
catch ( Exception ex )
{
Log.info( "Unable to trust certificate chain.", ex );
return false;
}
return true;
}
/**
* Creates a CertPath instance from the provided certificate chain.
*
* This implementation can process unordered input (ordering will by applied).
*
* @param chain A certificate chain (cannot be null or an empty collection).
* @return A CertPath instance (never null).
* @throws CertificateException When no CertPath instance could be created.
*/
protected synchronized CertPath getCertPath( Collection<X509Certificate> chain ) throws CertificateException
{
// Input validation
if ( chain == null || chain.isEmpty() )
{
throw new IllegalArgumentException( "Argument 'chain' cannot be null or empty." );
}
// Note that PKCS#7 does not require a specific order for the certificates in the file - ordering is needed.
final List<X509Certificate> ordered = CertificateManager.order( chain );
return certificateFactory.generateCertPath( ordered );
}
/**
* Imports one certificate as a trust anchor into this store.
*
* Note that this method explicitly allows one to add invalid certificates. Other methods in this class might ignore
* such a certificate ({@link #canTrust(Collection)} being a prime example).
*
* As this store is intended to contain certificates for "most-trusted" / root Certificate Authorities, this method
* will fail when the PEM representation contains more than one certificate.
*
* @param alias the name (key) under which the certificate is to be stored in the store (cannot be null or empty).
* @param pemRepresentation The PEM representation of the certificate to add (cannot be null or empty).
*/
public void installCertificate( String alias, String pemRepresentation ) throws CertificateStoreConfigException
{
// Input validation
if ( alias == null || alias.trim().isEmpty() )
{
throw new IllegalArgumentException( "Argument 'alias' cannot be null or an empty String." );
}
if ( pemRepresentation == null )
{
throw new IllegalArgumentException( "Argument 'pemRepresentation' cannot be null." );
}
alias = alias.trim();
// Check that there is a certificate for the specified alias
try
{
if ( store.containsAlias( alias ) )
{
throw new CertificateStoreConfigException( "Certificate already exists for alias: " + alias );
}
// From their PEM representation, parse the certificates.
final Collection<X509Certificate> certificates = CertificateManager.parseCertificates( pemRepresentation );
if ( certificates.isEmpty() ) {
throw new CertificateStoreConfigException( "No certificate was found in the input.");
}
if ( certificates.size() != 1 ) {
throw new CertificateStoreConfigException( "More than one certificate was found in the input." );
}
final X509Certificate certificate = certificates.iterator().next();
store.setCertificateEntry(alias, certificate);
persist();
}
catch ( CertificateException | KeyStoreException | IOException e )
{
reload(); // reset state of the store.
throw new CertificateStoreConfigException( "Unable to install a certificate into a trust store.", e );
}
// TODO Notify listeners that a new certificate has been added.
}
}
...@@ -213,6 +213,37 @@ public class DNSUtil { ...@@ -213,6 +213,37 @@ public class DNSUtil {
} }
/** /**
* Checks if the provided DNS pattern matches the provided name. For example, this method will:
* return <em>true</em> for name: <tt>xmpp.example.org</tt>, pattern: <tt>*.example.org</tt>
* return <em>false</em> for name: <tt>xmpp.example.org</tt>, pattern: <tt>example.org</tt>
*
* This method is not case sensitive.
*
* @param name The name to check against a pattern (cannot be null or empty).
* @param pattern the pattern (cannot be null or empty).
* @return true when the name is covered by the pattern, otherwise false.
*/
public static boolean isNameCoveredByPattern( String name, String pattern )
{
if ( name == null || name.isEmpty() || pattern == null || pattern.isEmpty() )
{
throw new IllegalArgumentException( "Arguments cannot be null or empty." );
}
final String needle = name.toLowerCase();
final String hayStack = pattern.toLowerCase();
if ( needle.equals( hayStack )) {
return true;
}
if ( hayStack.startsWith( "*." ) ) {
return needle.endsWith( hayStack.substring( 2 ) );
}
return false;
}
/**
* Encapsulates a hostname and port. * Encapsulates a hostname and port.
*/ */
public static class HostAddress { public static class HostAddress {
......
...@@ -20,10 +20,9 @@ ...@@ -20,10 +20,9 @@
package org.jivesoftware.openfire.net; package org.jivesoftware.openfire.net;
import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.KeyStoreException; import java.security.KeyStore;
import java.security.Security; import java.security.Security;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
...@@ -49,6 +48,7 @@ import org.jivesoftware.openfire.XMPPServer; ...@@ -49,6 +48,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthFactory; import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.auth.AuthToken; import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.AuthorizationManager; import org.jivesoftware.openfire.auth.AuthorizationManager;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.lockout.LockOutManager; import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.session.ClientSession; import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.ConnectionSettings; import org.jivesoftware.openfire.session.ConnectionSettings;
...@@ -192,21 +192,19 @@ public class SASLAuthentication { ...@@ -192,21 +192,19 @@ public class SASLAuthentication {
return null; return null;
} }
Element mechs = DocumentHelper.createElement(new QName("mechanisms", Element mechs = DocumentHelper.createElement( new QName( "mechanisms",
new Namespace("", "urn:ietf:params:xml:ns:xmpp-sasl"))); new Namespace( "", "urn:ietf:params:xml:ns:xmpp-sasl" ) ) );
if (session instanceof LocalIncomingServerSession) { if (session instanceof LocalIncomingServerSession) {
// Server connections don't follow the same rules as clients // Server connections don't follow the same rules as clients
if (session.isSecure()) { if (session.isSecure()) {
boolean haveTrustedCertificate = false; LocalIncomingServerSession svr = (LocalIncomingServerSession)session;
try { final KeyStore keyStore = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE );
LocalIncomingServerSession svr = (LocalIncomingServerSession)session; final KeyStore trustStore = SSLConfig.getStore( Purpose.SOCKETBASED_S2S_TRUSTSTORE );
X509Certificate trusted = CertificateManager.getEndEntityCertificate(svr.getConnection().getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore()); final X509Certificate trusted = CertificateManager.getEndEntityCertificate( svr.getConnection().getPeerCertificates(), keyStore, trustStore );
haveTrustedCertificate = trusted != null;
if (trusted != null && svr.getDefaultIdentity() != null) { boolean haveTrustedCertificate = trusted != null;
haveTrustedCertificate = verifyCertificate(trusted, svr.getDefaultIdentity()); if (trusted != null && svr.getDefaultIdentity() != null) {
} haveTrustedCertificate = verifyCertificate(trusted, svr.getDefaultIdentity());
} catch (IOException ex) {
Log.warn("Exception occurred while trying to determine whether remote certificate is trusted. Treating as untrusted.", ex);
} }
if (haveTrustedCertificate) { if (haveTrustedCertificate) {
// Offer SASL EXTERNAL only if TLS has already been negotiated and the peer has a trusted cert. // Offer SASL EXTERNAL only if TLS has already been negotiated and the peer has a trusted cert.
...@@ -471,7 +469,7 @@ public class SASLAuthentication { ...@@ -471,7 +469,7 @@ public class SASLAuthentication {
return false; return false;
} }
String sharedSecert = getSharedSecret(); String sharedSecert = getSharedSecret();
return StringUtils.hash(sharedSecert).equals(digest); return StringUtils.hash(sharedSecert).equals( digest );
} }
...@@ -557,7 +555,7 @@ public class SASLAuthentication { ...@@ -557,7 +555,7 @@ public class SASLAuthentication {
if (!verify) { if (!verify) {
authenticationSuccessful(session, hostname, null); authenticationSuccessful(session, hostname, null);
return Status.authenticated; return Status.authenticated;
} else if(verifyCertificates(session.getConnection().getPeerCertificates(), hostname)) { } else if(verifyCertificates(session.getConnection().getPeerCertificates(), hostname, true)) {
authenticationSuccessful(session, hostname, null); authenticationSuccessful(session, hostname, null);
LocalIncomingServerSession s = (LocalIncomingServerSession)session; LocalIncomingServerSession s = (LocalIncomingServerSession)session;
if (s != null) { if (s != null) {
...@@ -567,7 +565,7 @@ public class SASLAuthentication { ...@@ -567,7 +565,7 @@ public class SASLAuthentication {
} }
} }
else if (session instanceof LocalClientSession) { else if (session instanceof LocalClientSession) {
// Client EXTERNALL login // Client EXTERNAL login
Log.debug("SASLAuthentication: EXTERNAL authentication via SSL certs for c2s connection"); Log.debug("SASLAuthentication: EXTERNAL authentication via SSL certs for c2s connection");
// This may be null, we will deal with that later // This may be null, we will deal with that later
...@@ -581,12 +579,10 @@ public class SASLAuthentication { ...@@ -581,12 +579,10 @@ public class SASLAuthentication {
return Status.failed; return Status.failed;
} }
X509Certificate trusted; final KeyStore keyStore = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE );
try { final KeyStore trustStore = SSLConfig.getStore( Purpose.SOCKETBASED_C2S_TRUSTSTORE );
trusted = CertificateManager.getEndEntityCertificate(connection.getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore()); final X509Certificate trusted = CertificateManager.getEndEntityCertificate( connection.getPeerCertificates(), keyStore, trustStore );
} catch (IOException e) {
trusted = null;
}
if (trusted == null) { if (trusted == null) {
Log.debug("SASLAuthentication: EXTERNAL authentication requested, but EE cert untrusted."); Log.debug("SASLAuthentication: EXTERNAL authentication requested, but EE cert untrusted.");
authenticationFailed(session, Failure.NOT_AUTHORIZED); authenticationFailed(session, Failure.NOT_AUTHORIZED);
...@@ -655,15 +651,20 @@ public class SASLAuthentication { ...@@ -655,15 +651,20 @@ public class SASLAuthentication {
return false; return false;
} }
/**
* @deprecated Use {@link #verifyCertificates(Certificate[], String, boolean)} instead.
*/
@Deprecated
public static boolean verifyCertificates(Certificate[] chain, String hostname) { public static boolean verifyCertificates(Certificate[] chain, String hostname) {
try { return verifyCertificates( chain, hostname, true );
X509Certificate trusted = CertificateManager.getEndEntityCertificate(chain, SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore()); }
if (trusted != null) { public static boolean verifyCertificates(Certificate[] chain, String hostname, boolean isS2S) {
return verifyCertificate(trusted, hostname); final KeyStore keyStore = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE );
} final KeyStore trustStore = SSLConfig.getStore( isS2S ? Purpose.SOCKETBASED_S2S_TRUSTSTORE : Purpose.SOCKETBASED_C2S_TRUSTSTORE );
} catch(IOException e) { final X509Certificate trusted = CertificateManager.getEndEntityCertificate( chain, keyStore, trustStore );
Log.warn("Keystore issue while verifying certificate chain: {}", e.getMessage()); if (trusted != null) {
return verifyCertificate(trusted, hostname);
} }
return false; return false;
} }
...@@ -726,7 +727,7 @@ public class SASLAuthentication { ...@@ -726,7 +727,7 @@ public class SASLAuthentication {
else { else {
reply.append("/>"); reply.append("/>");
} }
session.deliverRawText(reply.toString()); session.deliverRawText( reply.toString() );
// We only support SASL for c2s // We only support SASL for c2s
if (session instanceof ClientSession) { if (session instanceof ClientSession) {
((LocalClientSession) session).setAuthToken(new AuthToken(username)); ((LocalClientSession) session).setAuthToken(new AuthToken(username));
......
package org.jivesoftware.openfire.net;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.util.CertificateEventListener;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* A factory object for creating server sockets based on Openfire's SSL configuration.
*
* This implementation distinguishes between server sockets created for server-to-server ('s2s') and for
* client-to-server ('c2s') communication. The primary difference between the two is the set of key and trust stores
* that are used.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
// TODO: This code was split off from SSLConfig, but does not appear to be used! Remove?
public class SSLConfigSocketFactory
{
private static final Logger Log = LoggerFactory.getLogger( SSLConfigSocketFactory.class );
/**
* The factory used for server-to-server connections.
*/
private static SSLServerSocketFactory s2sFactory;
/**
* The factory used for client-to-server connections.
*/
private static SSLServerSocketFactory c2sFactory;
static
{
// Initial instantiation
resetFactory();
// Reset SSL factory when certificates are modified
CertificateManager.addListener( new CertificateEventListener()
{
// Reset SSL factory since key stores have changed
public void certificateCreated( KeyStore keyStore, String alias, X509Certificate cert )
{
resetFactory();
}
public void certificateDeleted( KeyStore keyStore, String alias )
{
resetFactory();
}
public void certificateSigned( KeyStore keyStore, String alias, List<X509Certificate> certificates )
{
resetFactory();
}
} );
}
private static void resetFactory()
{
Log.debug( "(Re)setting the SSL-based socket factories." );
try
{
final KeyStore s2sTrustStore = SSLConfig.getStore( Purpose.SOCKETBASED_S2S_TRUSTSTORE );
final KeyStore c2sTrustStore = SSLConfig.getStore( Purpose.SOCKETBASED_C2S_TRUSTSTORE );
s2sFactory = createS2SServerSocketFactory();
if ( s2sTrustStore == c2sTrustStore )
{
c2sFactory = s2sFactory;
}
else
{
c2sFactory = createC2SServerSocketFactory();
}
}
catch ( Exception e )
{
Log.error( "An exception occurred while (re)setting the SSL-based socket factories. Factories will be unavailable.", e );
s2sFactory = null;
c2sFactory = null;
}
}
static SSLServerSocketFactory createC2SServerSocketFactory() throws NoSuchAlgorithmException, KeyManagementException
{
final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
final TrustStoreConfig trustStoreConfig = (TrustStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_C2S_TRUSTSTORE );
final String algorithm = JiveGlobals.getProperty( "xmpp.socket.ssl.algorithm", "TLS" );
final SSLContext context = SSLContext.getInstance( algorithm );
context.init( identityStoreConfig.getKeyManagers(), trustStoreConfig.getTrustManagers(), new java.security.SecureRandom() );
return context.getServerSocketFactory();
}
static SSLServerSocketFactory createS2SServerSocketFactory() throws NoSuchAlgorithmException, KeyManagementException, UnrecoverableKeyException, KeyStoreException, IOException
{
final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
final TrustStoreConfig trustStoreConfig = (TrustStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_S2S_TRUSTSTORE );
final String algorithm = JiveGlobals.getProperty( "xmpp.socket.ssl.algorithm", "TLS" );
final SSLContext context = SSLContext.getInstance( algorithm );
context.init( identityStoreConfig.getKeyManagers(), trustStoreConfig.getTrustManagers(), new java.security.SecureRandom() );
return context.getServerSocketFactory();
}
/**
* Create a ServerSocket for server-to-server connections. This method will throw an IOException if it fails to
* create a socket.
*
* @return the ServerSocket for a server-to-server connection (never null).
* @throws IOException Failed to create a socket.
*/
public static ServerSocket createS2SServerSocket( int port, InetAddress ifAddress ) throws IOException
{
if ( s2sFactory == null )
{
throw new IOException( "S2S server socket factory has not been initialized successfully." );
}
return s2sFactory.createServerSocket( port, -1, ifAddress );
}
/**
* Create a ServerSocket for client-to-server connections. This method will throw an IOException if it fails to
* create a socket.
*
* @return the ServerSocket for a client-to-server connection (never null).
* @throws IOException Failed to create a socket.
*/
public static ServerSocket createC2SServerSocket( int port, InetAddress ifAddress ) throws IOException
{
if ( c2sFactory == null )
{
throw new IOException( "C2S server socket factory has not been initialized successfully." );
}
return c2sFactory.createServerSocket( port, -1, ifAddress );
}
/**
* Get the SSLServerSocketFactory for server-to-server connections. When the factory has not been initialized
* successfully, this method returns null.
*
* @return the SSLServerSocketFactory for server-to-server connections (possibly null).
*/
public static SSLServerSocketFactory getS2SServerSocketFactory()
{
return s2sFactory;
}
/**
* Get the SSLServerSocketFactory for client-to-server connections. When the factory has not been initialized
* successfully, this method returns null.
*
* @return the SSLServerSocketFactory for client-to-server connections (possibly null).
*/
public static SSLServerSocketFactory getC2SServerSocketFactory()
{
return c2sFactory;
}
/**
* Returns an array of cipher suite names that are enabled by default for server-to-server communication. When no
* cipher suite names cannot be determined (ie: when the socket factory has not been initialized) this method
* returns an empty array.
*
* @return array of cipher suites names, possibly empty, but never null.
* @see SSLServerSocketFactory#getDefaultCipherSuites()
*/
public static String[] getS2SDefaultCipherSuites()
{
if ( s2sFactory == null )
{
return new String[ 0 ];
}
return s2sFactory.getDefaultCipherSuites();
}
/**
* Returns an array of cipher suite names that are available (but not necessarily enabled) for server-to-server
* communication. When cipher suite names cannot be determined (ie: when the socket factory has not been
* initialized) this method returns an empty array.
*
* @return array of cipher suites names, possibly empty, but never null.
* @see SSLServerSocketFactory#getSupportedCipherSuites()
*/
public static String[] getS2SSupportedCipherSuites()
{
if ( s2sFactory == null )
{
return new String[ 0 ];
}
return s2sFactory.getSupportedCipherSuites();
}
/**
* Returns an array of cipher suite names that are enabled by default for client-to-server communication. When
* cipher suite names cannot be determined (ie: when the socket factory has not been initialized) this method
* returns an empty array.
*
* @return array of cipher suites names, possibly empty, but never null.
* @see SSLServerSocketFactory#getDefaultCipherSuites()
*/
public static String[] getC2SDefaultCipherSuites()
{
if ( c2sFactory == null )
{
return new String[ 0 ];
}
return c2sFactory.getDefaultCipherSuites();
}
/**
* Returns an array of cipher suite names that are available (but not necessarily enabled) for client-to-server
* communication. When cipher suite names cannot be determined (ie: when the socket factory has not been
* initialized) this method returns an empty array.
*
* @return array of cipher suites names, possibly empty, but never null.
* @see SSLServerSocketFactory#getSupportedCipherSuites()
*/
public static String[] getC2SSupportedCipherSuites()
{
if ( c2sFactory == null )
{
return new String[ 0 ];
}
return c2sFactory.getSupportedCipherSuites();
}
}
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.net;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.X509KeyManager;
/**
* A skeleton placeholder for developers wishing to implement their own custom
* key manager. In future revisions we may expand the skeleton code if customers
* request assistance in creating custom key managers.
* <p>
* The key manager is an essential part of server SSL support. Typically you
* will implement a custom key manager to retrieve certificates from repositories
* that are not of standard Java types (e.g. obtaining them from LDAP or a JDBC database).
* </p>
*
* @author Iain Shigeoka
*/
public class SSLJiveKeyManager implements X509KeyManager {
private static final Logger Log = LoggerFactory.getLogger(SSLJiveKeyManager.class);
public String[] getClientAliases(String s, Principal[] principals) {
return new String[0];
}
public String chooseClientAlias(String s, Principal[] principals) {
return null;
}
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return null;
}
public String[] getServerAliases(String s, Principal[] principals) {
return new String[0];
}
public String chooseServerAlias(String s, Principal[] principals) {
return null;
}
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return null;
}
public X509Certificate[] getCertificateChain(String s) {
return new X509Certificate[0];
}
public PrivateKey getPrivateKey(String s) {
return null;
}
}
/**
* $RCSfile$
* $Revision: 2774 $
* $Date: 2005-09-05 01:53:16 -0300 (Mon, 05 Sep 2005) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.net;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A custom KeyManagerFactory that creates a key manager list using the
* default key manager or a standard keystore as specified in openfire.xml.
* The default keystore provided with the Jive distribution uses the Sun Java
* Keystore (JKS) and that takes a single password which must apply to both the
* keystore and the key itself. Users may specify another keystore type and keystore
* location. Alternatively, don't set a keystore type to use the JVM defaults and
* configure your JVMs security files (see your JVM documentation) to plug in
* any KeyManagerFactory provider.
*
* @author Iain Shigeoka
*/
public class SSLJiveKeyManagerFactory {
private static final Logger Log = LoggerFactory.getLogger(SSLJiveKeyManagerFactory.class);
/**
* Creates a KeyManager list which is null if the storeType is null, or
* is a standard KeyManager that uses a KeyStore of type storeType,
* located at 'keystore' location under home, and uses 'keypass' as
* the password for the keystore password and key password. The default
* Jive keystore contains a self-signed X509 certificate pair under the
* alias '127.0.0.1' in a Java KeyStore (JKS) with initial password 'changeit'.
* This is sufficient for local host testing but should be using standard
* key management tools for any significant testing or deployment. See
* the Jive XMPP server security documentation for more information.
*
* @param storeType The type of keystore (e.g. "JKS") to use or null to indicate no keystore should be used
* @param keystore The relative location of the keystore under home
* @param keypass The password for the keystore and key
* @return An array of relevant KeyManagers (may be null indicating a default KeyManager should be created)
* @throws NoSuchAlgorithmException If the keystore type doesn't exist (not provided or configured with your JVM)
* @throws KeyStoreException If the keystore is corrupt
* @throws IOException If the keystore could not be located or loaded
* @throws CertificateException If there were no certificates to be loaded or they are invalid
* @throws UnrecoverableKeyException If they keystore coud not be opened (typically the password is bad)
*/
public static KeyManager[] getKeyManagers(String storeType, String keystore, String keypass) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
KeyManager[] keyManagers;
if (keystore == null) {
keyManagers = null;
}
else {
if (keypass == null) {
keypass = "";
}
KeyStore keyStore = KeyStore.getInstance(storeType);
keyStore.load(new FileInputStream(keystore), keypass.toCharArray());
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, keypass.toCharArray());
keyManagers = keyFactory.getKeyManagers();
}
return keyManagers;
}
public static KeyManager[] getKeyManagers(KeyStore keystore, String keypass) {
KeyManager[] keyManagers;
try {
if (keystore == null) {
keyManagers = null;
} else {
KeyManagerFactory keyFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
if (keypass == null) {
keypass = SSLConfig.getKeyPassword();
}
keyFactory.init(keystore, keypass.toCharArray());
keyManagers = keyFactory.getKeyManagers();
}
} catch (KeyStoreException e) {
keyManagers = null;
Log.error("SSLJiveKeyManagerFactory startup problem.\n" +
" the keystore is corrupt", e);
} catch (NoSuchAlgorithmException e) {
keyManagers = null;
Log.error("SSLJiveKeyManagerFactory startup problem.\n" +
" the keystore type doesn't exist (not provided or configured with your JVM)", e);
} catch (UnrecoverableKeyException e) {
keyManagers = null;
Log.error("SSLJiveKeyManagerFactory startup problem.\n" +
" the keystore could not be opened (typically the password is bad)", e);
}
return keyManagers;
}
}
/**
* $RCSfile$
* $Revision: 2774 $
* $Date: 2005-09-05 01:53:16 -0300 (Mon, 05 Sep 2005) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.net;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A custom TrustManagerFactory that creates a trust manager list using the
* default trust manager or a standard keystore as specified in openfire.xml.
* There is no default trust keystore provided with the Jive distribution as most
* clients will not need to be authenticated with the server.
* <p>
* The Java Keystore (JKS) takes a single password which must apply to both the
* keystore and the key itself. Users may specify another keystore type and keystore
* location. Alternatively, don't set a keystore type to use the JVM defaults and
* configure your JVMs security files (see your JVM documentation) to plug in
* any TrustManagerFactory provider.</p>
*
* @author Iain Shigeoka
*/
public class SSLJiveTrustManagerFactory {
private static final Logger Log = LoggerFactory.getLogger(SSLJiveTrustManagerFactory.class);
/**
* Creates a TrustManager list which is null if the storeType is null, or
* is a standard TrustManager that uses a KeyStore of type storeType,
* located at 'keystore' location under home, and uses 'keypass' as
* the password for the keystore password and key password (note that
* trust managers typically don't need a key password as public keys
* are stored in the clear and can be obtained without a key password).
* The default Jive distribution doesn't ship with a trust keystore
* as it is not needed (the server does not require client authentication).
*
* @param storeType The type of keystore (e.g. "JKS") to use or null to indicate no keystore should be used
* @param truststore The relative location of the keystore under home
* @param trustpass The password for the keystore and key
* @return An array of relevant KeyManagers (may be null indicating a default KeyManager should be created)
* @throws NoSuchAlgorithmException If the keystore type doesn't exist (not provided or configured with your JVM)
* @throws KeyStoreException If the keystore is corrupt
* @throws IOException If the keystore could not be located or loaded
* @throws CertificateException If there were no certificates to be loaded or they are invalid
*/
public static TrustManager[] getTrustManagers(String storeType, String truststore, String trustpass) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException {
TrustManager[] trustManagers;
if (truststore == null) {
trustManagers = null;
}
else {
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
if (trustpass == null) {
trustpass = "";
}
KeyStore keyStore = KeyStore.getInstance(storeType);
keyStore.load(new FileInputStream(truststore), trustpass.toCharArray());
trustFactory.init(keyStore);
trustManagers = trustFactory.getTrustManagers();
}
return trustManagers;
}
//TODO: Is this for c2s or s2s connections? Or both?
public static TrustManager[] getTrustManagers(KeyStore truststore,
String trustpass) {
TrustManager[] trustManagers;
try {
if (truststore == null) {
trustManagers = null;
} else {
TrustManagerFactory trustFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
if (trustpass == null) {
trustpass = SSLConfig.gets2sTrustPassword();
}
trustFactory.init(truststore);
trustManagers = trustFactory.getTrustManagers();
}
} catch (KeyStoreException e) {
trustManagers = null;
Log.error("SSLJiveTrustManagerFactory startup problem.\n" +
" the keystore is corrupt", e);
} catch (NoSuchAlgorithmException e) {
trustManagers = null;
Log.error("SSLJiveTrustManagerFactory startup problem.\n" +
" the keystore type doesn't exist (not provided or configured with your JVM)", e);
}
return trustManagers;
}
}
...@@ -20,13 +20,9 @@ ...@@ -20,13 +20,9 @@
package org.jivesoftware.openfire.net; package org.jivesoftware.openfire.net;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.KeyManagementException; import java.security.*;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
...@@ -37,6 +33,9 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus; ...@@ -37,6 +33,9 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLEngineResult.Status;
import org.jivesoftware.openfire.Connection; import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -58,17 +57,6 @@ public class TLSWrapper { ...@@ -58,17 +57,6 @@ public class TLSWrapper {
*/ */
private boolean logging = false; private boolean logging = false;
/*
* Enables the JSSE system debugging system property:
*
* -Djavax.net.debug=all
*
* This gives a lot of low-level information about operations underway, including specific
* handshake messages, and might be best examined after gaining some familiarity with this
* application.
*/
private static boolean debug = false;
private SSLEngine tlsEngine; private SSLEngine tlsEngine;
private SSLEngineResult tlsEngineResult; private SSLEngineResult tlsEngineResult;
...@@ -76,42 +64,41 @@ public class TLSWrapper { ...@@ -76,42 +64,41 @@ public class TLSWrapper {
private int appBuffSize; private int appBuffSize;
public TLSWrapper(Connection connection, boolean clientMode, boolean needClientAuth, String remoteServer) { public TLSWrapper(Connection connection, boolean clientMode, boolean needClientAuth, String remoteServer) {
final boolean isClientToServer = (remoteServer == null);
boolean c2sConnection = (remoteServer == null);
if (debug) {
System.setProperty("javax.net.debug", "all");
}
String algorithm = JiveGlobals.getProperty("xmpp.socket.ssl.algorithm", "TLS");
// Create/initialize the SSLContext with key material // Create/initialize the SSLContext with key material
try { try {
// First initialize the key and trust material. // First initialize the key and trust material.
KeyStore ksKeys = SSLConfig.getKeyStore(); final SSLConfig sslConfig = SSLConfig.getInstance();
String keypass = SSLConfig.getKeyPassword(); final Purpose purpose = (isClientToServer ? Purpose.SOCKETBASED_C2S_TRUSTSTORE : Purpose.SOCKETBASED_S2S_TRUSTSTORE );
final TrustStoreConfig trustStoreConfig = (TrustStoreConfig) sslConfig.getStoreConfig( purpose );
KeyStore ksTrust = (c2sConnection ? SSLConfig.getc2sTrustStore() : SSLConfig.gets2sTrustStore());
String trustpass = (c2sConnection ? SSLConfig.getc2sTrustPassword() : SSLConfig.gets2sTrustPassword());
// KeyManager's decide which key material to use.
KeyManager[] km = SSLJiveKeyManagerFactory.getKeyManagers(ksKeys, keypass);
// TrustManager's decide whether to allow connections. // TrustManager's decide whether to allow connections.
TrustManager[] tm = SSLJiveTrustManagerFactory.getTrustManagers(ksTrust, trustpass); final TrustManager[] tm;
if (clientMode || needClientAuth) {
if (c2sConnection) { if (clientMode || needClientAuth)
{
final KeyStore ksTrust = trustStoreConfig.getStore();
if (isClientToServer)
{
// Check if we can trust certificates presented by the client // Check if we can trust certificates presented by the client
tm = new TrustManager[]{new ClientTrustManager(ksTrust)}; tm = new TrustManager[]{new ClientTrustManager(ksTrust)};
} }
else { else
{
// Check if we can trust certificates presented by the server // Check if we can trust certificates presented by the server
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, connection)}; tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, connection)};
} }
} }
else
{
tm = trustStoreConfig.getTrustManagers();
}
SSLContext tlsContext = SSLContext.getInstance(algorithm); final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) sslConfig.getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
final String algorithm = JiveGlobals.getProperty("xmpp.socket.ssl.algorithm", "TLS");
tlsContext.init(km, tm, null); final SSLContext tlsContext = SSLContext.getInstance(algorithm);
tlsContext.init( identityStoreConfig.getKeyManagers(), tm, null);
/* /*
* Configure the tlsEngine to act as a server in the SSL/TLS handshake. We're a server, * Configure the tlsEngine to act as a server in the SSL/TLS handshake. We're a server,
...@@ -126,13 +113,10 @@ public class TLSWrapper { ...@@ -126,13 +113,10 @@ public class TLSWrapper {
netBuffSize = sslSession.getPacketBufferSize(); netBuffSize = sslSession.getPacketBufferSize();
appBuffSize = sslSession.getApplicationBufferSize(); appBuffSize = sslSession.getApplicationBufferSize();
} catch (KeyManagementException e) { }
Log.error("TLSHandler startup problem.\n" + " SSLContext initialisation failed.", e); catch ( NoSuchAlgorithmException | KeyManagementException ex )
} catch (NoSuchAlgorithmException e) { {
Log.error("TLSHandler startup problem.\n" + " The " + algorithm + " does not exist", e); Log.error("TLSHandler startup problem. SSLContext initialisation failed.", ex );
} catch (IOException e) {
Log.error("TLSHandler startup problem.\n"
+ " the KeyStore or TrustStore does not exist", e);
} }
} }
......
...@@ -35,7 +35,6 @@ import java.security.KeyStore; ...@@ -35,7 +35,6 @@ import java.security.KeyStore;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSession;
...@@ -51,11 +50,10 @@ import org.jivesoftware.openfire.Connection; ...@@ -51,11 +50,10 @@ import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.ConnectionCloseListener; import org.jivesoftware.openfire.ConnectionCloseListener;
import org.jivesoftware.openfire.PacketDeliverer; import org.jivesoftware.openfire.PacketDeliverer;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.net.ClientTrustManager; import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SSLJiveKeyManagerFactory; import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.openfire.net.SSLJiveTrustManagerFactory; import org.jivesoftware.openfire.net.*;
import org.jivesoftware.openfire.net.ServerTrustManager;
import org.jivesoftware.openfire.session.ConnectionSettings; import org.jivesoftware.openfire.session.ConnectionSettings;
import org.jivesoftware.openfire.session.LocalSession; import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.openfire.session.Session; import org.jivesoftware.openfire.session.Session;
...@@ -393,35 +391,38 @@ public class NIOConnection implements Connection { ...@@ -393,35 +391,38 @@ public class NIOConnection implements Connection {
} }
public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception { public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception {
boolean c2s = (remoteServer == null); final boolean isClientToServer = (remoteServer == null);
KeyStore ksKeys = SSLConfig.getKeyStore();
String keypass = SSLConfig.getKeyPassword();
KeyStore ksTrust = (c2s ? SSLConfig.getc2sTrustStore() : SSLConfig.gets2sTrustStore() ); Log.debug( "StartTLS: using {}", isClientToServer ? "c2s" : "s2s" );
String trustpass = (c2s ? SSLConfig.getc2sTrustPassword() : SSLConfig.gets2sTrustPassword() );
if (c2s) Log.debug("NIOConnection: startTLS: using c2s");
else Log.debug("NIOConnection: startTLS: using s2s");
// KeyManager's decide which key material to use.
KeyManager[] km = SSLJiveKeyManagerFactory.getKeyManagers(ksKeys, keypass);
// TrustManager's decide whether to allow connections. final SSLConfig sslConfig = SSLConfig.getInstance();
TrustManager[] tm = SSLJiveTrustManagerFactory.getTrustManagers(ksTrust, trustpass); final TrustStoreConfig storeConfig;
if (isClientToServer) {
storeConfig = (TrustStoreConfig) sslConfig.getStoreConfig( Purpose.SOCKETBASED_C2S_TRUSTSTORE );
} else {
storeConfig = (TrustStoreConfig) sslConfig.getStoreConfig( Purpose.SOCKETBASED_S2S_TRUSTSTORE );
}
final TrustManager[] tm;
if (clientMode || authentication == ClientAuth.needed || authentication == ClientAuth.wanted) { if (clientMode || authentication == ClientAuth.needed || authentication == ClientAuth.wanted) {
// We might need to verify a certificate from our peer, so get different TrustManager[]'s // We might need to verify a certificate from our peer, so get different TrustManager[]'s
if(c2s) { final KeyStore ksTrust = storeConfig.getStore();
if(isClientToServer) {
// Check if we can trust certificates presented by the client // Check if we can trust certificates presented by the client
tm = new TrustManager[]{new ClientTrustManager(ksTrust)}; tm = new TrustManager[]{new ClientTrustManager(ksTrust)};
} else { } else {
// Check if we can trust certificates presented by the server // Check if we can trust certificates presented by the server
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, this)}; tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, this)};
} }
} else {
tm = storeConfig.getTrustManagers();
} }
String algorithm = JiveGlobals.getProperty(ConnectionSettings.Client.TLS_ALGORITHM, "TLS"); String algorithm = JiveGlobals.getProperty(ConnectionSettings.Client.TLS_ALGORITHM, "TLS");
SSLContext tlsContext = SSLContext.getInstance(algorithm); SSLContext tlsContext = SSLContext.getInstance( algorithm );
tlsContext.init(km, tm, null); final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) sslConfig.getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
tlsContext.init( identityStoreConfig.getKeyManagers(), tm, null);
SslFilter filter = new SslFilter(tlsContext); SslFilter filter = new SslFilter(tlsContext);
filter.setUseClientMode(clientMode); filter.setUseClientMode(clientMode);
......
...@@ -541,7 +541,7 @@ public class ServerDialback { ...@@ -541,7 +541,7 @@ public class ServerDialback {
return false; return false;
} }
else { else {
if (SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), hostname)) { if (SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), hostname, true)) {
// If the remote host passes strong auth, just skip the dialback. // If the remote host passes strong auth, just skip the dialback.
Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname); Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname);
sb = new StringBuilder(); sb = new StringBuilder();
......
...@@ -34,6 +34,7 @@ import org.jivesoftware.openfire.XMPPServer; ...@@ -34,6 +34,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthToken; import org.jivesoftware.openfire.auth.AuthToken;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.cluster.ClusterManager; import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SASLAuthentication; import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.SocketConnection; import org.jivesoftware.openfire.net.SocketConnection;
...@@ -255,7 +256,7 @@ public class LocalClientSession extends LocalSession implements ClientSession { ...@@ -255,7 +256,7 @@ public class LocalClientSession extends LocalSession implements ClientSession {
if (!connection.isSecure()) { if (!connection.isSecure()) {
boolean hasCertificates = false; boolean hasCertificates = false;
try { try {
hasCertificates = SSLConfig.getKeyStore().size() > 0; hasCertificates = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE ).size() > 0;
} }
catch (Exception e) { catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package org.jivesoftware.openfire.session; package org.jivesoftware.openfire.session;
import java.io.IOException; import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
...@@ -34,6 +35,7 @@ import org.jivesoftware.openfire.Connection; ...@@ -34,6 +35,7 @@ import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.SessionManager; import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.StreamID; import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SASLAuthentication; import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.SocketConnection; import org.jivesoftware.openfire.net.SocketConnection;
...@@ -151,7 +153,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -151,7 +153,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
Connection.TLSPolicy.required; Connection.TLSPolicy.required;
boolean hasCertificates = false; boolean hasCertificates = false;
try { try {
hasCertificates = SSLConfig.getKeyStore().size() > 0; hasCertificates = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE ).size() > 0;
} }
catch (Exception e) { catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
...@@ -371,13 +373,11 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -371,13 +373,11 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
usingSelfSigned = true; usingSelfSigned = true;
} else { } else {
try { try {
usingSelfSigned = CertificateManager.isSelfSignedCertificate(SSLConfig.getKeyStore(), (X509Certificate) chain[0]); final KeyStore keyStore = SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE );
usingSelfSigned = CertificateManager.isSelfSignedCertificate(keyStore, (X509Certificate) chain[0]);
} catch (KeyStoreException ex) { } catch (KeyStoreException ex) {
Log.warn("Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex); Log.warn("Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex);
usingSelfSigned = true; usingSelfSigned = true;
} catch (IOException ex) {
Log.warn("Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex);
usingSelfSigned = true;
} }
} }
......
...@@ -387,7 +387,7 @@ public class LocalOutgoingServerSession extends LocalServerSession implements Ou ...@@ -387,7 +387,7 @@ public class LocalOutgoingServerSession extends LocalServerSession implements Ou
throw e; throw e;
} }
log.debug("TLS negotiation was successful."); log.debug("TLS negotiation was successful.");
if (!SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), hostname)) { if (!SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), hostname, true)) {
log.debug("X.509/PKIX failure on outbound session"); log.debug("X.509/PKIX failure on outbound session");
if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) { if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) {
log.debug("Will continue with dialback."); log.debug("Will continue with dialback.");
......
...@@ -41,9 +41,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -41,9 +41,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.management.JMException; import javax.management.JMException;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.buffer.SimpleBufferAllocator; import org.apache.mina.core.buffer.SimpleBufferAllocator;
...@@ -70,13 +68,10 @@ import org.jivesoftware.openfire.container.BasicModule; ...@@ -70,13 +68,10 @@ import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.container.PluginManagerListener; import org.jivesoftware.openfire.container.PluginManagerListener;
import org.jivesoftware.openfire.http.HttpBindManager; import org.jivesoftware.openfire.http.HttpBindManager;
import org.jivesoftware.openfire.net.SSLConfig; import org.jivesoftware.openfire.keystore.IdentityStoreConfig;
import org.jivesoftware.openfire.net.ServerSocketReader; import org.jivesoftware.openfire.keystore.Purpose;
import org.jivesoftware.openfire.net.SocketAcceptThread; import org.jivesoftware.openfire.keystore.TrustStoreConfig;
import org.jivesoftware.openfire.net.SocketConnection; import org.jivesoftware.openfire.net.*;
import org.jivesoftware.openfire.net.SocketReader;
import org.jivesoftware.openfire.net.SocketSendingTracker;
import org.jivesoftware.openfire.net.StalledSessionsFilter;
import org.jivesoftware.openfire.nio.ClientConnectionHandler; import org.jivesoftware.openfire.nio.ClientConnectionHandler;
import org.jivesoftware.openfire.nio.ComponentConnectionHandler; import org.jivesoftware.openfire.nio.ComponentConnectionHandler;
import org.jivesoftware.openfire.nio.MultiplexerConnectionHandler; import org.jivesoftware.openfire.nio.MultiplexerConnectionHandler;
...@@ -457,15 +452,11 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana ...@@ -457,15 +452,11 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
sslSocketAcceptor, maxBufferSize); sslSocketAcceptor, maxBufferSize);
// Add the SSL filter now since sockets are "borned" encrypted in the old ssl method // Add the SSL filter now since sockets are "borned" encrypted in the old ssl method
SSLContext sslContext = SSLContext.getInstance(algorithm); final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE );
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); final TrustStoreConfig trustStoreConfig = (TrustStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_C2S_TRUSTSTORE );
keyFactory.init(SSLConfig.getKeyStore(), SSLConfig.getKeyPassword().toCharArray());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(SSLConfig.getc2sTrustStore());
sslContext.init(keyFactory.getKeyManagers(), final SSLContext sslContext = SSLContext.getInstance( algorithm );
trustFactory.getTrustManagers(), sslContext.init( identityStoreConfig.getKeyManagers(), trustStoreConfig.getTrustManagers(), new java.security.SecureRandom());
new java.security.SecureRandom());
SslFilter sslFilter = new SslFilter(sslContext); SslFilter sslFilter = new SslFilter(sslContext);
if (JiveGlobals.getProperty(ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY,"disabled").equals("needed")) { if (JiveGlobals.getProperty(ConnectionSettings.Client.AUTH_PER_CLIENTCERT_POLICY,"disabled").equals("needed")) {
...@@ -623,11 +614,9 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana ...@@ -623,11 +614,9 @@ public class ConnectionManagerImpl extends BasicModule implements ConnectionMana
public boolean isClientSSLListenerEnabled() { public boolean isClientSSLListenerEnabled() {
try { try {
return JiveGlobals.getBooleanProperty(ConnectionSettings.Client.ENABLE_OLD_SSLPORT, false) && SSLConfig.getKeyStore().size() > 0; return JiveGlobals.getBooleanProperty(ConnectionSettings.Client.ENABLE_OLD_SSLPORT, false) && SSLConfig.getStore( Purpose.SOCKETBASED_IDENTITYSTORE ).size() > 0;
} catch (KeyStoreException e) { } catch (KeyStoreException e) {
return false; return false;
} catch (IOException e) {
return false;
} }
} }
......
...@@ -142,20 +142,65 @@ ...@@ -142,20 +142,65 @@
<!-- TLS / SSL--> <!-- TLS / SSL-->
<sidebar id="sidebar-certificates" name="${sidebar.sidebar-certificates}"> <sidebar id="sidebar-certificates" name="${sidebar.sidebar-certificates}">
<!-- Server Certificates --> <item id="security-certificate-store-management" name="Certificate Stores"
<item id="security-keystore" name="${sidebar.security-keystore}" url="security-certificate-store-management.jsp"
url="security-keystore.jsp" description="Manage Openfire Certificate stores">
description="${sidebar.security-keystore.descr}"/>
<!--&lt;!&ndash; Certificate key stores ("Openfire Certificates") &ndash;&gt;-->
<!-- C2S Certificate Truststore --> <!--<item id="sidebar-certificates-keys" name="${sidebar.sidebar-certificates-keys}"-->
<item id="security-truststore-c2s" name="${sidebar.security-truststore-c2s}" <!--url="security-keystore.jsp">-->
url="security-truststore.jsp?type=c2s"
description="${sidebar.security-truststore-c2s.descr}"/> <sidebar id="sidebar-certificates-keys-submenu" name="${sidebar.sidebar-certificates-keys-submenu}">
<!-- S2S Certificate Truststore --> <!-- Socket Server Certificates -->
<item id="security-truststore-s2s" name="${sidebar.security-truststore-s2s}" <item id="security-keystore-socket" name="${sidebar.security-keystore-socket}"
url="security-truststore.jsp?type=s2s" url="security-keystore.jsp?connectivityType=socket"
description="${sidebar.security-truststore-s2s.descr}"/> description="${sidebar.security-keystore-socket.descr}"/>
<!-- BOSH Server Certificates -->
<item id="security-keystore-bosh" name="${sidebar.security-keystore-bosh}"
url="security-keystore.jsp?connectivityType=bosh"
description="${sidebar.security-keystore-bosh.descr}"/>
<!-- Administrative Server Certificates -->
<item id="security-keystore-administrative" name="${sidebar.security-keystore-administrative}"
url="security-keystore.jsp?connectivityType=administrative"
description="${sidebar.security-keystore-administrative.descr}"/>
</sidebar>
</item>
<!--&lt;!&ndash; Socket C2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-socket-c2s" name="${sidebar.security-truststore-socket-c2s}"-->
<!--url="security-truststore.jsp?connectivityType=socket&amp;type=c2s"-->
<!--description="${sidebar.security-truststore-socket-c2s.descr}"/>-->
<!--&lt;!&ndash; Socket S2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-socket-s2s" name="${sidebar.security-truststore-socket-s2s}"-->
<!--url="security-truststore.jsp?connectivityType=socket&amp;type=s2s"-->
<!--description="${sidebar.security-truststore-socket-s2s.descr}"/>-->
<!--&lt;!&ndash; BOSH C2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-bosh-c2s" name="${sidebar.security-truststore-bosh-c2s}"-->
<!--url="security-truststore.jsp?connectivityType=bosh&amp;type=c2s"-->
<!--description="${sidebar.security-truststore-bosh-c2s.descr}"/>-->
<!--&lt;!&ndash; BOSH S2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-bosh-s2s" name="${sidebar.security-truststore-bosh-s2s}"-->
<!--url="security-truststore.jsp?connectivityType=bosh&amp;type=s2s"-->
<!--description="${sidebar.security-truststore-bosh-s2s.descr}"/>-->
<!--&lt;!&ndash; Administrative C2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-administrative-c2s" name="${sidebar.security-truststore-administrative-c2s}"-->
<!--url="security-truststore.jsp?connectivityType=administrative&amp;type=c2s"-->
<!--description="${sidebar.security-truststore-administrative-c2s.descr}"/>-->
<!--&lt;!&ndash; Administrative S2S Certificate Truststore &ndash;&gt;-->
<!--<item id="security-truststore-administrative-s2s" name="${sidebar.security-truststore-administrative-s2s}"-->
<!--url="security-truststore.jsp?connectivityType=administrative&amp;type=s2s"-->
<!--description="${sidebar.security-truststore-administrative-s2s.descr}"/>-->
</sidebar> </sidebar>
......
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.jivesoftware.util.Base64;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import sun.security.provider.X509Factory;
import java.io.File;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* Unit tests that verify the functionality of {@link TrustStoreConfig}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class TrustStoreConfigTest
{
private static final Provider PROVIDER = new BouncyCastleProvider();
static
{
// Add the BC provider to the list of security providers
Security.addProvider( PROVIDER );
}
/**
* An instance that is freshly recreated before each test.
*/
private TrustStoreConfig trustStoreConfig;
@Before
public void createFixture() throws Exception
{
// Create a fresh store in a location that holds only temporary files.
final String tempDir = System.getProperty("java.io.tmpdir");
final String location = tempDir + ( tempDir.endsWith( File.separator ) ? "" : File.separator ) + UUID.randomUUID();
final KeyStore keyStore = KeyStore.getInstance( KeyStore.getDefaultType());
final String password = "TS%WV@# aSG 4";
keyStore.load( null, password.toCharArray() );
// Populate the store with a valid CA certificate.
final X509Certificate validCertificate = generateTestSelfSignedCertificate( true );
keyStore.setCertificateEntry( "valid-ca", validCertificate );
// Populate the store with an invalid CA certificate.
final X509Certificate invalidCertificate = generateTestSelfSignedCertificate( false );
keyStore.setCertificateEntry( "invalid-ca", invalidCertificate );
// Persist the keystore file
try ( FileOutputStream fos = new FileOutputStream( location ) ) {
keyStore.store( fos, password.toCharArray() );
}
// Use the new keystore file to create a fresh trust store, which will be used as a fixture by the tests.
trustStoreConfig = new TrustStoreConfig( location, password, keyStore.getType(), false );
}
private static X509Certificate generateTestSelfSignedCertificate( boolean isValid ) throws Exception
{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" );
keyPairGenerator.initialize( 1024 );
KeyPair KPair = keyPairGenerator.generateKeyPair();
X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
v3CertGen.setSerialNumber( BigInteger.valueOf( Math.abs( new SecureRandom().nextInt() ) ) );
X509Principal principal;
if ( isValid ) {
principal = new X509Principal("CN=valid.example.org, OU=None, O=None L=None, C=None");
v3CertGen.setNotBefore( new Date( System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30 ) );
v3CertGen.setNotAfter( new Date( System.currentTimeMillis() + ( 1000L * 60 * 60 * 24 * 365 * 10 ) ) );
} else {
principal = new X509Principal("CN=invalid.example.org, OU=None, O=None L=None, C=None");
v3CertGen.setNotBefore( new Date( System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30 ) );
v3CertGen.setNotAfter( new Date( System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30 ) );
}
v3CertGen.setIssuerDN( principal );
v3CertGen.setSubjectDN( principal );
v3CertGen.setPublicKey( KPair.getPublic() );
v3CertGen.setSignatureAlgorithm( "MD5WithRSAEncryption" );
return v3CertGen.generateX509Certificate(KPair.getPrivate());
}
private static String toPEM( X509Certificate certificate ) throws Exception {
final StringBuilder sb = new StringBuilder();
sb.append( X509Factory.BEGIN_CERT ).append( '\n' );
sb.append( Base64.encodeBytes( certificate.getEncoded() ) ).append( '\n' );
sb.append( X509Factory.END_CERT).append( '\n' );
return sb.toString();
}
@After
public void tearDown() throws Exception
{
// Attempt to delete any left-overs from the test.
if (trustStoreConfig != null)
{
Files.deleteIfExists( Paths.get( trustStoreConfig.getCanonicalPath() ) );
trustStoreConfig = null;
}
}
/**
* The store in the fixture contains two certificates - one that is valid, and one that is invalid.
*
* This test verifies that {@link TrustStoreConfig#getAllCertificates()} returns both.
*/
@Test
public void testGetAll() throws Exception
{
// Setup fixture.
// Execute system under test.
final Map<String, X509Certificate> result = trustStoreConfig.getAllCertificates();
// Verify results.
Assert.assertEquals( 2, result.size() );
}
/**
* The store in the fixture contains two certificates - one that is valid, and one that is invalid.
*
* This test verifies that {@link TrustStoreConfig#getAllValidTrustAnchors()} returns only the valid one.
*/
@Test
public void testGetValid() throws Exception
{
// Setup fixture.
// Execute system under test.
final Set<TrustAnchor> result = trustStoreConfig.getAllValidTrustAnchors();
// Verify results.
Assert.assertEquals( 1, result.size() );
Assert.assertTrue( result.iterator().next().getTrustedCert().getIssuerDN().getName().contains( "CN=valid.example.org" ) );
}
/**
* A chain that has a trust anchor in the trust store (and is otherwise valid) should be trusted.
*/
@Test
public void testTrustCertSignedByCA() throws Exception
{
// Setup fixture
final Collection<X509Certificate> chain = new HashSet<>();
chain.add( (X509Certificate) trustStoreConfig.getStore().getCertificate( "valid-ca" ) ); // somewhat of a hack. Should use a distinct cert for the test.
// Execute System Under Test
final boolean result = trustStoreConfig.canTrust( chain );
// Verify
Assert.assertTrue( result );
}
/**
* A chain that has no trust anchor in the trust store (but is otherwise valid) should not be trusted.
*/
@Test
public void testDontTrustCertNotSignedByCA() throws Exception
{
// Setup fixture
final Collection<X509Certificate> chain = new HashSet<>();
chain.add( generateTestSelfSignedCertificate( true ) );
// Execute System Under Test
final boolean result = trustStoreConfig.canTrust( chain );
// Verify
Assert.assertFalse( result );
}
/**
* This test verifies that when a certificate is installed in the store using
* {@link TrustStoreConfig#installCertificate(String, String)} a certificate chain of which the anchor is that same
* certificate is successfully verified.
*/
@Test
public void verifyWithNewlyInstalledCACert() throws Exception
{
// Setup fixture
final X509Certificate cert = generateTestSelfSignedCertificate( true );
final String pemCert = toPEM( cert );
final Collection<X509Certificate> chain = new HashSet<>();
chain.add( cert ); // somewhat of a hack. Should use a distinct cert for the test.
// Execute System Under Test
trustStoreConfig.installCertificate( "new-cert", pemCert );
final boolean result = trustStoreConfig.canTrust( chain );
// Verify
Assert.assertTrue( result );
}
}
...@@ -59,7 +59,7 @@ public class DNSUtilTest { ...@@ -59,7 +59,7 @@ public class DNSUtilTest {
final List<DNSUtil.WeightedHostAddress> result = DNSUtil.prioritize(new DNSUtil.WeightedHostAddress[]{host}); final List<DNSUtil.WeightedHostAddress> result = DNSUtil.prioritize(new DNSUtil.WeightedHostAddress[]{host});
// verify // verify
Assert.assertEquals(1, result.size()); Assert.assertEquals( 1, result.size() );
Assert.assertEquals(host, result.get(0)); Assert.assertEquals(host, result.get(0));
} }
...@@ -113,7 +113,7 @@ public class DNSUtilTest { ...@@ -113,7 +113,7 @@ public class DNSUtilTest {
// verify // verify
Assert.assertEquals(3, result.size()); Assert.assertEquals(3, result.size());
Assert.assertEquals(hostA, result.get(0)); Assert.assertEquals(hostA, result.get(0));
Assert.assertEquals(hostC, result.get(1)); Assert.assertEquals(hostC, result.get( 1 ));
Assert.assertEquals(hostB, result.get(2)); Assert.assertEquals(hostB, result.get(2));
} }
...@@ -174,8 +174,8 @@ public class DNSUtilTest { ...@@ -174,8 +174,8 @@ public class DNSUtilTest {
} }
// verify // verify
Assert.assertTrue(hostAWasFirst); Assert.assertTrue( hostAWasFirst );
Assert.assertTrue(hostBWasFirst); Assert.assertTrue( hostBWasFirst );
} }
/** /**
...@@ -216,4 +216,131 @@ public class DNSUtilTest { ...@@ -216,4 +216,131 @@ public class DNSUtilTest {
Assert.assertTrue(hostBWasFirst); Assert.assertTrue(hostBWasFirst);
} }
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} finds a match when both
* arguments have the same value.
*/
@Test
public void testNameCoverageExactMatch() throws Exception
{
// setup
final String name = "xmpp.example.org";
final String pattern = name;
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertTrue( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} does not find a match when both
* arguments have different values.
*/
@Test
public void testNameCoverageUnequal() throws Exception
{
// setup
final String name = "xmpp.example.org";
final String pattern = "something.completely.different";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertFalse( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} does not find a match when the
* needle/name is a subdomain of the DNS pattern, without the DNS pattern including a wildcard.
*/
@Test
public void testNameCoverageSubdomainNoWildcard() throws Exception
{
// setup
final String name = "xmpp.example.org";
final String pattern = "example.org";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertFalse( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} does not find a match when the
* last part of the needle/name equals the pattern.
*/
@Test
public void testNameCoveragePartialMatchButNoSubdomain() throws Exception
{
// setup
final String name = "xmppexample.org";
final String pattern = "example.org";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertFalse( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} finds a match when the
* needle/name is a subdomain of the DNS pattern, while the DNS pattern includes a wildcard.
*/
@Test
public void testNameCoverageSubdomainWithWildcard() throws Exception
{
// setup
final String name = "xmpp.example.org";
final String pattern = "*.example.org";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertTrue( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} finds a match when the
* needle/name is a subdomain of a subdomain of the DNS pattern, while the DNS pattern includes a wildcard.
*/
@Test
public void testNameCoverageSubSubdomainWithWildcard() throws Exception
{
// setup
final String name = "deeper.xmpp.example.org";
final String pattern = "*.example.org";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertTrue( result );
}
/**
* A test that verifies that {@link DNSUtil#isNameCoveredByPattern(String, String)} finds a match when the
* needle/name equals the domain part of the DNS pattern, while the DNS pattern includes a wildcard.
*
* Although somewhat shady, the certificate management in Openfire depends on this to hold true.
*/
@Test
public void testNameCoverageSubdomainWithWildcardOfSameDomain() throws Exception
{
// setup
final String name = "xmpp.example.org";
final String pattern = "*.xmpp.example.org";
// do magic
final boolean result = DNSUtil.isNameCoveredByPattern( name, pattern );
// verify
Assert.assertTrue( result );
}
} }
...@@ -155,4 +155,14 @@ ...@@ -155,4 +155,14 @@
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class> <function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String[] split(java.lang.String, java.lang.String)</function-signature> <function-signature>java.lang.String[] split(java.lang.String, java.lang.String)</function-signature>
</function> </function>
<function>
<name>serverIdentities</name>
<function-class>org.jivesoftware.util.CertificateManager</function-class>
<function-signature>java.util.List getServerIdentities(java.security.cert.X509Certificate)</function-signature>
</function>
<function>
<name>clientIdentities</name>
<function-class>org.jivesoftware.util.CertificateManager</function-class>
<function-signature>java.util.List getClientIdentities(java.security.cert.X509Certificate)</function-signature>
</function>
</taglib> </taglib>
<%@ page import="org.jivesoftware.util.CertificateManager, <%@ page errorPage="error.jsp" %>
org.jivesoftware.util.ParamUtils, <%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
org.jivesoftware.util.StringUtils, <%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
org.jivesoftware.openfire.XMPPServer, <%@ page import="org.jivesoftware.util.ParamUtils" %>
org.jivesoftware.openfire.net.SSLConfig, <%@ page import="java.util.HashMap" %>
java.io.ByteArrayInputStream, <%@ page import="java.util.Map" %>
java.util.HashMap, <%@ page import="org.jivesoftware.openfire.XMPPServer" %>
java.util.Map" <%@ page import="org.jivesoftware.openfire.net.SSLConfig" %>
errorPage="error.jsp"%>
<%@ page import="java.security.KeyStore" %>
<%@ taglib uri="admin" prefix="admin" %> <%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
...@@ -16,12 +14,30 @@ ...@@ -16,12 +14,30 @@
<% webManager.init(request, response, session, application, out ); %> <% webManager.init(request, response, session, application, out ); %>
<% // Get parameters: <% // Get parameters:
boolean save = ParamUtils.getParameter(request, "save") != null; final boolean save = ParamUtils.getParameter( request, "save" ) != null;
String privateKey = ParamUtils.getParameter(request, "private-key"); final String privateKey = ParamUtils.getParameter(request, "private-key");
String passPhrase = ParamUtils.getParameter(request, "passPhrase"); final String passPhrase = ParamUtils.getParameter(request, "passPhrase");
String certificate = ParamUtils.getParameter(request, "certificate"); final String certificate = ParamUtils.getParameter(request, "certificate");
final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final Map<String, String> errors = new HashMap<String, String>();
Purpose storePurpose;
try
{
storePurpose = Purpose.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
}
if (! storePurpose.isIdentityStore() ) {
errors.put( "storePurpose", "shoud be an identity store (not a trust store)");
storePurpose = null;
}
pageContext.setAttribute( "storePurpose", storePurpose );
Map<String, String> errors = new HashMap<String, String>();
if (save) { if (save) {
if (privateKey == null || "".equals(privateKey)) { if (privateKey == null || "".equals(privateKey)) {
errors.put("privateKey", "privateKey"); errors.put("privateKey", "privateKey");
...@@ -31,30 +47,24 @@ ...@@ -31,30 +47,24 @@
} }
if (errors.isEmpty()) { if (errors.isEmpty()) {
try { try {
KeyStore keystore; final IdentityStoreConfig identityStoreConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( storePurpose );
try {
keystore = SSLConfig.getKeyStore();
}
catch (Exception e) {
keystore = SSLConfig.initializeKeyStore();
}
// Create an alias for the signed certificate // Create an alias for the signed certificate
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
int index = 1; int index = 1;
String alias = domain + "_" + index; String alias = domain + "_" + index;
while (keystore.containsAlias(alias)) { while ( identityStoreConfig.getStore().containsAlias( alias )) {
index = index + 1; index = index + 1;
alias = domain + "_" + index; alias = domain + "_" + index;
} }
// Import certificate // Import certificate
CertificateManager.installCert(keystore, SSLConfig.gets2sTrustStore(), identityStoreConfig.installCertificate( alias, privateKey, passPhrase, certificate );
SSLConfig.getKeyPassword(), alias, new ByteArrayInputStream(privateKey.getBytes()), passPhrase,
new ByteArrayInputStream(certificate.getBytes()), true, true);
// Save keystore
SSLConfig.saveStores();
// Log the event // Log the event
webManager.logEvent("imported SSL certificate", "alias = "+alias); webManager.logEvent("imported SSL certificate in "+ storePurposeText, "alias = "+alias);
response.sendRedirect("security-keystore.jsp");
response.sendRedirect("security-keystore.jsp?storePurpose="+storePurposeText);
return; return;
} }
catch (Exception e) { catch (Exception e) {
...@@ -67,8 +77,8 @@ ...@@ -67,8 +77,8 @@
<html> <html>
<head> <head>
<title><fmt:message key="ssl.import.certificate.keystore.title"/></title> <title><fmt:message key="ssl.import.certificate.keystore.${connectivityType}.title"/></title>
<meta name="pageID" content="security-keystore"/> <meta name="pageID" content="security-keystore-${connectivityType}"/>
</head> </head>
<body> <body>
...@@ -110,6 +120,7 @@ ...@@ -110,6 +120,7 @@
<!-- BEGIN 'Import Private Key and Certificate' --> <!-- BEGIN 'Import Private Key and Certificate' -->
<form action="import-keystore-certificate.jsp" method="post" name="f"> <form action="import-keystore-certificate.jsp" method="post" name="f">
<input type="hidden" name="connectivityType" value="${connectivityType}"/>
<div class="jive-contentBoxHeader"> <div class="jive-contentBoxHeader">
<fmt:message key="ssl.import.certificate.keystore.boxtitle" /> <fmt:message key="ssl.import.certificate.keystore.boxtitle" />
</div> </div>
......
<%@ page errorPage="error.jsp"%> <%@ page errorPage="error.jsp"%>
<%@ page import="org.jivesoftware.util.CertificateManager"%>
<%@ page import="org.jivesoftware.util.ParamUtils"%> <%@ page import="org.jivesoftware.util.ParamUtils"%>
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%> <%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="java.io.ByteArrayInputStream"%>
<%@ page import="java.util.HashMap"%> <%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%> <%@ page import="java.util.Map"%>
<%@ page import="java.security.KeyStore" %> <%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.TrustStoreConfig" %>
<%@ taglib uri="admin" prefix="admin" %> <%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
...@@ -14,51 +13,38 @@ ...@@ -14,51 +13,38 @@
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager"/> <jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager"/>
<% webManager.init(request, response, session, application, out ); %> <% webManager.init(request, response, session, application, out ); %>
<% final boolean save = ParamUtils.getParameter(request, "save") != null; <% final boolean save = ParamUtils.getParameter(request, "save") != null;
final String type = ParamUtils.getParameter(request, "type"); final String alias = ParamUtils.getParameter(request, "alias");
final String alias = ParamUtils.getParameter(request, "alias"); final String certificate = ParamUtils.getParameter(request, "certificate");
final String certificate = ParamUtils.getParameter(request, "certificate"); final String storePurposeText = ParamUtils.getParameter(request, "storePurpose");
final Map<String, String> errors = new HashMap<String, String>(); final Map<String, String> errors = new HashMap<String, String>();
KeyStore store = null; Purpose storePurpose;
try
if (type == null)
{ {
errors.put("type", "The store type has not been specified."); storePurpose = Purpose.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
} }
else
{
try
{
switch (type)
{
case "s2s":
store = SSLConfig.gets2sTrustStore();
break;
case "c2s": if (! storePurpose.isTrustStore() ) {
store = SSLConfig.getc2sTrustStore(); errors.put( "storePurpose", "shoud be a trust store (not an identity store)");
break; storePurpose = null;
default:
throw new Exception("Unknown store type: " + type);
}
}
catch (Exception e)
{
e.printStackTrace();
errors.put("type", e.getMessage());
}
} }
pageContext.setAttribute( "storePurpose", storePurpose );
if (save && errors.isEmpty()) if (save && errors.isEmpty())
{ {
final TrustStoreConfig trustStoreConfig = (TrustStoreConfig) SSLConfig.getInstance().getStoreConfig( storePurpose );
if (alias == null || "".equals(alias)) if (alias == null || "".equals(alias))
{ {
errors.put("missingalias", "missingalias"); errors.put("missingalias", "missingalias");
} }
else if (store.containsAlias(alias)) else if (trustStoreConfig.getStore().containsAlias( alias ))
{ {
// Verify that the provided alias is not already available // Verify that the provided alias is not already available
errors.put("existingalias", "existingalias"); errors.put("existingalias", "existingalias");
...@@ -73,14 +59,12 @@ ...@@ -73,14 +59,12 @@
try try
{ {
// Import certificate // Import certificate
CertificateManager.installCertsInTrustStore(store, alias, new ByteArrayInputStream(certificate.getBytes())); trustStoreConfig.installCertificate( alias, certificate );
// Save keystore
SSLConfig.saveStores();
// Log the event // Log the event
webManager.logEvent("imported SSL certificate in "+type+" truststore", "alias = "+alias); webManager.logEvent("imported SSL certificate in "+ storePurposeText, "alias = "+alias);
response.sendRedirect("security-truststore.jsp?type="+type+"&importsuccess=true");
response.sendRedirect( "security-truststore.jsp?storePurpose=" + storePurposeText + "&importsuccess=true" );
return; return;
} }
catch (Throwable e) catch (Throwable e)
...@@ -95,9 +79,9 @@ ...@@ -95,9 +79,9 @@
<html> <html>
<head> <head>
<title> <title>
<fmt:message key="ssl.import.certificate.keystore.title"/> - <fmt:message key="ssl.certificates.truststore.${param.type}-title"/> <fmt:message key="ssl.import.certificate.keystore.${connectivityType}.title"/> - <fmt:message key="ssl.certificates.truststore.${param.type}-title"/>
</title> </title>
<meta name="pageID" content="security-truststore-${param.type}"/> <meta name="pageID" content="security-truststore-${connectivityType}-${param.type}"/>
</head> </head>
<body> <body>
...@@ -145,6 +129,7 @@ ...@@ -145,6 +129,7 @@
<!-- BEGIN 'Import Certificate' --> <!-- BEGIN 'Import Certificate' -->
<form action="import-truststore-certificate.jsp?type=${param.type}" method="post" name="f"> <form action="import-truststore-certificate.jsp?type=${param.type}" method="post" name="f">
<input type="hidden" name="connectivityType" value="${connectivityType}"/>
<div class="jive-contentBoxHeader"> <div class="jive-contentBoxHeader">
<fmt:message key="ssl.import.certificate.keystore.boxtitle"/> <fmt:message key="ssl.import.certificate.keystore.boxtitle"/>
</div> </div>
......
...@@ -43,6 +43,9 @@ ...@@ -43,6 +43,9 @@
<%@ page import="java.text.DecimalFormat" %> <%@ page import="java.text.DecimalFormat" %>
<%@ page import="java.util.List" %> <%@ page import="java.util.List" %>
<%@ page import="org.slf4j.LoggerFactory" %> <%@ page import="org.slf4j.LoggerFactory" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreConfig" %>
<%@ page import="org.jivesoftware.openfire.keystore.IdentityStoreConfig" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %> <%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
...@@ -73,7 +76,6 @@ ...@@ -73,7 +76,6 @@
boolean serverOn = (webManager.getXMPPServer() != null); boolean serverOn = (webManager.getXMPPServer() != null);
String interfaceName = JiveGlobals.getXMLProperty("network.interface"); String interfaceName = JiveGlobals.getXMLProperty("network.interface");
ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager()); ConnectionManagerImpl connectionManager = ((ConnectionManagerImpl) XMPPServer.getInstance().getConnectionManager());
NioSocketAcceptor socketAcceptor = connectionManager.getSocketAcceptor(); NioSocketAcceptor socketAcceptor = connectionManager.getSocketAcceptor();
NioSocketAcceptor sslSocketAcceptor = connectionManager.getSSLSocketAcceptor(); NioSocketAcceptor sslSocketAcceptor = connectionManager.getSSLSocketAcceptor();
...@@ -251,8 +253,9 @@ ...@@ -251,8 +253,9 @@
<fmt:message key="index.server_name" /> <fmt:message key="index.server_name" />
</td> </td>
<td class="c2"> <td class="c2">
<% final IdentityStoreConfig storeConfig = (IdentityStoreConfig) SSLConfig.getInstance().getStoreConfig( Purpose.SOCKETBASED_IDENTITYSTORE ); %>
<% try { %> <% try { %>
<% if (!CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {%> <% if (!storeConfig.containsDomainCertificate( "RSA" )) {%>
<img src="images/warning-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="index.certificate-warning" />" title="<fmt:message key="index.certificate-warning" />">&nbsp; <img src="images/warning-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="index.certificate-warning" />" title="<fmt:message key="index.certificate-warning" />">&nbsp;
<% } %> <% } %>
<% } catch (Exception e) { %> <% } catch (Exception e) { %>
...@@ -452,7 +455,8 @@ ...@@ -452,7 +455,8 @@
<td><%= "0.0.0.0".equals(address.getHostName()) ? LocaleUtils.getLocalizedString("ports.all_ports") : address.getHostName() %></td> <td><%= "0.0.0.0".equals(address.getHostName()) ? LocaleUtils.getLocalizedString("ports.all_ports") : address.getHostName() %></td>
<td><%= address.getPort() %></td> <td><%= address.getPort() %></td>
<% try { %> <% try { %>
<% if (!CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), XMPPServer.getInstance().getServerInfo().getXMPPDomain()) || LocalClientSession.getTLSPolicy() == org.jivesoftware.openfire.Connection.TLSPolicy.disabled) { %>
<% if (!storeConfig.containsDomainCertificate( "RSA" ) || LocalClientSession.getTLSPolicy() == org.jivesoftware.openfire.Connection.TLSPolicy.disabled) { %>
<td><img src="images/blank.gif" width="1" height="1" alt=""/></td> <td><img src="images/blank.gif" width="1" height="1" alt=""/></td>
<% } else { %> <% } else { %>
<td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td> <td><img src="images/lock.gif" width="16" height="16" border="0" alt="<fmt:message key="ports.secure.alt" />" title="<fmt:message key="ports.secure.alt" />"/></td>
......
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
<%@ page import="org.jivesoftware.openfire.net.SSLConfig"%> <%@ page import="org.jivesoftware.openfire.net.SSLConfig"%>
<%@ page import="java.util.HashMap"%> <%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%> <%@ page import="java.util.Map"%>
<%@ page import="java.security.KeyStore" %>
<%@ page import="java.security.cert.X509Certificate" %> <%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="javax.xml.bind.DatatypeConverter" %> <%@ page import="javax.xml.bind.DatatypeConverter" %>
<%@ page import="java.security.AlgorithmParameters" %> <%@ page import="java.security.AlgorithmParameters" %>
<%@ page import="org.jivesoftware.openfire.keystore.Purpose" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreConfig" %>
<%@ taglib uri="admin" prefix="admin" %> <%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
...@@ -18,80 +19,69 @@ ...@@ -18,80 +19,69 @@
<jsp:useBean id="now" class="java.util.Date"/> <jsp:useBean id="now" class="java.util.Date"/>
<% webManager.init(request, response, session, application, out ); <% webManager.init(request, response, session, application, out );
final String type = ParamUtils.getParameter(request, "type"); final String alias = ParamUtils.getParameter( request, "alias" );
final String alias = ParamUtils.getParameter(request, "alias"); final String storePurposeText = ParamUtils.getParameter( request, "storePurpose" );
final Map<String, String> errors = new HashMap<String, String>(); final Map<String, String> errors = new HashMap<String, String>();
KeyStore store = null; Purpose storePurpose;
try
if (type == null)
{ {
errors.put("type", "The store type has not been specified."); storePurpose = Purpose.valueOf( storePurposeText );
} catch (RuntimeException ex) {
errors.put( "storePurpose", ex.getMessage() );
storePurpose = null;
} }
else if (alias == null) {
pageContext.setAttribute( "storePurpose", storePurpose );
if (alias == null) {
errors.put("alias", "The alias has not been specified."); errors.put("alias", "The alias has not been specified.");
} }
else else
{ {
try try
{ {
switch (type) final CertificateStoreConfig certificateStoreConfig = SSLConfig.getInstance().getStoreConfig( storePurpose );
{
case "s2s":
store = SSLConfig.gets2sTrustStore();
break;
case "c2s":
store = SSLConfig.getc2sTrustStore();
break;
case "server":
store = SSLConfig.getKeyStore();
break;
default:
throw new Exception("Unknown store type: " + type);
}
// Get the certificate // Get the certificate
final X509Certificate certificate = (X509Certificate) store.getCertificate(alias); final X509Certificate certificate = (X509Certificate) certificateStoreConfig.getStore().getCertificate( alias );
if (certificate == null) { if ( certificate == null ) {
errors.put("alias", "alias"); errors.put( "alias", "alias" );
} else { } else {
pageContext.setAttribute("certificate", certificate); pageContext.setAttribute( "certificate", certificate );
} }
} }
catch (Exception e) catch ( Exception e )
{ {
e.printStackTrace(); e.printStackTrace();
errors.put("type", e.getMessage()); errors.put( "type", e.getMessage() );
} }
} }
// Handle a "go back" click: // Handle a "go back" click:
if (request.getParameter("back") != null) { if ( request.getParameter( "back" ) != null ) {
if ("server".equals(type)) { if ( storePurpose.isTrustStore() ) {
response.sendRedirect("security-keystore.jsp"); response.sendRedirect( "security-truststore.jsp?storePurpose=" + storePurpose );
} else { } else {
response.sendRedirect("security-truststore.jsp?type=" + type); response.sendRedirect( "security-keystore.jsp?storePurpose=" + storePurpose );
} }
return; return;
} }
pageContext.setAttribute("errors", errors); pageContext.setAttribute( "errors", errors );
%> %>
<html> <html>
<head> <head>
<title><fmt:message key="ssl.certificate.details.title"/></title> <title><fmt:message key="ssl.certificate.details.title"/></title>
<c:choose> <c:choose>
<c:when test="${param.type eq 'server'}"> <c:when test="${storePurpose.identityStore}">
<meta name="pageID" content="security-keystore"/> <meta name="pageID" content="security-keystore"/>
</c:when> </c:when>
<c:otherwise> <c:otherwise>
<meta name="pageID" content="security-truststore-${param.type}"/> <meta name="pageID" content="security-truststore"/>
</c:otherwise> </c:otherwise>
</c:choose> </c:choose>
</head> </head>
...@@ -414,6 +404,7 @@ ...@@ -414,6 +404,7 @@
</tr> </tr>
<c:if test="${not empty certificate.sigAlgParams}"> <c:if test="${not empty certificate.sigAlgParams}">
<% <%
final X509Certificate certificate = (X509Certificate) pageContext.getAttribute("certificate"); final X509Certificate certificate = (X509Certificate) pageContext.getAttribute("certificate");
final AlgorithmParameters sigParams = AlgorithmParameters.getInstance(certificate.getSigAlgName()); final AlgorithmParameters sigParams = AlgorithmParameters.getInstance(certificate.getSigAlgName());
sigParams.init( certificate.getSigAlgParams() ); sigParams.init( certificate.getSigAlgParams() );
...@@ -450,7 +441,7 @@ ...@@ -450,7 +441,7 @@
<br/> <br/>
<form action="security-certificate-details.jsp"> <form action="security-certificate-details.jsp">
<input type="hidden" name="type" value="${param.type}"/> <input type="hidden" name="storePurpose" value="${storePurpose}"/>
<div style="text-align: center;"> <div style="text-align: center;">
<input type="submit" name="back" value="<fmt:message key="session.details.back_button"/>"> <input type="submit" name="back" value="<fmt:message key="session.details.back_button"/>">
</div> </div>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -174,7 +174,6 @@ ...@@ -174,7 +174,6 @@
<tr> <tr>
<td width="1%" nowrap> <td width="1%" nowrap>
<label for="nicknametf"><fmt:message key="user.roster.nickname" />:</label> <label for="nicknametf"><fmt:message key="user.roster.nickname" />:</label>
</td>
<td width="99%"> <td width="99%">
<input type="text" name="nickname" size="30" maxlength="255" value="<%= ((nickname!=null) ? StringUtils.escapeForXML(nickname) : "") %>" <input type="text" name="nickname" size="30" maxlength="255" value="<%= ((nickname!=null) ? StringUtils.escapeForXML(nickname) : "") %>"
id="nicknametf"> id="nicknametf">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment