Commit 3276384c authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Refactoring work. JM-893

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@6061 b35dd754-fafc-0310-a699-88a17e54d16e
parent 8ec834b8
...@@ -19,19 +19,22 @@ import org.bouncycastle.asn1.x509.X509Name; ...@@ -19,19 +19,22 @@ import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PKCS10CertificationRequest; import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.jivesoftware.wildfire.net.SSLConfig;
import org.jivesoftware.wildfire.net.TLSStreamHandler;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.*; import java.security.*;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.*; import java.util.*;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Utility class that provides similar functionality to the keytool tool. Generated certificates * Utility class that provides similar functionality to the keytool tool. Generated certificates
...@@ -41,6 +44,8 @@ import java.util.LinkedList; ...@@ -41,6 +44,8 @@ import java.util.LinkedList;
*/ */
public class CertificateManager { public class CertificateManager {
private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)");
private static Provider provider = new BouncyCastleProvider(); private static Provider provider = new BouncyCastleProvider();
/** /**
...@@ -48,6 +53,8 @@ public class CertificateManager { ...@@ -48,6 +53,8 @@ public class CertificateManager {
*/ */
private static final int CERT_REQ_LINE_LENGTH = 76; private static final int CERT_REQ_LINE_LENGTH = 76;
private static List<CertificateEventListener> listeners = new CopyOnWriteArrayList<CertificateEventListener>();
static { static {
// Add the BC provider to the list of security providers // Add the BC provider to the list of security providers
Security.addProvider(provider); Security.addProvider(provider);
...@@ -60,6 +67,7 @@ public class CertificateManager { ...@@ -60,6 +67,7 @@ public class CertificateManager {
* to the store. * to the store.
* *
* @param ksKeys key store where the new certificate and private key are going to be stored. * @param ksKeys key store where the new certificate and private key are going to be stored.
* @param keyPassword password of the keystore.
* @param alias name to use when storing the certificate in the key store. * @param alias name to use when storing the certificate in the key store.
* @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME" * @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME"
* @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe" * @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe"
...@@ -68,16 +76,24 @@ public class CertificateManager { ...@@ -68,16 +76,24 @@ public class CertificateManager {
* @throws GeneralSecurityException * @throws GeneralSecurityException
* @throws IOException * @throws IOException
*/ */
public static X509Certificate createDSACert(KeyStore ksKeys, String alias, String issuerDN, String subjectDN, public static X509Certificate createDSACert(KeyStore ksKeys, String keyPassword, String alias, String issuerDN,
String domain) String subjectDN, String domain)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException {
// Generate public and private keys // Generate public and private keys
KeyPair keyPair = generateKeyPair("DSA", 1024); KeyPair keyPair = generateKeyPair("DSA", 1024);
// Create X509 certificate with keys and specified domain // Create X509 certificate with keys and specified domain
X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "SHA1withDSA"); X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "SHA1withDSA");
// Store new certificate and private key in the keystore // Store new certificate and private key in the keystore
ksKeys.setKeyEntry(alias, keyPair.getPrivate(), SSLConfig.getKeyPassword().toCharArray(), ksKeys.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new X509Certificate[]{cert});
new X509Certificate[]{cert}); // Notify listeners that a new certificate has been created
for (CertificateEventListener listener : listeners) {
try {
listener.certificateCreated(ksKeys, alias, cert);
}
catch (Exception e) {
Log.error(e);
}
}
// Return new certificate // Return new certificate
return cert; return cert;
} }
...@@ -89,6 +105,7 @@ public class CertificateManager { ...@@ -89,6 +105,7 @@ public class CertificateManager {
* to the store. * to the store.
* *
* @param ksKeys key store where the new certificate and private key are going to be stored. * @param ksKeys key store where the new certificate and private key are going to be stored.
* @param keyPassword password of the keystore.
* @param alias name to use when storing the certificate in the key store. * @param alias name to use when storing the certificate in the key store.
* @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME" * @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME"
* @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe" * @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe"
...@@ -97,20 +114,129 @@ public class CertificateManager { ...@@ -97,20 +114,129 @@ public class CertificateManager {
* @throws GeneralSecurityException * @throws GeneralSecurityException
* @throws IOException * @throws IOException
*/ */
public static X509Certificate createRSACert(KeyStore ksKeys, String alias, String issuerDN, String subjectDN, public static X509Certificate createRSACert(KeyStore ksKeys, String keyPassword, String alias, String issuerDN,
String domain) String subjectDN, String domain)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException {
// Generate public and private keys // Generate public and private keys
KeyPair keyPair = generateKeyPair("RSA", 1024); KeyPair keyPair = generateKeyPair("RSA", 1024);
// Create X509 certificate with keys and specified domain // Create X509 certificate with keys and specified domain
X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "MD5withRSA"); X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "MD5withRSA");
// Store new certificate and private key in the keystore // Store new certificate and private key in the keystore
ksKeys.setKeyEntry(alias, keyPair.getPrivate(), SSLConfig.getKeyPassword().toCharArray(), ksKeys.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new X509Certificate[]{cert});
new X509Certificate[]{cert}); // Notify listeners that a new certificate has been created
for (CertificateEventListener listener : listeners) {
try {
listener.certificateCreated(ksKeys, alias, cert);
}
catch (Exception e) {
Log.error(e);
}
}
// Return new certificate // Return new certificate
return cert; return cert;
} }
/**
* Deletes the specified certificate from the
*
* @param ksKeys key store where the certificate is stored.
* @param alias alias of the certificate to delete.
* @throws GeneralSecurityException
* @throws IOException
*/
public static void deleteCertificate(KeyStore ksKeys, String alias) throws GeneralSecurityException, IOException {
ksKeys.deleteEntry(alias);
// Notify listeners that a new certificate has been created
for (CertificateEventListener listener : listeners) {
try {
listener.certificateDeleted(ksKeys, alias);
}
catch (Exception e) {
Log.error(e);
}
}
}
/**
* 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 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();
Matcher matcher = cnPattern.matcher(name);
if (matcher.find()) {
name = matcher.group(2);
}
// 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>.
*/
private static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection<List<?>> 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 (List item : altNames) {
Integer type = (Integer) item.get(0);
if (type == 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) {
// Ignore
}
catch (IOException e) {
// Ignore
}
catch (Exception e) {
Log.error("Error decoding subjectAltName", 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;
}
/** /**
* Returns true if an RSA certificate was found in the specified keystore for the specified domain. * Returns true if an RSA certificate was found in the specified keystore for the specified domain.
* *
...@@ -160,7 +286,7 @@ public class CertificateManager { ...@@ -160,7 +286,7 @@ public class CertificateManager {
private static boolean isCertificate(KeyStore ksKeys, String domain, String algorithm) throws KeyStoreException { private static boolean isCertificate(KeyStore ksKeys, String domain, String algorithm) throws KeyStoreException {
for (Enumeration<String> aliases = ksKeys.aliases(); aliases.hasMoreElements();) { for (Enumeration<String> aliases = ksKeys.aliases(); aliases.hasMoreElements();) {
X509Certificate certificate = (X509Certificate) ksKeys.getCertificate(aliases.nextElement()); X509Certificate certificate = (X509Certificate) ksKeys.getCertificate(aliases.nextElement());
for (String identity : TLSStreamHandler.getPeerIdentities(certificate)) { for (String identity : getPeerIdentities(certificate)) {
if (identity.endsWith(domain) && certificate.getPublicKey().getAlgorithm().equals(algorithm)) { if (identity.endsWith(domain) && certificate.getPublicKey().getAlgorithm().equals(algorithm)) {
return true; return true;
} }
...@@ -231,6 +357,9 @@ public class CertificateManager { ...@@ -231,6 +357,9 @@ public class CertificateManager {
* certificate then you can also specify if you want to verify that the root certificate in the chain * certificate then you can also specify if you want to verify that the root certificate in the chain
* can be trusted. * can be trusted.
* *
* @param keyStore key store where the certificate is stored.
* @param trustStore key store where ca certificates are stored.
* @param keyPassword password of the keystore.
* @param alias the alias of the existing certificate being signed. * @param alias the alias of the existing certificate being signed.
* @param inputStream the stream containing the CA reply. * @param inputStream the stream containing the CA reply.
* @param trustCACerts true if certificates present in the truststore file will be used to verify the * @param trustCACerts true if certificates present in the truststore file will be used to verify the
...@@ -240,9 +369,8 @@ public class CertificateManager { ...@@ -240,9 +369,8 @@ public class CertificateManager {
* @return true if the CA reply was successfully processed. * @return true if the CA reply was successfully processed.
* @throws Exception * @throws Exception
*/ */
public static boolean installReply(String alias, InputStream inputStream, boolean trustCACerts, public static boolean installReply(KeyStore keyStore, KeyStore trustStore, String keyPassword, String alias, InputStream inputStream, boolean trustCACerts,
boolean validateRoot) throws Exception { boolean validateRoot) throws Exception {
KeyStore keyStore = SSLConfig.getKeyStore();
// Check that there is a certificate for the specified alias // Check that there is a certificate for the specified alias
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias); X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
...@@ -251,7 +379,7 @@ public class CertificateManager { ...@@ -251,7 +379,7 @@ public class CertificateManager {
return false; return false;
} }
// Retrieve the private key of the stored certificate // Retrieve the private key of the stored certificate
PrivateKey privKey = (PrivateKey) keyStore.getKey(alias, SSLConfig.getKeyPassword().toCharArray()); PrivateKey privKey = (PrivateKey) keyStore.getKey(alias, keyPassword.toCharArray());
// Load certificates found in the PEM input stream // Load certificates found in the PEM input stream
List<X509Certificate> certs = new ArrayList<X509Certificate>(); List<X509Certificate> certs = new ArrayList<X509Certificate>();
for (Certificate cert : CertificateFactory.getInstance("X509").generateCertificates(inputStream)) { for (Certificate cert : CertificateFactory.getInstance("X509").generateCertificates(inputStream)) {
...@@ -260,26 +388,57 @@ public class CertificateManager { ...@@ -260,26 +388,57 @@ public class CertificateManager {
if (certs.isEmpty()) { if (certs.isEmpty()) {
throw new Exception("Reply has no certificates"); throw new Exception("Reply has no certificates");
} }
Collection<X509Certificate> newCerts; List<X509Certificate> newCerts;
if (certs.size() == 1) { if (certs.size() == 1) {
// Reply has only one certificate // Reply has only one certificate
newCerts = establishCertChain(certificate, certs.get(0), trustCACerts); newCerts = establishCertChain(keyStore, trustStore, certificate, certs.get(0), trustCACerts);
} else { } else {
// Reply has a chain of certificates // Reply has a chain of certificates
newCerts = validateReply(alias, certificate, certs, trustCACerts, validateRoot); newCerts = validateReply(keyStore, trustStore, alias, certificate, certs, trustCACerts, validateRoot);
} }
if (newCerts != null) { if (newCerts != null) {
keyStore.setKeyEntry(alias, privKey, SSLConfig.getKeyPassword().toCharArray(), keyStore.setKeyEntry(alias, privKey, keyPassword.toCharArray(),
newCerts.toArray(new X509Certificate[newCerts.size()])); newCerts.toArray(new X509Certificate[newCerts.size()]));
// Notify listeners that a new certificate has been created
for (CertificateEventListener listener : listeners) {
try {
listener.certificateSigned(keyStore, alias, newCerts);
}
catch (Exception e) {
Log.error(e);
}
}
return true; return true;
} else { } else {
return false; return false;
} }
} }
/**
* Registers a listener to receive events.
*
* @param listener the listener.
*/
public static void addListener(CertificateEventListener listener) {
if (listener == null) {
throw new NullPointerException();
}
listeners.add(listener);
}
private static Collection<X509Certificate> establishCertChain(X509Certificate certificate, /**
* Unregisters a listener to receive events.
*
* @param listener the listener.
*/
public static void removeListener(CertificateEventListener listener) {
listeners.remove(listener);
}
private static List<X509Certificate> establishCertChain(KeyStore keyStore, KeyStore trustStore,
X509Certificate certificate,
X509Certificate certReply, boolean trustCACerts) X509Certificate certReply, boolean trustCACerts)
throws Exception { throws Exception {
if (certificate != null) { if (certificate != null) {
...@@ -293,11 +452,11 @@ public class CertificateManager { ...@@ -293,11 +452,11 @@ public class CertificateManager {
} }
} }
Map<Principal, List<X509Certificate>> knownCerts = new Hashtable<Principal, List<X509Certificate>>(); Map<Principal, List<X509Certificate>> knownCerts = new Hashtable<Principal, List<X509Certificate>>();
if (SSLConfig.getKeyStore().size() > 0) { if (keyStore.size() > 0) {
knownCerts.putAll(getCertsByIssuer(SSLConfig.getKeyStore())); knownCerts.putAll(getCertsByIssuer(keyStore));
} }
if (trustCACerts && SSLConfig.getTrustStore().size() > 0) { if (trustCACerts && trustStore.size() > 0) {
knownCerts.putAll(getCertsByIssuer(SSLConfig.getTrustStore())); knownCerts.putAll(getCertsByIssuer(trustStore));
} }
LinkedList<X509Certificate> answer = new LinkedList<X509Certificate>(); LinkedList<X509Certificate> answer = new LinkedList<X509Certificate>();
if (buildChain(certReply, answer, knownCerts)) { if (buildChain(certReply, answer, knownCerts)) {
...@@ -394,9 +553,10 @@ public class CertificateManager { ...@@ -394,9 +553,10 @@ public class CertificateManager {
* @param userCert the user certificate of the alias * @param userCert the user certificate of the alias
* @param replyCerts the chain provided in the reply * @param replyCerts the chain provided in the reply
*/ */
private static Collection<X509Certificate> validateReply(String alias, X509Certificate userCert, private static List<X509Certificate> validateReply(KeyStore keyStore, KeyStore trustStore, String alias,
List<X509Certificate> replyCerts, boolean trustCACerts, X509Certificate userCert, List<X509Certificate> replyCerts,
boolean verifyRoot) throws Exception { boolean trustCACerts, boolean verifyRoot)
throws Exception {
// order the certs in the reply (bottom-up). // order the certs in the reply (bottom-up).
int i; int i;
PublicKey userPubKey = userCert.getPublicKey(); PublicKey userPubKey = userCert.getPublicKey();
...@@ -451,17 +611,16 @@ public class CertificateManager { ...@@ -451,17 +611,16 @@ public class CertificateManager {
} }
// do we trust the (root) cert at the top? // do we trust the (root) cert at the top?
KeyStore caStore = SSLConfig.getTrustStore();
X509Certificate topCert = replyCerts.get(replyCerts.size() - 1); X509Certificate topCert = replyCerts.get(replyCerts.size() - 1);
boolean foundInKeyStore = SSLConfig.getKeyStore().getCertificateAlias(topCert) != null; boolean foundInKeyStore = keyStore.getCertificateAlias(topCert) != null;
boolean foundInCAStore = trustCACerts && (caStore.getCertificateAlias(topCert) != null); boolean foundInCAStore = trustCACerts && (trustStore.getCertificateAlias(topCert) != null);
if (!foundInKeyStore && !foundInCAStore) { if (!foundInKeyStore && !foundInCAStore) {
boolean verified = false; boolean verified = false;
X509Certificate rootCert = null; X509Certificate rootCert = null;
if (trustCACerts) { if (trustCACerts) {
for (Enumeration<String> aliases = caStore.aliases(); aliases.hasMoreElements();) { for (Enumeration<String> aliases = trustStore.aliases(); aliases.hasMoreElements();) {
String name = aliases.nextElement(); String name = aliases.nextElement();
rootCert = (X509Certificate) caStore.getCertificate(name); rootCert = (X509Certificate) trustStore.getCertificate(name);
if (rootCert != null) { if (rootCert != null) {
try { try {
topCert.verify(rootCert.getPublicKey()); topCert.verify(rootCert.getPublicKey());
......
...@@ -324,10 +324,12 @@ public class XMPPServer { ...@@ -324,10 +324,12 @@ public class XMPPServer {
// No certificates were found so create new self-signed certificates // No certificates were found so create new self-signed certificates
if (!dsaFound) { if (!dsaFound) {
CertificateManager.createDSACert(ksKeys, name + "_dsa", "cn=" + name, "cn=" + name, "*." + name); CertificateManager.createDSACert(ksKeys, SSLConfig.getKeyPassword(), name + "_dsa", "cn=" + name,
"cn=" + name, "*." + name);
} }
if (!rsaFound) { if (!rsaFound) {
CertificateManager.createRSACert(ksKeys, name + "_rsa", "cn=" + name, "cn=" + name, "*." + name); CertificateManager.createRSACert(ksKeys, SSLConfig.getKeyPassword(), name + "_rsa", "cn=" + name,
"cn=" + name, "*." + name);
} }
// Save new certificates into the key store // Save new certificates into the key store
if (!dsaFound || !rsaFound) { if (!dsaFound || !rsaFound) {
......
...@@ -11,10 +11,11 @@ ...@@ -11,10 +11,11 @@
package org.jivesoftware.wildfire.net; package org.jivesoftware.wildfire.net;
import org.dom4j.Element;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.QName; import org.dom4j.Element;
import org.dom4j.Namespace; import org.dom4j.Namespace;
import org.dom4j.QName;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils; import org.jivesoftware.util.StringUtils;
...@@ -414,7 +415,7 @@ public class SASLAuthentication { ...@@ -414,7 +415,7 @@ public class SASLAuthentication {
SocketConnection connection = (SocketConnection) session.getConnection(); SocketConnection connection = (SocketConnection) session.getConnection();
try { try {
for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) { for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) {
if (TLSStreamHandler.getPeerIdentities((X509Certificate) certificate) if (CertificateManager.getPeerIdentities((X509Certificate) certificate)
.contains(hostname)) { .contains(hostname)) {
authenticationSuccessful(session, hostname, null); authenticationSuccessful(session, hostname, null);
return Status.authenticated; return Status.authenticated;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.jivesoftware.wildfire.net; package org.jivesoftware.wildfire.net;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
...@@ -80,7 +81,7 @@ class ServerTrustManager implements X509TrustManager { ...@@ -80,7 +81,7 @@ class ServerTrustManager implements X509TrustManager {
if (verify) { if (verify) {
int nSize = x509Certificates.length; int nSize = x509Certificates.length;
List<String> peerIdentities = TLSStreamHandler.getPeerIdentities(x509Certificates[0]); List<String> peerIdentities = CertificateManager.getPeerIdentities(x509Certificates[0]);
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) { if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) {
// Working down the chain, for every certificate in the chain, // Working down the chain, for every certificate in the chain,
...@@ -165,25 +166,6 @@ class ServerTrustManager implements X509TrustManager { ...@@ -165,25 +166,6 @@ class ServerTrustManager implements X509TrustManager {
} }
} }
private boolean isChainTrusted(X509Certificate[] chain) {
boolean trusted = false;
try {
// Start with the root and see if it is in the Keystore.
// The root is at the end of the chain.
for (int i = chain.length - 1; i >= 0; i--) {
if (trustStore.getCertificateAlias(chain[i]) != null) {
trusted = true;
break;
}
}
}
catch (Exception e) {
Log.error(e);
trusted = false;
}
return trusted;
}
public X509Certificate[] getAcceptedIssuers() { public X509Certificate[] getAcceptedIssuers() {
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false)) { if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false)) {
// Answer an empty list since we accept any issuer // Answer an empty list since we accept any issuer
......
...@@ -11,9 +11,7 @@ ...@@ -11,9 +11,7 @@
package org.jivesoftware.wildfire.net; package org.jivesoftware.wildfire.net;
import org.bouncycastle.asn1.*;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
...@@ -22,21 +20,12 @@ import javax.net.ssl.SSLSession; ...@@ -22,21 +20,12 @@ import javax.net.ssl.SSLSession;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket; import java.net.Socket;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating * TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating
...@@ -87,88 +76,6 @@ public class TLSStreamHandler { ...@@ -87,88 +76,6 @@ public class TLSStreamHandler {
*/ */
private static ByteBuffer hsBB = ByteBuffer.allocate(0); private static ByteBuffer hsBB = ByteBuffer.allocate(0);
private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)");
/**
* 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 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();
Matcher matcher = cnPattern.matcher(name);
if (matcher.find()) {
name = matcher.group(2);
}
// 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>.
*/
private static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection<List<?>> 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 (List item : altNames) {
Integer type = (Integer) item.get(0);
if (type == 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) {
// Ignore
}
catch (IOException e) {
// Ignore
}
catch (Exception e) {
Log.error("Error decoding subjectAltName", 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;
}
/** /**
* Creates a new TLSStreamHandler and secures the plain socket connection. When connecting * Creates a new TLSStreamHandler and secures the plain socket connection. When connecting
* to a remote server then <tt>clientMode</tt> will be <code>true</code> and * to a remote server then <tt>clientMode</tt> will be <code>true</code> and
......
<%@ page import="org.jivesoftware.util.CertificateManager, <%@ page import="org.jivesoftware.util.CertificateManager,
org.jivesoftware.util.JiveGlobals, org.jivesoftware.util.JiveGlobals,
org.jivesoftware.util.ParamUtils, org.jivesoftware.util.ParamUtils,
org.jivesoftware.util.StringUtils,
org.jivesoftware.wildfire.XMPPServer, org.jivesoftware.wildfire.XMPPServer,
org.jivesoftware.wildfire.net.SSLConfig, org.jivesoftware.wildfire.net.SSLConfig,
org.jivesoftware.wildfire.net.TLSStreamHandler,
java.io.ByteArrayInputStream, java.io.ByteArrayInputStream,
java.security.KeyStore, java.security.KeyStore,
java.security.cert.X509Certificate, java.security.PrivateKey,
java.util.Date" java.security.cert.X509Certificate"
errorPage="error.jsp"%> errorPage="error.jsp"%>
<%@ page import="java.util.Date" %>
<%@ page import="java.util.Enumeration" %> <%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.HashMap" %> <%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %> <%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="java.security.PrivateKey" %>
<%@ 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" %>
...@@ -40,11 +39,11 @@ ...@@ -40,11 +39,11 @@
try { try {
if (!CertificateManager.isDSACertificate(keyStore, domain)) { if (!CertificateManager.isDSACertificate(keyStore, domain)) {
CertificateManager CertificateManager
.createDSACert(keyStore, domain + "_dsa", "cn=" + domain, "cn=" + domain, "*." + domain); .createDSACert(keyStore, SSLConfig.getKeyPassword(), domain + "_dsa", "cn=" + domain, "cn=" + domain, "*." + domain);
} }
if (!CertificateManager.isRSACertificate(keyStore, domain)) { if (!CertificateManager.isRSACertificate(keyStore, domain)) {
CertificateManager CertificateManager
.createRSACert(keyStore, domain + "_rsa", "cn=" + domain, "cn=" + domain, "*." + domain); .createRSACert(keyStore, SSLConfig.getKeyPassword(), domain + "_rsa", "cn=" + domain, "cn=" + domain, "*." + domain);
} }
// Save new certificates into the key store // Save new certificates into the key store
SSLConfig.saveStores(); SSLConfig.saveStores();
...@@ -57,7 +56,7 @@ ...@@ -57,7 +56,7 @@
if (delete) { if (delete) {
if (type != null && alias != null) { if (type != null && alias != null) {
try { try {
SSLConfig.getKeyStore().deleteEntry(alias); CertificateManager.deleteCertificate(keyStore, alias);
SSLConfig.saveStores(); SSLConfig.saveStores();
response.sendRedirect("ssl-certificates.jsp?deletesuccess=true"); response.sendRedirect("ssl-certificates.jsp?deletesuccess=true");
return; return;
...@@ -72,7 +71,8 @@ ...@@ -72,7 +71,8 @@
String reply = ParamUtils.getParameter(request, "reply"); String reply = ParamUtils.getParameter(request, "reply");
if (alias != null && reply != null && reply.trim().length() > 0) { if (alias != null && reply != null && reply.trim().length() > 0) {
try { try {
CertificateManager.installReply(alias, new ByteArrayInputStream(reply.getBytes()), true, true); CertificateManager.installReply(SSLConfig.getKeyStore(), SSLConfig.getTrustStore(),
SSLConfig.getKeyPassword(), alias, new ByteArrayInputStream(reply.getBytes()), true, true);
SSLConfig.saveStores(); SSLConfig.saveStores();
response.sendRedirect("ssl-certificates.jsp?importsuccess=true"); response.sendRedirect("ssl-certificates.jsp?importsuccess=true");
return; return;
...@@ -206,19 +206,6 @@ ...@@ -206,19 +206,6 @@
</tbody> </tbody>
</table> </table>
</div><br> </div><br>
<% } else if (errors.size() > 0) { %>
<div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr><td class="jive-icon"><img src="images/error-16x16.gif" width="16" height="16" border="0" alt=""></td>
<td class="jive-icon-label">
<fmt:message key="ssl.settings.error_certificate" />
</td></tr>
</tbody>
</table>
</div><br>
<% } %> <% } %>
<!-- BEGIN 'Installed Certificates' --> <!-- BEGIN 'Installed Certificates' -->
...@@ -257,7 +244,7 @@ ...@@ -257,7 +244,7 @@
String a = (String) aliases.nextElement(); String a = (String) aliases.nextElement();
X509Certificate c = (X509Certificate) keyStore.getCertificate(a); X509Certificate c = (X509Certificate) keyStore.getCertificate(a);
StringBuffer identities = new StringBuffer(); StringBuffer identities = new StringBuffer();
for (String identity : TLSStreamHandler.getPeerIdentities(c)) { for (String identity : CertificateManager.getPeerIdentities(c)) {
identities.append(identity).append(", "); identities.append(identity).append(", ");
} }
if (identities.length() > 0) { if (identities.length() > 0) {
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
<%@ page import="org.jivesoftware.wildfire.XMPPServer" %> <%@ page import="org.jivesoftware.wildfire.XMPPServer" %>
<%@ page import="org.jivesoftware.wildfire.net.SSLConfig" %> <%@ page import="org.jivesoftware.wildfire.net.SSLConfig" %>
<%@ page import="java.security.KeyStore" %> <%@ page import="java.security.KeyStore" %>
<%@ page import="java.security.PrivateKey" %>
<%@ page import="java.security.cert.X509Certificate" %> <%@ page import="java.security.cert.X509Certificate" %>
<%@ page import="org.jivesoftware.util.StringUtils" %> <%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.*" %> <%@ 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/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" %>
...@@ -72,9 +72,9 @@ ...@@ -72,9 +72,9 @@
boolean isSigningPending = !isSelfSigned && keyStore.getCertificateChain(alias).length == 1; boolean isSigningPending = !isSelfSigned && keyStore.getCertificateChain(alias).length == 1;
if (isSelfSigned || isSigningPending) { if (isSelfSigned || isSigningPending) {
if (CertificateManager.isDSACertificate(certificate)) { if (CertificateManager.isDSACertificate(certificate)) {
CertificateManager.createDSACert(keyStore, alias, issuerDN.toString(), "cn=" + domain, "*." + domain); CertificateManager.createDSACert(keyStore, SSLConfig.getKeyPassword(), alias, issuerDN.toString(), "cn=" + domain, "*." + domain);
} else { } else {
CertificateManager.createRSACert(keyStore, alias, issuerDN.toString(), "cn=" + domain, "*." + domain); CertificateManager.createRSACert(keyStore, SSLConfig.getKeyPassword(), alias, issuerDN.toString(), "cn=" + domain, "*." + domain);
} }
} }
} }
......
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