Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OF-1049: Fixes for certificate management #515

Merged
merged 4 commits into from Jan 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/i18n/openfire_i18n_en.properties
Expand Up @@ -2262,6 +2262,10 @@ ssl.certificates.store-management.component-stores.info=These stores are used to
ssl.certificates.store-management.connection-manager-stores.title=Connection Manager Stores
ssl.certificates.store-management.connection-manager-stores.info=These stores are used to establish connections with Openfire Connection Managers.ssl.certificates.store-management.socket-s2s-stores.title=Server Federation Stores
ssl.certificates.store-management.manage=Manage Store Contents
ssl.certificates.store-management.file_label=File
ssl.certificates.store-management.password_label=Password
ssl.certificates.store-management.error.cannot-access=Configuration problem: Unable to access the store.
ssl.certificates.store-management.saved_successfully=Settings updated successfully.

# Openfire Certificates Page

Expand Down
Expand Up @@ -142,7 +142,7 @@ public void startup() {
sslEnabled = false;
try {
final IdentityStore identityStore = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore( ConnectionType.WEBADMIN );
if (adminSecurePort > 0 )
if (identityStore != null && adminSecurePort > 0 )
{
if ( identityStore.getAllCertificates().isEmpty() )
{
Expand Down Expand Up @@ -189,7 +189,7 @@ public void startup() {
}
catch ( Exception e )
{
Log.error( "An exception occured while trying to make available the admin console via HTTPS.", e );
Log.error( "An exception occurred while trying to make available the admin console via HTTPS.", e );
}

// Make sure that at least one connector was registered.
Expand All @@ -206,13 +206,13 @@ public void startup() {

try {
adminServer.start();

// Log the ports that the admin server is listening on.
logAdminConsolePorts();
}
catch (Exception e) {
Log.error("Could not start admin console server", e);
}

// Log the ports that the admin server is listening on.
logAdminConsolePorts();
}

/**
Expand Down
Expand Up @@ -43,32 +43,34 @@ public synchronized void initialize( XMPPServer server )
{
try
{
Log.debug( "(identity store for connection type '{}') Initializing store...", type );
final CertificateStoreConfiguration identityStoreConfiguration = getIdentityStoreConfiguration( type );
typeToIdentityStore.put( type, identityStoreConfiguration );
if ( !identityStores.containsKey( identityStoreConfiguration ) )
{
final IdentityStore store = new IdentityStore( identityStoreConfiguration, false );
identityStores.put( identityStoreConfiguration, store );
}
typeToIdentityStore.put( type, identityStoreConfiguration );
}
catch ( CertificateStoreConfigException | IOException e )
{
Log.warn( "Unable to instantiate identity store for type '" + type + "'", e );
Log.warn( "(identity store for connection type '{}') Unable to instantiate store ", type, e );
}

try
{
Log.debug( "(trust store for connection type '{}') Initializing store...", type );
final CertificateStoreConfiguration trustStoreConfiguration = getTrustStoreConfiguration( type );
typeToTrustStore.put( type, trustStoreConfiguration );
if ( !trustStores.containsKey( trustStoreConfiguration ) )
{
final TrustStore store = new TrustStore( trustStoreConfiguration, false );
trustStores.put( trustStoreConfiguration, store );
}
typeToTrustStore.put( type, trustStoreConfiguration );
}
catch ( CertificateStoreConfigException | IOException e )
{
Log.warn( "Unable to instantiate trust store for type '" + type + "'", e );
Log.warn( "(trust store for connection type '{}') Unable to instantiate store ", type, e );
}
}
}
Expand All @@ -86,16 +88,22 @@ public synchronized void destroy()
public IdentityStore getIdentityStore( ConnectionType type )
{
final CertificateStoreConfiguration configuration = typeToIdentityStore.get( type );
if (configuration == null) {
return null;
}
return identityStores.get( configuration );
}

public TrustStore getTrustStore( ConnectionType type )
{
final CertificateStoreConfiguration configuration = typeToTrustStore.get( type );
if (configuration == null) {
return null;
}
return trustStores.get( configuration );
}

public void replaceIdentityStore( ConnectionType type, CertificateStoreConfiguration configuration ) throws CertificateStoreConfigException
public void replaceIdentityStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
if ( type == null)
{
Expand All @@ -114,7 +122,7 @@ public void replaceIdentityStore( ConnectionType type, CertificateStoreConfigura
if ( !identityStores.containsKey( configuration ) )
{
// This constructor can throw an exception. If it does, the state of the manager should not have already changed.
final IdentityStore store = new IdentityStore( configuration, true );
final IdentityStore store = new IdentityStore( configuration, createIfAbsent );
identityStores.put( configuration, store );
}

Expand Down Expand Up @@ -143,7 +151,7 @@ public void replaceIdentityStore( ConnectionType type, CertificateStoreConfigura
JiveGlobals.setProperty( type.getPrefix() + "keypass", new String( configuration.getPassword() ) );
}

public void replaceTrustStore( ConnectionType type, CertificateStoreConfiguration configuration ) throws CertificateStoreConfigException
public void replaceTrustStore( ConnectionType type, CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
if ( type == null)
{
Expand All @@ -162,7 +170,7 @@ public void replaceTrustStore( ConnectionType type, CertificateStoreConfiguratio
if ( !trustStores.containsKey( configuration ) )
{
// This constructor can throw an exception. If it does, the state of the manager should not have already changed.
final TrustStore store = new TrustStore( configuration, true );
final TrustStore store = new TrustStore( configuration, createIfAbsent );
trustStores.put( configuration, store );
}

Expand Down
28 changes: 11 additions & 17 deletions src/java/org/jivesoftware/openfire/keystore/IdentityStore.java
Expand Up @@ -7,7 +7,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import java.io.IOException;
import java.security.*;
Expand Down Expand Up @@ -40,26 +39,21 @@ public class IdentityStore extends CertificateStore
{
private static final Logger Log = LoggerFactory.getLogger( IdentityStore.class );

// protected final KeyManagerFactory keyFactory;

public IdentityStore( CertificateStoreConfiguration configuration, boolean createIfAbsent ) throws CertificateStoreConfigException
{
super( configuration, createIfAbsent );
// try
// {
// keyFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
// keyFactory.init( store, configuration.getPassword() );
// }
// catch ( UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException ex )
// {
// throw new CertificateStoreConfigException( "Unable to load store of type '" + configuration.getType() + "' from location '" + configuration.getFile() + "'", ex );
// }
}

// public KeyManager[] getKeyManagers()
// {
// return keyFactory.getKeyManagers();
// }
try
{
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
keyManagerFactory.init( this.getStore(), configuration.getPassword() );
}
catch ( NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException ex )
{
throw new CertificateStoreConfigException( "Unable to initialize identity store (a common cause: the password for a key is different from the password of the entire store).", ex );
}

}

/**
* Creates a Certificate Signing Request based on the private key and certificate identified by the provided alias.
Expand Down
150 changes: 128 additions & 22 deletions src/web/security-certificate-store-management.jsp
Expand Up @@ -3,6 +3,10 @@
<%@ page import="java.util.HashMap" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreManager" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateStoreConfiguration" %>
<%@ page import="java.io.File" %>
<%@ page import="org.jivesoftware.util.Log" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
Expand All @@ -12,10 +16,55 @@
<jsp:useBean id="now" class="java.util.Date"/>
<% webManager.init(request, response, session, application, out );

final CertificateStoreManager certificateStoreManager = XMPPServer.getInstance().getCertificateStoreManager();

final Map<String, String> errors = new HashMap<>();
pageContext.setAttribute( "errors", errors );
pageContext.setAttribute( "connectionTypes", ConnectionType.values() );
pageContext.setAttribute( "certificateStoreManager", XMPPServer.getInstance().getCertificateStoreManager() );
pageContext.setAttribute( "certificateStoreManager", certificateStoreManager );

final boolean update = request.getParameter("update") != null;
if ( update ) {
ConnectionType connectionType = null;
try {
connectionType = ConnectionType.valueOf( request.getParameter( "connectionType" ) );
} catch ( IllegalArgumentException ex ) {
Log.warn( ex );
errors.put( "connectionType", ex.getMessage() );
}

final String locKey = request.getParameter( "loc-key" );
final String pwdKey = request.getParameter( "pwd-key" );
final String locTrust = request.getParameter( "loc-trust" );
final String pwdTrust = request.getParameter( "pwd-trust" );

if ( locKey == null || locKey.isEmpty() ) {
errors.put( "locKey", "Identity Store location must be defined." );
}
if ( pwdKey == null || pwdKey.isEmpty() ) {
errors.put( "pwdKey", "Identity Store password must be defined." );
}
if ( locTrust == null || locTrust.isEmpty() ) {
errors.put( "locTrust", "Trust Store location must be defined." );
}
if ( pwdTrust == null || pwdTrust.isEmpty() ) {
errors.put( "pwdTrust", "Trust Store password must be defined." );
}

if ( errors.isEmpty() ) {
try
{
final CertificateStoreConfiguration configKey = new CertificateStoreConfiguration( "jks", new File( locKey ), pwdKey.toCharArray() );
final CertificateStoreConfiguration configTrust = new CertificateStoreConfiguration( "jks", new File( locTrust ), pwdTrust.toCharArray() );
certificateStoreManager.replaceIdentityStore( connectionType, configKey, false );
certificateStoreManager.replaceTrustStore( connectionType, configTrust, false );
pageContext.setAttribute( "updated", true );
} catch ( Exception ex ) {
Log.warn( ex );
errors.put( "update", ex.getMessage() );
}
}
}
%>
<html>
<head>
Expand All @@ -40,6 +89,13 @@
</admin:infobox>
</c:forEach>

<!-- Display success report, but only if there were no errors. -->
<c:if test="${updated and empty errors}">
<admin:infoBox type="success">
<fmt:message key="ssl.certificates.store-management.saved_successfully"/>
</admin:infoBox>
</c:if>

<p>
<fmt:message key="ssl.certificates.store-management.info-1"/>
</p>
Expand Down Expand Up @@ -73,27 +129,77 @@
</c:choose>
</c:set>

<admin:contentBox title="${title}">
<p>
<c:out value="${description}"/>
</p>

<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td><label for="loc-key-socket"><fmt:message key="ssl.certificates.identity-store"/>:</label></td>
<td><input id="loc-key-socket" name="loc-key-socket" type="text" size="80" readonly value="${certificateStoreManager.getIdentityStore(connectionType).configuration.file}"/></td>
<td><a href="security-keystore.jsp?connectionType=${connectionType}"><fmt:message key="ssl.certificates.store-management.manage"/></a></td>
</tr>
<tr>
<td><label for="loc-trust-socket-c2s"><fmt:message key="ssl.certificates.trust-store"/>:</label></td>
<td><input id="loc-trust-socket-c2s" name="loc-trust-socket-c2s" type="text" size="80" readonly value="${certificateStoreManager.getTrustStore(connectionType).configuration.file}"/></td>
<td><a href="security-truststore.jsp?connectionType=${connectionType}"><fmt:message key="ssl.certificates.store-management.manage"/></a></td>
</tr>
</tbody>
</table>

</admin:contentBox>
<form action="security-certificate-store-management.jsp" method="post">
<input type="hidden" name="connectionType" value="${connectionType}"/>

<admin:contentBox title="${title}">
<p>
<c:out value="${description}"/>
</p>

<h4><fmt:message key="ssl.certificates.identity-store"/></h4>

<c:if test="${empty certificateStoreManager.getIdentityStore(connectionType)}">
<admin:infobox type="warning"><fmt:message key="ssl.certificates.store-management.error.cannot-access"/></admin:infobox>
</c:if>

<c:set var="pwdKey" value=""/>
<c:forEach items="${certificateStoreManager.getIdentityStoreConfiguration(connectionType).password}" var="c">
<c:set var="pwdKey" value="${pwdKey}${c}"/>
</c:forEach>

<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td><label for="loc-key"><fmt:message key="ssl.certificates.store-management.file_label"/>:</label></td>
<td><input id="loc-key" name="loc-key" type="text" size="80" value="${certificateStoreManager.getIdentityStoreConfiguration(connectionType).file}"/></td>
</tr>
<tr>
<td><label for="pwd-key"><fmt:message key="ssl.certificates.store-management.password_label"/>:</label></td>
<td><input id="pwd-key" name="pwd-key" type="password" size="30" value="${pwdKey}"/></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><a href="security-keystore.jsp?connectionType=${connectionType}"><fmt:message key="ssl.certificates.store-management.manage"/></a></td>
</tr>
</tbody>
</table>

<br/>
<h4><fmt:message key="ssl.certificates.trust-store"/></h4>

<c:if test="${empty certificateStoreManager.getTrustStore(connectionType)}">
<admin:infobox type="warning"><fmt:message key="ssl.certificates.store-management.error.cannot-access"/></admin:infobox>
</c:if>

<c:set var="pwdTrust" value=""/>
<c:forEach items="${certificateStoreManager.getTrustStoreConfiguration(connectionType).password}" var="c">
<c:set var="pwdTrust" value="${pwdTrust}${c}"/>
</c:forEach>

<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td><label for="loc-trust"><fmt:message key="ssl.certificates.store-management.file_label"/>:</label></td>
<td><input id="loc-trust" name="loc-trust" type="text" size="80" value="${certificateStoreManager.getTrustStoreConfiguration(connectionType).file}"/></td>
</tr>
<tr>
<td><label for="pwd-trust"><fmt:message key="ssl.certificates.store-management.password_label"/>:</label></td>
<td><input id="pwd-trust" name="pwd-trust" type="password" size="30" value="${pwdTrust}"/></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><a href="security-truststore.jsp?connectionType=${connectionType}"><fmt:message key="ssl.certificates.store-management.manage"/></a></td>
</tr>
</tbody>
</table>

<br/>
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />">

</admin:contentBox>

</form>

</c:forEach>

Expand Down