Commit b66f4bcf authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

The SubjectAltName certificate extension is now used to get server names. JM-564

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3431 b35dd754-fafc-0310-a699-88a17e54d16e
parent db7d09c8
......@@ -14,9 +14,9 @@ package org.jivesoftware.wildfire.net;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.ClientSession;
import org.jivesoftware.wildfire.Session;
import org.jivesoftware.wildfire.XMPPServer;
......@@ -285,9 +285,8 @@ public class SASLAuthentication {
hostname = StringUtils.decodeBase64(hostname);
// Check that hostname matches the one provided in a certificate
for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) {
if (hostname
.equals(TLSStreamHandler.getPeerIdentity((X509Certificate) certificate)))
{
if (TLSStreamHandler.getPeerIdentities((X509Certificate) certificate)
.contains(hostname)) {
authenticationSuccessful(hostname);
return true;
}
......
......@@ -16,6 +16,7 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
/**
* ServerTrustManager is a Trust Manager that is only used for s2s connections. This TrustManager
......@@ -79,7 +80,7 @@ class ServerTrustManager implements X509TrustManager {
if (verify) {
int nSize = x509Certificates.length;
String peerIdentity = TLSStreamHandler.getPeerIdentity(x509Certificates[0]);
List<String> peerIdentities = TLSStreamHandler.getPeerIdentities(x509Certificates[0]);
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) {
// Working down the chain, for every certificate in the chain,
......@@ -99,12 +100,12 @@ class ServerTrustManager implements X509TrustManager {
}
catch (GeneralSecurityException generalsecurityexception) {
throw new CertificateException(
"signature verification failed of " + peerIdentity);
"signature verification failed of " + peerIdentities);
}
}
else {
throw new CertificateException(
"subject/issuer verification failed of " + peerIdentity);
"subject/issuer verification failed of " + peerIdentities);
}
}
principalLast = principalSubject;
......@@ -121,7 +122,7 @@ class ServerTrustManager implements X509TrustManager {
.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false))
{
Log.warn("Accepting self-signed certificate of remote server: " +
peerIdentity);
peerIdentities);
trusted = true;
}
}
......@@ -129,23 +130,23 @@ class ServerTrustManager implements X509TrustManager {
Log.error(e);
}
if (!trusted) {
throw new CertificateException("root certificate not trusted of " + peerIdentity);
throw new CertificateException("root certificate not trusted of " + peerIdentities);
}
}
// Verify that the first certificate in the chain corresponds to
// the server we desire to authenticate.
// Check if the certificate uses a wildcard indicating that subdomains are valid
if (peerIdentity.startsWith("*.")) {
if (peerIdentities.size() == 1 && peerIdentities.get(0).startsWith("*.")) {
// Remove the wildcard
peerIdentity = peerIdentity.replace("*.", "");
String peerIdentity = peerIdentities.get(0).replace("*.", "");
// Check if the requested subdomain matches the certified domain
if (!server.endsWith(peerIdentity)) {
throw new CertificateException("target verification failed of " + peerIdentity);
throw new CertificateException("target verification failed of " + peerIdentities);
}
}
else if (!server.equals(peerIdentity)) {
throw new CertificateException("target verification failed of " + peerIdentity);
else if (!peerIdentities.contains(server)) {
throw new CertificateException("target verification failed of " + peerIdentities);
}
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.validity", true)) {
......@@ -157,7 +158,7 @@ class ServerTrustManager implements X509TrustManager {
x509Certificates[i].checkValidity(date);
}
catch (GeneralSecurityException generalsecurityexception) {
throw new CertificateException("invalid date of " + peerIdentity);
throw new CertificateException("invalid date of " + peerIdentities);
}
}
}
......
......@@ -11,6 +11,9 @@
package org.jivesoftware.wildfire.net;
import org.bouncycastle.asn1.*;
import org.jivesoftware.util.Log;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
......@@ -18,14 +21,16 @@ import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.security.Principal;
import java.util.*;
/**
* TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating
......@@ -77,21 +82,75 @@ public class TLSStreamHandler {
private static ByteBuffer hsBB = ByteBuffer.allocate(0);
/**
* Returns the identity of the remote server as defined in the specified certificate. The
* identity is defined in the subjectDN of the certificate and it can also be defined in
* the subjectAltName extension of type "xmpp". When the extension is being used then the
* identity defined in the extension in going to be returned. Otherwise, the value stored in
* Returns the identities of the remote server as defined in the specified certificate. The
* identities are defined in the subjectDN of the certificate and it can also be defined in
* the subjectAltName extensions of type "xmpp". When the extension is being used then the
* identities defined in the extension are going to be returned. Otherwise, the value stored in
* the subjectDN is returned.
*
* @param x509Certificate the certificate the holds the identity of the remote server.
* @return the identity of the remote server as defined in the specified certificate.
* @param x509Certificate the certificate the holds the identities of the remote server.
* @return the identities of the remote server as defined in the specified certificate.
*/
public static List<String> getPeerIdentities(X509Certificate x509Certificate) {
// Look the identity in the subjectAltName extension if available
List<String> names = getSubjectAlternativeNames(x509Certificate);
if (names.isEmpty()) {
String name = x509Certificate.getSubjectDN().getName();
name = name.replace("CN=", "");
// Create an array with the unique identity
names = new ArrayList<String>();
names.add(name);
}
return names;
}
/**
* Returns the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*
* @param certificate the certificate presented by the remote entity.
* @return the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*/
public static String getPeerIdentity(X509Certificate x509Certificate) {
Principal principalSubject = x509Certificate.getSubjectDN();
// TODO Look the identity in the subjectAltName extension if available
String name = principalSubject.getName();
name = name.replace("CN=", "");
return name;
private static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection altNames = certificate.getSubjectAlternativeNames();
// Check that the certificate includes the SubjectAltName extension
if (altNames == null) {
return Collections.emptyList();
}
// Use the type OtherName to search for the certified server name
for (Iterator lists=altNames.iterator(); lists.hasNext();) {
List item = (List) lists.next();
Integer type = (Integer) item.get(0);
if (type.intValue() == 0) {
// Type OtherName found so return the associated value
try {
// Value is encoded using ASN.1 so decode it to get the server's identity
ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
DEREncodable encoded = decoder.readObject();
encoded = ((DERSequence) encoded).getObjectAt(1);
encoded = ((DERTaggedObject) encoded).getObject();
encoded = ((DERTaggedObject) encoded).getObject();
String identity = ((DERUTF8String) encoded).getString();
// Add the decoded server name to the list of identities
identities.add(identity);
}
catch (UnsupportedEncodingException e) {}
catch (IOException e) {}
}
// Other types are not good for XMPP so ignore them
if (Log.isDebugEnabled()) {
Log.debug("SubjectAltName of invalid type found: " + certificate);
}
}
}
catch (CertificateParsingException e) {
Log.error("Error parsing SubjectAltName in certificate: " + certificate, e);
}
return identities;
}
/**
......
......@@ -8,22 +8,25 @@
- a copy of which is included in this distribution.
--%>
<%@ page import="org.jivesoftware.util.*,
java.util.*,
org.jivesoftware.wildfire.net.SSLConfig,
java.security.KeyStore,
java.security.cert.CertificateFactory,
java.security.cert.Certificate,
java.io.ByteArrayInputStream"
<%@ page import="org.jivesoftware.util.JiveGlobals,
org.jivesoftware.util.ParamUtils,
org.jivesoftware.wildfire.ClientSession,
org.jivesoftware.wildfire.Connection,
org.jivesoftware.wildfire.ConnectionManager,
org.jivesoftware.wildfire.XMPPServer,
org.jivesoftware.wildfire.net.SSLConfig"
errorPage="error.jsp"
%>
<%@ page import="org.jivesoftware.wildfire.ClientSession"%>
<%@ page import="org.jivesoftware.wildfire.net.SocketConnection"%>
<%@ page import="org.jivesoftware.wildfire.XMPPServer"%>
<%@ page import="org.jivesoftware.wildfire.ConnectionManager"%>
<%@ page import="org.jivesoftware.wildfire.Connection"%>
<%@ page import="java.security.cert.X509Certificate"%>
<%@ page import="org.jivesoftware.wildfire.net.TLSStreamHandler"%>
<%@ page import="java.io.ByteArrayInputStream"%>
<%@ page import="java.security.KeyStore"%>
<%@ page import="java.security.cert.Certificate"%>
<%@ page import="java.security.cert.CertificateFactory"%>
<%@ page import="java.security.cert.X509Certificate"%>
<%@ page import="java.util.Date"%>
<%@ page import="java.util.Enumeration"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
......@@ -562,11 +565,18 @@
i++;
String a = (String)aliases.nextElement();
X509Certificate c = (X509Certificate) keyStore.getCertificate(a);
StringBuffer identities = new StringBuffer();
for (String identity : TLSStreamHandler.getPeerIdentities(c)) {
identities.append(identity).append(", ");
}
if (identities.length() > 0) {
identities.setLength(identities.length() - 2);
}
%>
<tr valign="top">
<td id="rs<%=i%>" width="1" rowspan="1"><%= (i) %>.</td>
<td>
<%= TLSStreamHandler.getPeerIdentity(c) %> (<%= a %>)
<%= identities.toString() %> (<%= a %>)
</td>
<td>
<% boolean expired = c.getNotAfter().before(new Date());
......
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