Unverified Commit 07bebd80 authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #1031 from guusdk/OF-1495_Add-SANs

OF-1495: Add subject alternative names to certs and CSRs
parents 21639a68 bed4eed5
......@@ -2383,6 +2383,9 @@ ssl.certificates.keystore.restart_server=Certificates were modified so HTTP serv
ssl.certificates.keystore.io_error=Unable to access the certificate key store. The file may be corrupt.
ssl.certificates.keystore.no_installed=One or more certificates are missing. Click {0}here{1} to generate self-signed \
certificates or {2}here{3} to import a signed certificate and its private key.
ssl.certificates.keystore.no_complete_installed=The installed certificates do not cover all of the identities of this \
server. Click {0}here{1} to replace your certificates with self-signed certificates that do, or {2}here{3} to import \
a signed certificate and its private key.
ssl.certificates.keystore.error_importing-reply=An error occurred while importing the Certificate Authority reply. \
Verify that the reply is correct and that it belongs to the correct certificate.
ssl.certificates.keystore.issuer-updated=Issuer information updated successfully.
......
......@@ -13,6 +13,7 @@ import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.*;
......@@ -110,7 +111,7 @@ public class IdentityStore extends CertificateStore
return pemCSR;
}
catch ( IOException | KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | OperatorCreationException e )
catch ( IOException | KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | OperatorCreationException | CertificateParsingException e )
{
throw new CertificateStoreConfigException( "Cannot generate CSR for alias '"+ alias +"'", e );
}
......@@ -442,6 +443,69 @@ public class IdentityStore extends CertificateStore
}
}
/**
* Checks if the store contains a certificate of a particular algorithm that contains at least all of the identities
* of this server (which includes the XMPP domain name, but also its hostname, and XMPP addresses of components
* that are currently being hosted).
*
* This method will not distinguish between self-signed and non-self-signed certificates.
*/
public synchronized boolean containsAllIdentityCertificate( String algorithm ) throws CertificateStoreConfigException
{
final Collection<String> dns = CertificateManager.determineSubjectAlternateNameDnsNameValues();
try
{
for ( final String alias : Collections.list( store.aliases() ) )
{
final Set<String> missingDns = new HashSet<>();
final Certificate certificate = store.getCertificate( alias );
if ( !( certificate instanceof X509Certificate ) )
{
continue;
}
if ( !certificate.getPublicKey().getAlgorithm().equalsIgnoreCase( algorithm ) )
{
continue;
}
final List<String> serverIdentities = CertificateManager.getServerIdentities( (X509Certificate) certificate );
// Are all of our DNS names covered?
for ( String dnsId : dns )
{
boolean found = false;
for ( String identity : serverIdentities )
{
if ( !DNSUtil.isNameCoveredByPattern( dnsId, identity ) )
{
found = true;
break;
}
}
if ( !found )
{
Log.info( "Certificate with alias '{}' is missing DNS identity '{}'.", alias, dnsId );
missingDns.add( dnsId );
}
}
if ( missingDns.isEmpty() )
{
return true;
}
}
return false;
}
catch ( KeyStoreException e )
{
throw new CertificateStoreConfigException( "An exception occurred while searching for " + algorithm + " certificates that match the Openfire domain.", e );
}
}
/**
* Populates the key store with a self-signed certificate for the domain of this XMPP service.
*/
......@@ -469,6 +533,7 @@ public class IdentityStore extends CertificateStore
final String name = JiveGlobals.getProperty( "xmpp.domain" ).toLowerCase();
final String alias = name + "_" + algorithm.toLowerCase();
final int validityInDays = 5*365;
final Set<String> sanDnsNames = CertificateManager.determineSubjectAlternateNameDnsNameValues();
Log.info( "Generating a new private key and corresponding self-signed certificate for domain name '{}', using the {} algorithm (sign-algorithm: {} with a key size of {} bits). Certificate will be valid for {} days.", name, algorithm, signAlgorithm, keySize, validityInDays );
// Generate public and private keys
......@@ -477,7 +542,7 @@ public class IdentityStore extends CertificateStore
final KeyPair keyPair = generateKeyPair( algorithm.toUpperCase(), keySize );
// Create X509 certificate with keys and specified domain
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, name, name, name, signAlgorithm );
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, name, name, name, signAlgorithm, sanDnsNames );
// Store new certificate and private key in the key store
store.setKeyEntry( alias, keyPair.getPrivate(), configuration.getPassword(), new X509Certificate[]{cert} );
......
......@@ -17,6 +17,8 @@
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.openfire.keystore.CertificateUtils" %>
<%@ page import="java.util.Set" %>
<%@ 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" %>
......@@ -114,7 +116,8 @@
int days = 60;
// Regenerate self-sign certs whose subjectDN matches the issuerDN and set the new issuerDN
X509Certificate newCertificate = CertificateManager.createX509V3Certificate(new KeyPair(pubKey, privKey), days, builder, builder, domain, signAlgoritm);
final Set<String> sanDnsNames = CertificateManager.determineSubjectAlternateNameDnsNameValues();
X509Certificate newCertificate = CertificateManager.createX509V3Certificate(new KeyPair(pubKey, privKey), days, builder, builder, domain, signAlgoritm, sanDnsNames);
keyStore.setKeyEntry(alias, privKey, identityStore.getConfiguration().getPassword(), new X509Certificate[] { newCertificate });
}
}
......
<%@page import="org.jivesoftware.util.StringUtils"%>
<%@page import="java.util.LinkedHashMap"%>
<%@page import="java.security.PrivateKey"%>
<%@page import="org.jivesoftware.util.CertificateManager"%>
<%@ page import="org.jivesoftware.util.CookieUtils" %>
......@@ -12,10 +11,7 @@
<%@ page import="org.jivesoftware.openfire.spi.ConnectionType" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.*" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
......@@ -28,6 +24,7 @@
<% // Get parameters:
boolean generate = ParamUtils.getBooleanParameter(request, "generate");
boolean generateFull = ParamUtils.getBooleanParameter(request, "generateFull");
boolean delete = ParamUtils.getBooleanParameter(request, "delete");
boolean importReply = ParamUtils.getBooleanParameter(request, "importReply");
final String alias = ParamUtils.getParameter( request, "alias" );
......@@ -37,9 +34,10 @@
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (generate | delete | importReply) {
if (generate | generateFull | delete | importReply) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
generate = false;
generateFull = false;
delete = false;
importReply = false;
errors.put("csrf", "CSRF Failure!");
......@@ -78,6 +76,8 @@
pageContext.setAttribute( "validRSACert", identityStore.containsDomainCertificate( "RSA" ) );
pageContext.setAttribute( "validDSACert", identityStore.containsDomainCertificate( "DSA" ) );
pageContext.setAttribute( "allIDRSACert", identityStore.containsAllIdentityCertificate( "RSA" ) );
pageContext.setAttribute( "allIDDSACert", identityStore.containsAllIdentityCertificate( "DSA" ) );
if ( delete )
{
......@@ -108,7 +108,6 @@
if (generate) {
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
try {
if (errors.containsKey("ioerror") || !identityStore.containsDomainCertificate("DSA")) {
identityStore.addSelfSignedDomainCertificate("DSA");
......@@ -128,6 +127,25 @@
}
}
if (generateFull) {
try {
if (!identityStore.containsAllIdentityCertificate("DSA")) {
identityStore.addSelfSignedDomainCertificate("DSA");
}
if (!identityStore.containsAllIdentityCertificate("RSA")) {
identityStore.addSelfSignedDomainCertificate("RSA");
}
// Save new certificates into the key store
identityStore.persist();
// Log the event
webManager.logEvent("generated SSL self-signed certs", null);
response.sendRedirect("security-keystore.jsp?connectionType="+connectionType);
return;
} catch (Exception e) {
e.printStackTrace();
errors.put("generate", e.getMessage());
}
}
if (importReply) {
String reply = ParamUtils.getParameter(request, "reply");
if (alias != null && reply != null && reply.trim().length() > 0) {
......@@ -187,7 +205,8 @@
</admin:infobox>
</c:forEach>
<c:if test="${not validDSACert or not validRSACert}">
<c:choose>
<c:when test="${not validDSACert or not validRSACert}">
<admin:infobox type="warning">
<fmt:message key="ssl.certificates.keystore.no_installed">
<fmt:param value="<a href='security-keystore.jsp?csrf=${csrf}&generate=true&connectionType=${connectionType}'>"/>
......@@ -196,7 +215,19 @@
<fmt:param value="</a>"/>
</fmt:message>
</admin:infobox>
</c:if>
</c:when>
<c:when test="${not allIDDSACert or not allIDRSACert}">
<admin:infobox type="info">
<fmt:message key="ssl.certificates.keystore.no_complete_installed">
<fmt:param value="<a href='security-keystore.jsp?csrf=${csrf}&generateFull=true&connectionType=${connectionType}'>"/>
<fmt:param value="</a>"/>
<fmt:param value="<a href='import-keystore-certificate.jsp?connectionType=${connectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</admin:infobox>
</c:when>
</c:choose>
<c:if test="${param.addupdatesuccess}"><admin:infobox type="success"><fmt:message key="ssl.certificates.added_updated"/></admin:infobox></c:if>
<c:if test="${param.generatesuccess}"><admin:infobox type="success"><fmt:message key="ssl.certificates.generated"/></admin:infobox></c:if>
......
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