diff --git a/core/pom.xml b/core/pom.xml
index 12b6c5f42..7faa934e7 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -254,6 +254,13 @@
bcpkix-jdk15on
1.57
+
+
+ org.bouncycastle.bctls-jdk15on.1.57.org.bouncycastle
+
+ bctls-jdk15on
+ 1.57
+
diff --git a/core/src/main/java/org/jivesoftware/AccountCreationWizard.java b/core/src/main/java/org/jivesoftware/AccountCreationWizard.java
index 50a391a10..73f68ebfe 100644
--- a/core/src/main/java/org/jivesoftware/AccountCreationWizard.java
+++ b/core/src/main/java/org/jivesoftware/AccountCreationWizard.java
@@ -17,6 +17,7 @@
package org.jivesoftware;
+import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.jivesoftware.resource.Res;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.XMPPError;
@@ -32,10 +33,12 @@ import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.spark.util.ResourceUtils;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.log.Log;
+import org.jivesoftware.sparkimpl.certificates.SparkSSLContext;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
import org.jxmpp.util.XmppStringUtils;
+import javax.net.ssl.SSLContext;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
@@ -353,7 +356,19 @@ public class AccountCreationWizard extends JPanel {
{
builder.setHost( localPreferences.getXmppHost() );
}
-
+
+ if (securityMode != ConnectionConfiguration.SecurityMode.disabled && !useOldSSL) {
+ // This use STARTTLS which starts initially plain connection to upgrade it to TLS, it use the same port as
+ // plain connections which is 5222.
+ try {
+ SSLContext context = SparkSSLContext.setUpContext();
+ builder.setCustomSSLContext(context);
+ builder.setSecurityMode( securityMode );
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ Log.warning("Couldnt establish secured connection", e);
+ }
+ }
+
if ( securityMode != ConnectionConfiguration.SecurityMode.disabled && useOldSSL )
{
if (!hostPortConfigured) {
diff --git a/core/src/main/java/org/jivesoftware/LoginDialog.java b/core/src/main/java/org/jivesoftware/LoginDialog.java
index a9862935c..55332aadc 100644
--- a/core/src/main/java/org/jivesoftware/LoginDialog.java
+++ b/core/src/main/java/org/jivesoftware/LoginDialog.java
@@ -44,9 +44,10 @@ import org.jivesoftware.spark.ui.login.LoginSettingDialog;
import org.jivesoftware.spark.util.*;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.log.Log;
+import org.jivesoftware.sparkimpl.plugin.manager.Enterprise;
+import org.jivesoftware.sparkimpl.certificates.SparkSSLContext;
import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettings;
import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettingsManager;
-import org.jivesoftware.sparkimpl.plugin.manager.Enterprise;
import org.jivesoftware.sparkimpl.settings.JiveInfo;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
@@ -57,6 +58,7 @@ import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
+import javax.net.ssl.SSLContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -277,6 +279,18 @@ public class LoginDialog {
builder.setProxyInfo( proxyInfo );
}
+ if (securityMode != ConnectionConfiguration.SecurityMode.disabled && !useOldSSL) {
+ // This use STARTTLS which starts initially plain connection to upgrade it to TLS, it use the same port as
+ // plain connections which is 5222.
+ try {
+ SSLContext context = SparkSSLContext.setUpContext();
+ builder.setCustomSSLContext(context);
+ builder.setSecurityMode( securityMode );
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ Log.warning("Couldnt establish secured connection", e);
+ }
+ }
+
if ( securityMode != ConnectionConfiguration.SecurityMode.disabled && useOldSSL ) {
if (!hostPortConfigured) {
// SMACK 4.1.9 does not support XEP-0368, and does not apply a port change, if the host is not changed too.
diff --git a/core/src/main/java/org/jivesoftware/spark/ui/login/CertificatesManagerSettingsPanel.java b/core/src/main/java/org/jivesoftware/spark/ui/login/CertificatesManagerSettingsPanel.java
index 7ed383099..344e23b62 100644
--- a/core/src/main/java/org/jivesoftware/spark/ui/login/CertificatesManagerSettingsPanel.java
+++ b/core/src/main/java/org/jivesoftware/spark/ui/login/CertificatesManagerSettingsPanel.java
@@ -44,6 +44,7 @@ import org.jivesoftware.spark.util.ResourceUtils;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.certificates.CertificateController;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
+import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
/**
* This class serve as visual in implementation of manageable list of certificates. Together with CertificateController
@@ -64,6 +65,8 @@ public class CertificatesManagerSettingsPanel extends JPanel implements ActionLi
private JCheckBox acceptSelfSigned = new JCheckBox();
private JCheckBox checkCRL = new JCheckBox();
private JCheckBox checkOCSP = new JCheckBox();
+ private JCheckBox allowSoftFail = new JCheckBox();
+ private JCheckBox acceptNotValidYet = new JCheckBox();
private JButton showCert = new JButton();
private JFileChooser fileChooser = new JFileChooser();
private JButton fileButton = new JButton();
@@ -76,6 +79,7 @@ public class CertificatesManagerSettingsPanel extends JPanel implements ActionLi
this.localPreferences = localPreferences;
certControll = new CertificateController(localPreferences);
setLayout(new GridBagLayout());
+ certControll.createCertTableModel();
certTable = new JTable(certControll.getTableModel()){
@Override
@@ -112,64 +116,141 @@ public class CertificatesManagerSettingsPanel extends JPanel implements ActionLi
ResourceUtils.resButton(acceptAll, Res.getString("checkbox.accept.all"));
ResourceUtils.resButton(acceptExpired, Res.getString("checkbox.accept.expired"));
- ResourceUtils.resButton(acceptRevoked, Res.getString("checkbox.accept.invalid"));
+ ResourceUtils.resButton(acceptNotValidYet, Res.getString("checkbox.accept.not.valid.yet"));
+ ResourceUtils.resButton(acceptRevoked, Res.getString("checkbox.accept.revoked"));
ResourceUtils.resButton(acceptSelfSigned, Res.getString("checkbox.accept.self.signed"));
ResourceUtils.resButton(checkCRL, Res.getString("checkbox.check.crl"));
ResourceUtils.resButton(checkOCSP, Res.getString("checkbox.check.ocsp"));
+ ResourceUtils.resButton(allowSoftFail, Res.getString("checkbox.allow.soft.fail"));
ResourceUtils.resButton(showCert, Res.getString("button.show.certificate"));
ResourceUtils.resButton(fileButton, Res.getString("label.choose.file"));
-
+
+ acceptAll.setSelected(localPreferences.isAcceptAllCertificates());
+ acceptSelfSigned.setSelected(localPreferences.isAcceptSelfSigned());
+ acceptExpired.setSelected(localPreferences.isAcceptExpired());
+ acceptNotValidYet.setSelected(localPreferences.isAcceptNotValidYet());
+ acceptRevoked.setSelected(localPreferences.isAcceptRevoked());
+ checkCRL.setSelected(localPreferences.isCheckCRL());
+ checkOCSP.setSelected(localPreferences.isCheckOCSP());
+ allowSoftFail.setSelected(localPreferences.isAllowSoftFail());
+
+
acceptAll.addActionListener(this);
certTable.addMouseListener(this);
certTable.getModel().addTableModelListener(this);
showCert.setEnabled(false);
showCert.addActionListener(this);
fileButton.addActionListener(this);
+ checkCRL.addActionListener(this);
+ checkOCSP.addActionListener(this);
+ acceptRevoked.addActionListener(this);
+ acceptExpired.setEnabled(!acceptAll.isSelected());
+ acceptNotValidYet.setEnabled(!acceptAll.isSelected());
+ acceptRevoked.setEnabled(!acceptAll.isSelected());
+ acceptSelfSigned.setEnabled(!acceptAll.isSelected());
+ checkCRL.setEnabled(!acceptRevoked.isSelected());
+ checkOCSP.setEnabled(checkCRL.isSelected());
+ allowSoftFail.setEnabled(checkOCSP.isSelected());
filePanel.setLayout(new GridBagLayout());
filePanel.add(fileButton, new GridBagConstraints(0, 0, 2, 1, 1.0, 1.0, WEST, HORIZONTAL, DEFAULT_INSETS, 40, 0));
filePanel.setBorder(
BorderFactory.createTitledBorder(Res.getString("label.certificate.add.certificate.to.truststore")));
- add(scrollPane, new GridBagConstraints(0, 0, 6, 1, 1.0, 1.0, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(acceptAll, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(acceptSelfSigned, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(acceptExpired, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(acceptRevoked, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(checkCRL, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(checkOCSP, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
- add(showCert, new GridBagConstraints(2, 1, 2, 1, 0.0, 0.0, WEST, HORIZONTAL, DEFAULT_INSETS, 40, 0));
- add(filePanel, new GridBagConstraints(2, 2, 2, 4, 0.0, 0.0, WEST, HORIZONTAL, DEFAULT_INSETS, 40, 0));
+ add(scrollPane, new GridBagConstraints(0, 0, 6, 1, 1.0, 1.0, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+
+ add(acceptAll, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+ add(acceptSelfSigned, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+
+ add(acceptExpired, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+ add(acceptNotValidYet, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+
+ add(acceptRevoked, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+ add(checkCRL, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+
+ add(checkOCSP, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+ add(allowSoftFail, new GridBagConstraints(3, 2, 1, 1, 0.0, 0.5, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+
+ add(showCert, new GridBagConstraints(4, 1, 2, 1, 0.0, 0.0, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
+ add(filePanel, new GridBagConstraints(4, 2, 2, 4, 0.0, 0.0, WEST, HORIZONTAL, DEFAULT_INSETS, 0, 0));
}
- @Override
- public void actionPerformed(ActionEvent e) {
- if (e.getSource() == acceptAll && acceptAll.isSelected()) {
- acceptSelfSigned.setSelected(true);
- acceptExpired.setSelected(true);
- acceptRevoked.setSelected(true);
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == acceptAll) {
+ if (acceptAll.isSelected()) {
- acceptSelfSigned.setEnabled(false);
- acceptExpired.setEnabled(false);
- acceptRevoked.setEnabled(false);
- } else if (e.getSource() == acceptAll && !acceptAll.isSelected()) {
- acceptSelfSigned.setEnabled(true);
- acceptExpired.setEnabled(true);
- acceptRevoked.setEnabled(true);
- } else if (e.getSource() == showCert) {
- certControll.showCertificate();
- } else if (e.getSource() == fileButton) {
- addCertificate();
- }
- }
+ acceptSelfSigned.setSelected(true);
+ acceptExpired.setSelected(true);
+ acceptNotValidYet.setSelected(true);
+ acceptRevoked.setSelected(true);
+ checkCRL.setSelected(false);
+ checkOCSP.setSelected(false);
+ allowSoftFail.setSelected(false);
- @Override
- public void mouseClicked(MouseEvent e) {
+ acceptSelfSigned.setEnabled(false);
+ acceptExpired.setEnabled(false);
+ acceptNotValidYet.setEnabled(false);
+ acceptRevoked.setEnabled(false);
+ checkCRL.setEnabled(false);
+ checkOCSP.setEnabled(false);
+ allowSoftFail.setEnabled(false);
+ } else if (!acceptAll.isSelected()) {
- }
+ acceptSelfSigned.setEnabled(true);
+ acceptExpired.setEnabled(true);
+ acceptNotValidYet.setEnabled(true);
+ acceptRevoked.setEnabled(true);
- @Override
- public void mouseEntered(MouseEvent e) {
+ }
+ } else if (e.getSource() == showCert) {
+ certControll.showCertificate();
+
+ } else if (e.getSource() == fileButton) {
+ addCertificate();
+
+ } else if (e.getSource() == checkCRL) {
+
+ if (checkCRL.isSelected()) {
+
+ checkOCSP.setEnabled(true);
+ } else if (!checkCRL.isSelected()) {
+
+ checkOCSP.setSelected(false);
+ checkOCSP.setEnabled(false);
+ allowSoftFail.setEnabled(false);
+ }
+
+ } else if (e.getSource() == acceptRevoked) {
+ if (acceptRevoked.isSelected()) {
+
+ checkCRL.setSelected(false);
+ checkOCSP.setSelected(false);
+
+ checkCRL.setEnabled(false);
+ checkOCSP.setEnabled(false);
+ } else if (!acceptRevoked.isSelected()) {
+ checkCRL.setEnabled(true);
+ }
+ } else if (e.getSource() == checkOCSP) {
+ if (checkOCSP.isSelected()) {
+
+ allowSoftFail.setEnabled(true);
+ } else if (!checkOCSP.isSelected()) {
+
+ allowSoftFail.setEnabled(false);
+ allowSoftFail.setSelected(false);
+ }
+ }
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
}
@@ -248,4 +329,16 @@ public class CertificatesManagerSettingsPanel extends JPanel implements ActionLi
}
});
}
+
+ public void saveSettings() {
+ localPreferences.setAcceptExpired(acceptExpired.isSelected());
+ localPreferences.setAcceptNotValidYet(acceptNotValidYet.isSelected());
+ localPreferences.setAcceptSelfSigned(acceptSelfSigned.isSelected());
+ localPreferences.setAcceptRevoked(acceptRevoked.isSelected());
+ localPreferences.setAcceptAllCertificates(acceptAll.isSelected());
+ localPreferences.setCheckCRL(checkCRL.isSelected());
+ localPreferences.setCheckOCSP(checkOCSP.isSelected());
+ localPreferences.setAllowSoftFail(allowSoftFail.isSelected());
+ SettingsManager.saveSettings();
+ }
}
diff --git a/core/src/main/java/org/jivesoftware/spark/ui/login/LoginSettingDialog.java b/core/src/main/java/org/jivesoftware/spark/ui/login/LoginSettingDialog.java
index 7c735dd51..33ddb75af 100644
--- a/core/src/main/java/org/jivesoftware/spark/ui/login/LoginSettingDialog.java
+++ b/core/src/main/java/org/jivesoftware/spark/ui/login/LoginSettingDialog.java
@@ -148,6 +148,7 @@ public class LoginSettingDialog implements PropertyChangeListener
proxyPanel.saveSettings();
ssoPanel.saveSettings();
pkiPanel.saveSettings();
+ certManager.saveSettings();
SettingsManager.saveSettings();
optionsDialog.setVisible( false );
}
diff --git a/core/src/main/java/org/jivesoftware/spark/ui/login/SecurityLoginSettingsPanel.java b/core/src/main/java/org/jivesoftware/spark/ui/login/SecurityLoginSettingsPanel.java
index 53dfad841..c21fb787a 100644
--- a/core/src/main/java/org/jivesoftware/spark/ui/login/SecurityLoginSettingsPanel.java
+++ b/core/src/main/java/org/jivesoftware/spark/ui/login/SecurityLoginSettingsPanel.java
@@ -24,9 +24,6 @@ import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
import static java.awt.GridBagConstraints.*;
/**
@@ -49,7 +46,6 @@ public class SecurityLoginSettingsPanel extends JPanel
// Checkbox that toggles between 'old' style SSL (socket encryption, typically on port 5223), or STARTTLS. A check indicates 'old' behavior.
private JCheckBox useSSLBox;
- private JCheckBox acceptAllCertificatesBox;
private JCheckBox disableHostnameVerificationBox;
public SecurityLoginSettingsPanel( LocalPreferences localPreferences, JDialog optionsDialog )
@@ -73,7 +69,6 @@ public class SecurityLoginSettingsPanel extends JPanel
modeDisabledRadio.setToolTipText( Res.getString( "tooltip.encryptionmode.disabled" ) );
useSSLBox = new JCheckBox();
- acceptAllCertificatesBox = new JCheckBox();
disableHostnameVerificationBox = new JCheckBox();
// .. Set labels/text for all the components.
@@ -81,7 +76,6 @@ public class SecurityLoginSettingsPanel extends JPanel
ResourceUtils.resButton( modeIfPossibleRadio, Res.getString( "radio.encryptionmode.ifpossible" ) );
ResourceUtils.resButton( modeDisabledRadio, Res.getString( "radio.encryptionmode.disabled" ) );
ResourceUtils.resButton( useSSLBox, Res.getString( "label.old.ssl" ) );
- ResourceUtils.resButton( acceptAllCertificatesBox, Res.getString( "checkbox.accept.all.certificates" ) );
ResourceUtils.resButton( disableHostnameVerificationBox, Res.getString( "checkbox.disable.hostname.verification" ) );
// ... add the radio buttons to a group to make them interdependent.
@@ -94,7 +88,6 @@ public class SecurityLoginSettingsPanel extends JPanel
modeDisabledRadio.addChangeListener( e -> {
final boolean encryptionPossible = !modeDisabledRadio.isSelected();
useSSLBox.setEnabled( encryptionPossible );
- acceptAllCertificatesBox.setEnabled( encryptionPossible );
disableHostnameVerificationBox.setEnabled( encryptionPossible );
} );
@@ -103,7 +96,6 @@ public class SecurityLoginSettingsPanel extends JPanel
modeIfPossibleRadio.setSelected( localPreferences.getSecurityMode() == ConnectionConfiguration.SecurityMode.ifpossible );
modeDisabledRadio.setSelected( localPreferences.getSecurityMode() == ConnectionConfiguration.SecurityMode.disabled );
useSSLBox.setSelected( localPreferences.isSSL() );
- acceptAllCertificatesBox.setSelected( localPreferences.isAcceptAllCertificates() );
disableHostnameVerificationBox.setSelected( localPreferences.isDisableHostnameVerification() );
// ... place the components on the titled-border panel.
@@ -116,8 +108,7 @@ public class SecurityLoginSettingsPanel extends JPanel
add( encryptionModePanel, new GridBagConstraints( 0, 0, 1, 1, 1.0, 0.0, NORTHWEST, HORIZONTAL, DEFAULT_INSETS, 0, 0 ) );
// ... place the other components under the titled-border panel.
- add( acceptAllCertificatesBox, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, NORTHWEST, HORIZONTAL, DEFAULT_INSETS, 0, 0 ) );
- add( disableHostnameVerificationBox, new GridBagConstraints( 0, 2, 1, 1, 0.0, 1.0, NORTHWEST, HORIZONTAL, DEFAULT_INSETS, 0, 0 ) );
+ add( disableHostnameVerificationBox, new GridBagConstraints( 0, 1, 1, 1, 0.0, 1.0, NORTHWEST, HORIZONTAL, DEFAULT_INSETS, 0, 0 ) );
}
public boolean validate_settings()
@@ -140,7 +131,6 @@ public class SecurityLoginSettingsPanel extends JPanel
localPreferences.setSecurityMode( ConnectionConfiguration.SecurityMode.disabled );
}
localPreferences.setSSL( useSSLBox.isSelected() );
- localPreferences.setAcceptAllCertificates( acceptAllCertificatesBox.isSelected() );
localPreferences.setDisableHostnameVerification( disableHostnameVerificationBox.isSelected() );
SettingsManager.saveSettings();
}
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateController.java b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateController.java
index 77f568877..d63432342 100644
--- a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateController.java
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateController.java
@@ -25,6 +25,8 @@ import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;
+
+import org.jivesoftware.Spark;
import org.jivesoftware.resource.Res;
import org.jivesoftware.spark.ui.login.CertificateDialog;
import org.jivesoftware.spark.ui.login.CertificatesManagerSettingsPanel;
@@ -40,18 +42,17 @@ import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
*/
public class CertificateController {
- public final static File TRUSTED = new File(System.getProperty("user.dir") +"\\src\\main\\security\\truststore");
- public final static File BLACKLIST = new File(System.getProperty("user.dir") +"\\src\\main\\security\\blacklist");
- public final static File EXCEPTIONS = new File(System.getProperty("user.dir") +"\\src\\main\\security\\exceptions");
- public static String trustStorePath;
- private final static char[] passwd = "changeit".toCharArray();
+ public final static File TRUSTED = new File(Spark.getSparkUserHome() + File.separator + "security" + File.separator + "truststore");
+ public final static File BLACKLIST = new File(Spark.getSparkUserHome() + File.separator + "security" + File.separator + "blacklist");
+ public final static File EXCEPTIONS = new File(Spark.getSparkUserHome() + File.separator + "security" + File.separator + "exceptions");
+ public final static char[] passwd = "changeit".toCharArray();
private List certificates = new ArrayList<>(); // contain all certificates from all keystores
private List exemptedCertificates = new ArrayList<>(); // contain only certificates from exempted list
private List blackListedCertificates = new ArrayList<>(); //contain only revoked certificates
- private DefaultTableModel tableModel;
+ private static DefaultTableModel tableModel;
private Object[] certEntry;
private LocalPreferences localPreferences;
private static final String[] COLUMN_NAMES = { Res.getString("table.column.certificate.subject"),
@@ -64,7 +65,9 @@ public class CertificateController {
throw new IllegalArgumentException("localPreferences cannot be null");
}
this.localPreferences = localPreferences;
-
+ }
+
+ public void createCertTableModel(){
tableModel = new DefaultTableModel() {
// return adequate classes for columns so last column is Boolean
// displayed as checkbox
@@ -106,12 +109,10 @@ public class CertificateController {
}
}
}
-
-
/**
* If argument is true then move certificate to the exceptions Keystore, if false then move to the trusted Keystore.
* Useful for checkboxes where it's selected value indicates where certificate should be moved.
- * @param checked
+ * @param checked should it be moved?
*/
public void addOrRemoveFromExceptionList(boolean checked) {
int row = CertificatesManagerSettingsPanel.getCertTable().getSelectedRow();
@@ -135,18 +136,16 @@ public class CertificateController {
/**
* Return information if certificate is on exception list.
*
- * @param alias
+ * @param alias of the certificate
*/
public boolean isOnExceptionList(CertificateModel cert) {
- if(exemptedCertificates.contains(cert)){
- return true;
- }
- return false;
- }
-
+ return exemptedCertificates.contains(cert);
+ }
+
/**
- * Add certificates from keyStore to
- * @param storePath
+ * Add certificates from keyStore to
+ *
+ * @param storePath path of the store which will fill certificate table
*/
private void fillCertTableWithKeyStoreContent(File storePath) {
@@ -174,8 +173,8 @@ public class CertificateController {
/**
* Return file which contains certificate with given alias;
*
- * @param alias
- * @return File path
+ * @param alias of the certificate
+ * @return File path of KeyStore with certificate
*/
private File getAliasKeyStore(String alias) {
for (CertificateModel model : exemptedCertificates) {
@@ -199,7 +198,7 @@ public class CertificateController {
/**
* This method delete certificate with provided alias from the Truststore
*
- * @param alias
+ * @param alias Alias of the certificate to delete
* @throws KeyStoreException
* @throws IOException
* @throws NoSuchAlgorithmException
@@ -237,12 +236,16 @@ public class CertificateController {
}
+ public void moveCertificateToBlackList(String alias) throws FileNotFoundException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException{
+ moveCertificate(getAliasKeyStore(alias), BLACKLIST, alias);
+ }
+
/**
* This method transfer certificate from source KeyStore to target KeyStore.
*
- * @param source
- * @param target
- * @param alias
+ * @param source File with source KeyStore
+ * @param target File with target KeyStore
+ * @param alias Alias of the certificate meant to move
* @throws FileNotFoundException
* @throws IOException
* @throws KeyStoreException
@@ -286,7 +289,7 @@ public class CertificateController {
/**
* This method add certifiate from file ((*.cer), (*.crt), (*.der)) to Truststore.
*
- * @param file
+ * @param file File with certificate that is added
* @throws KeyStoreException
* @throws CertificateException
* @throws NoSuchAlgorithmException
@@ -330,8 +333,8 @@ public class CertificateController {
* This method also assure that it will not add second same alias to Truststore by adding number to alias.
* In case when common name cannot be extracted method will return "cert{number}".
*
- * @param cert
- * @return String Common Name
+ * @param cert Certificate which Common Name is meant to use
+ * @return String Common Name of the certificate
* @throws InvalidNameException
* @throws HeadlessException
* @throws KeyStoreException
@@ -366,7 +369,7 @@ public class CertificateController {
/**
* Check if there is certificate entry in Truststore with the same alias.
*
- * @param alias
+ * @param alias Alias of the certificate which is looked for in the model list
* @return True if KeyStore contain the same alias.
* @throws HeadlessException
* @throws KeyStoreException
@@ -383,7 +386,7 @@ public class CertificateController {
/**
* Check if this certificate already exist in Truststore.
*
- * @param alias
+ * @param alias Alias of the certificate for which it method look in the model list
* @return true if KeyStore already have this certificate.
* @throws KeyStoreException
*/
@@ -412,7 +415,7 @@ public class CertificateController {
/**
* Open dialog with certificate.
*
- * @param CertificateModel
+ * @param CertificateModel Model of the certificate which details are meant to be shown.
*/
public void showCertificate(CertificateModel certModel, CertificateDialogReason reason) {
CertificateDialog certDialog = new CertificateDialog(localPreferences, certModel, this, reason);
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateModel.java b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateModel.java
index 159861c39..4f0316c89 100644
--- a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateModel.java
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/CertificateModel.java
@@ -1,12 +1,10 @@
package org.jivesoftware.sparkimpl.certificates;
import java.io.IOException;
-import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Base64;
-import java.util.Calendar;
import java.util.Date;
import javax.naming.InvalidNameException;
@@ -27,10 +25,8 @@ import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralSubtree;
import org.bouncycastle.asn1.x509.NameConstraints;
import org.bouncycastle.asn1.x509.PolicyConstraints;
-import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.asn1.x509.SubjectDirectoryAttributes;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.util.encoders.Hex;
import org.jivesoftware.resource.Res;
@@ -140,7 +136,7 @@ public class CertificateModel {
String value = Res.getString("cert.is.critical") + critical + "\n";
boolean isSupported = true;
- if (oid.equals(Extension.subjectDirectoryAttributes)) {
+ if (oid.equals(Extension.subjectDirectoryAttributes.toString())) {
value += subjectDirectoryAttributesExtractor(primitive);
} else if (oid.equals(Extension.subjectKeyIdentifier.toString())) {
@@ -332,9 +328,6 @@ public class CertificateModel {
} else if (isBeforeNotBefore()) {
return "cert.not.valid.yet";
- } else if (isSelfSigned()) {
- return Res.getString("cert.self.signed");
-
} else {
return Res.getString("cert.valid");
}
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkExceptionsTrustManager.java b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkExceptionsTrustManager.java
new file mode 100644
index 000000000..061ec0b26
--- /dev/null
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkExceptionsTrustManager.java
@@ -0,0 +1,130 @@
+package org.jivesoftware.sparkimpl.certificates;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+
+import javax.net.ssl.X509TrustManager;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.jivesoftware.spark.util.log.Log;
+
+public class SparkExceptionsTrustManager implements X509TrustManager {
+
+ KeyStore exceptionsStore;
+ private Provider bcProvider = new BouncyCastleProvider(); // bc provider for path validation
+
+ public SparkExceptionsTrustManager() {
+ try (InputStream inputStream = new FileInputStream(CertificateController.EXCEPTIONS)) {
+ this.exceptionsStore = KeyStore.getInstance("JKS");
+ exceptionsStore.load(inputStream, CertificateController.passwd);
+ } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException e) {
+ Log.error("Couldn't load keystore for certificate exceptions authentication", e);
+ ;
+ }
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ // doesen't do any checks as there is it use only accepted issuers from exception list
+ try {
+ validatePath(chain);
+ } catch (NoSuchAlgorithmException | KeyStoreException | InvalidAlgorithmParameterException
+ | CertPathValidatorException | CertPathBuilderException e) {
+ Log.warning("Cannot build certificate chain", e);
+ throw new CertificateException("Cannot build certificate chain");
+ }
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ X509Certificate[] X509Certs = null;
+ try {
+ // See how many certificates are in the keystore.
+ int numberOfEntry = exceptionsStore.size();
+ // If there are any certificates in the keystore.
+ if (numberOfEntry > 0) {
+ // Create an array of X509Certificates
+ X509Certs = new X509Certificate[numberOfEntry];
+
+ // Get all of the certificate alias out of the keystore.
+ Enumeration aliases = exceptionsStore.aliases();
+
+ // Retrieve all of the certificates out of the keystore
+ // via the alias name.
+ int i = 0;
+ while (aliases.hasMoreElements()) {
+ X509Certs[i] = (X509Certificate) exceptionsStore.getCertificate((String) aliases.nextElement());
+ i++;
+ }
+
+ }
+ } catch (Exception e) {
+ Log.error(e.getMessage(), e);
+ X509Certs = null;
+ }
+ return X509Certs;
+ }
+
+ /**
+ * Validate certificate path. As it is exception, no checks against revocation or time validity are done but path
+ * still have to be validated in order to find connection between certificate presented by server and root CA in
+ * KeyStore
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CertPathValidatorException
+ * @throws CertPathBuilderException
+ * @throws CertificateException
+ */
+ private void validatePath(X509Certificate[] chain)
+ throws NoSuchAlgorithmException, KeyStoreException, InvalidAlgorithmParameterException,
+ CertPathValidatorException, CertPathBuilderException, CertificateException {
+
+ CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", bcProvider);
+ CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX");
+ X509CertSelector certSelector = new X509CertSelector();
+ certSelector.setCertificate(chain[chain.length - 1]);
+ // checks against time validity aren't done here as it exceptions list
+ certSelector.setCertificateValid(null);
+ PKIXBuilderParameters parameters = new PKIXBuilderParameters(exceptionsStore, certSelector);
+ // no checks against revocation as it is exception
+ parameters.setRevocationEnabled(false);
+
+ CertPathBuilderResult pathResult = certPathBuilder.build(parameters);
+ CertPath certPath = pathResult.getCertPath();
+ PKIXCertPathValidatorResult validationResult = (PKIXCertPathValidatorResult) certPathValidator
+ .validate(certPath, parameters);
+ X509Certificate trustedCert = validationResult.getTrustAnchor().getTrustedCert();
+
+ if (trustedCert == null) {
+ throw new CertificateException("Certificate path failed");
+ } else {
+ Log.debug("ClientTrustManager: Trusted CA: " + trustedCert.getSubjectDN());
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkSSLContext.java b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkSSLContext.java
new file mode 100644
index 000000000..844000207
--- /dev/null
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkSSLContext.java
@@ -0,0 +1,32 @@
+package org.jivesoftware.sparkimpl.certificates;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLContextSpi;
+
+public class SparkSSLContext extends SSLContext {
+
+ protected SparkSSLContext(SSLContextSpi contextSpi, Provider provider, String protocol) {
+ super(contextSpi, provider, protocol);
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * Create SSLContext and initialize it
+ *
+ * @return initialized SSL context with BouncyCastleProvider
+ * @throws KeyManagementException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ */
+ public static SSLContext setUpContext() throws KeyManagementException, NoSuchAlgorithmException {
+ SSLContext context = SparkSSLContext.getInstance("TLS");
+ context.init(null, SparkTrustManager.getTrustManagerList(), new SecureRandom());
+ return context;
+ }
+}
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkTrustManager.java b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkTrustManager.java
new file mode 100644
index 000000000..e17efbafd
--- /dev/null
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/certificates/SparkTrustManager.java
@@ -0,0 +1,408 @@
+package org.jivesoftware.sparkimpl.certificates;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CRLException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateRevokedException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.net.ssl.X509TrustManager;
+
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
+
+import org.jivesoftware.spark.util.log.Log;
+import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
+import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
+
+/**
+ * This trust manager wrap around SparkExceptionsTrustManager. In case when SparkExceptionsTrustManager fail then this
+ * TrustManager will provide certificates from TRUSTED KeyStore which are checked against data validity, revocation,
+ * acceptance of self-signed certificates and basic constraints.
+ *
+ *
+ * @author A
+ *
+ */
+public class SparkTrustManager implements X509TrustManager {
+
+ private LocalPreferences localPref = SettingsManager.getLocalPreferences();
+ private boolean checkCRL;
+ private boolean checkOCSP;
+ private boolean acceptExpired;
+ private boolean acceptNotValidYet;
+ private boolean acceptRevoked;
+ private boolean acceptSelfSigned;
+ private boolean allowSoftFail;
+
+ private CertStore crlStore;
+ private Collection crlCollection = new ArrayList<>();
+ private X509TrustManager exceptionsTrustManager;
+ private KeyStore trustStore;
+ private CertificateController certControll = new CertificateController(localPref);
+
+ public SparkTrustManager() {
+ exceptionsTrustManager = new SparkExceptionsTrustManager();
+
+ checkCRL = localPref.isCheckCRL();
+ checkOCSP = localPref.isCheckOCSP();
+ acceptExpired = localPref.isAcceptExpired();
+ acceptNotValidYet = localPref.isAcceptNotValidYet();
+ acceptRevoked = localPref.isAcceptRevoked();
+ acceptSelfSigned = localPref.isAcceptSelfSigned();
+ allowSoftFail = localPref.isAllowSoftFail();
+
+ loadTrustStore();
+ }
+
+ public static X509TrustManager[] getTrustManagerList() {
+ return new X509TrustManager[] { new SparkTrustManager() };
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ try {
+ // first check if certificate is accepted as as certificate from exceptions list, exceptionsTrustManager
+ // will make use of chain provided by exceptions KeyStore
+ exceptionsTrustManager.checkServerTrusted(chain, authType);
+ } catch (CertificateException ex) {
+ // in case when certificate isn't on exceptions list then make use of this Trust Manager
+
+ // validate chain by date (expired/not valid yet)
+ checkDateValidity(chain);
+
+ // check if certificate isn't self signed, self signed certificate still have to be in TrustStore to be
+ // accepted
+ if (isSelfSigned(chain) == false) {
+ // validate certificate path
+ try {
+ validatePath(chain);
+
+ } catch (NoSuchAlgorithmException | KeyStoreException | InvalidAlgorithmParameterException
+ | CertPathValidatorException | CertPathBuilderException e) {
+ Log.error("Validating path failed", e);
+ throw new CertificateException("Certificate path validation failed", e);
+
+ }
+ } else if (isSelfSigned(chain) && !acceptSelfSigned) {
+ // Self Signed certificate while it isn't accepted
+ throw new CertificateException("Self Signed certificate");
+
+ } else if (isSelfSigned(chain) && acceptSelfSigned) {
+ // check if certificate is in Keystore and check CRL, but do not validate path as certificate is Self
+ // Signed important reminder: hostname validation must be also turned off to accept self signed
+ // certificate
+ List certList = new ArrayList<>(Arrays.asList(getAcceptedIssuers()));
+ if (!certList.contains(chain[0])) {
+ throw new CertificateException("Certificate not in the TrustStore");
+ }
+ try {
+ loadCRL(chain);
+ for (X509CRL crl : crlCollection) {
+ if (crl.isRevoked(chain[0])) {
+ throw new CertificateException("Certificate is revoked");
+ }
+ }
+ } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | CertStoreException
+ | CRLException | IOException e) {
+ Log.warning("Couldn't load CRL");
+ }
+ }else {
+ throw new CertificateException("Certificate chain cannot be trusted");
+ }
+ }
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ X509Certificate[] X509Certs = null;
+ try {
+ // See how many certificates are in the keystore.
+ int numberOfEntry = trustStore.size();
+ // If there are any certificates in the keystore.
+ if (numberOfEntry > 0) {
+ // Create an array of X509Certificates
+ X509Certs = new X509Certificate[numberOfEntry];
+
+ // Get all of the certificate alias out of the keystore.
+ Enumeration aliases = trustStore.aliases();
+
+ // Retrieve all of the certificates out of the keystore
+ // via the alias name.
+ int i = 0;
+ while (aliases.hasMoreElements()) {
+ X509Certs[i] = (X509Certificate) trustStore.getCertificate((String) aliases.nextElement());
+ i++;
+ }
+
+ }
+ } catch (Exception e) {
+ Log.error(e.getMessage(), e);
+ X509Certs = null;
+ }
+ return X509Certs;
+ }
+
+ /**
+ * Return true if the certificate chain contain only one Self Signed certificate
+ */
+ private boolean isSelfSigned(X509Certificate[] chain) {
+ return chain[0].getIssuerX500Principal().getName().equals(chain[0].getSubjectX500Principal().getName())
+ && chain.length == 1;
+ }
+
+ /**
+ * Validate certificate path
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreException
+ * @throws InvalidAlgorithmParameterException
+ * @throws CertPathValidatorException
+ * @throws CertPathBuilderException
+ * @throws CertificateException
+ */
+ private void validatePath(X509Certificate[] chain)
+ throws NoSuchAlgorithmException, KeyStoreException, InvalidAlgorithmParameterException,
+ CertPathValidatorException, CertPathBuilderException, CertificateException {
+
+ // PKIX algorithm is defined in rfc3280
+ CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX");
+ CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX");
+
+ X509CertSelector certSelector = new X509CertSelector();
+
+ // set root CA certificate from chain for CertSelector so trust store must contain it
+ certSelector.setCertificate(chain[chain.length - 1]);
+
+ // checks against time validity aren't done here as are already done in checkDateValidity (X509Certificate[]
+ // chain)
+ certSelector.setCertificateValid(null);
+ // create parameters using trustStore as source of Trust Anchors and using X509CertSelector
+ PKIXBuilderParameters parameters = new PKIXBuilderParameters(trustStore, certSelector);
+
+ if (acceptRevoked == false) {
+ // check OCSP, CRL serve as backup
+ if (checkOCSP && checkCRL) {
+ // OCSP checking is done according to Java PKI Programmer's Guide, PKIXRevocationChecker was added in Java 8:
+ // https://docs.oracle.com/javase/8/docs/technotes/guides/security/certpath/CertPathProgGuide.html#PKIXRevocationChecker
+ PKIXRevocationChecker checker = (PKIXRevocationChecker) certPathBuilder.getRevocationChecker();
+ // soft fail option mean that OCSP or CRL must pass validation, in case when OCSP cannot be
+ // validated it will validate CRL and then if both cannot be checked it will fail
+ if (allowSoftFail) {
+ checker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.SOFT_FAIL));
+ }
+ parameters.addCertPathChecker(checker);
+ // will use PKIXRevocationChecker instead of the default revocation checker
+ parameters.setRevocationEnabled(false);
+ } else if (!checkOCSP && checkCRL) {
+ // check only CRL, need custom implementation
+ // add CRL store and download CRL list to this store.
+ try {
+ loadCRL(chain);
+ parameters.addCertStore(crlStore);
+ parameters.setRevocationEnabled(true);
+
+ } catch (CertStoreException | IOException | CRLException e) {
+ Log.error("Couldn't load crl", e);
+ throw new CertificateException();
+ }
+
+ } else {
+ //revocation checks disabled
+ parameters.setRevocationEnabled(false);
+ }
+
+ } else {
+ parameters.setRevocationEnabled(false);
+ }
+ try {
+ CertPathBuilderResult pathResult = certPathBuilder.build(parameters);
+ CertPath certPath = pathResult.getCertPath();
+
+ PKIXCertPathValidatorResult validationResult = (PKIXCertPathValidatorResult) certPathValidator
+ .validate(certPath, parameters);
+ X509Certificate trustedCert = validationResult.getTrustAnchor().getTrustedCert();
+
+ if (trustedCert == null) {
+ throw new CertificateException("certificate path failed: Trusted CA is NULL");
+ }
+ // check if all certificates in path have Basic Constraints, only certificate that isn't required to have
+ // this extension is last certificate: root CA
+ for (int i = 0; i < chain.length - 1; i++) {
+ checkBasicConstraints(chain[i]);
+ }
+ } catch (CertificateRevokedException e) {
+ Log.warning("Certificate was revoked", e);
+ // moveToBlackList(cert);
+ throw new CertificateException("Certificate was revoked");
+ }
+ }
+
+ /**
+ * check time date validity of certificates
+ *
+ * @throws CertificateException
+ */
+ private void checkDateValidity(X509Certificate[] chain) throws CertificateException {
+
+ for (X509Certificate cert : chain) {
+ // expiration check
+ try {
+ cert.checkValidity();
+ } catch (CertificateExpiredException e) {
+ Log.warning("Certificate is expired " + cert.getSubjectX500Principal().getName().toString(), e);
+ if (acceptExpired == false) {
+ throw new CertificateException("Certificate is expired");
+ }
+ } catch (CertificateNotYetValidException e) {
+ Log.warning("Certificate is not valid yet " + cert.getSubjectX500Principal().getName().toString(), e);
+ if (acceptNotValidYet == false) {
+ throw new CertificateException("Certificate is not valid yet");
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Check if certificate have basic constraints exception.
+ *
+ * @param chain
+ * - with certificates given by server
+ * @throws CertificateException
+ */
+ private void checkBasicConstraints(X509Certificate cert) throws CertificateException {
+ if (cert.getBasicConstraints() != -1) {
+ throw new CertificateException("Certificate have no basic constraints");
+ }
+ }
+
+ /**
+ * loads truststore
+ */
+ private void loadTrustStore() {
+ try (FileInputStream inputStream = new FileInputStream(CertificateController.TRUSTED)) {
+ trustStore = KeyStore.getInstance("JKS");
+ trustStore.load(inputStream, CertificateController.passwd);
+ } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException e) {
+ Log.error("Error at accesing truststore", e);
+ }
+ }
+
+ private void loadCRL(X509Certificate[] chain) throws IOException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, CertStoreException, CRLException, CertificateException {
+
+ // for each certificate in chain
+ for (X509Certificate cert : chain) {
+ if (cert.getExtensionValue(Extension.cRLDistributionPoints.getId()) != null) {
+ ASN1Primitive primitive = JcaX509ExtensionUtils
+ .parseExtensionValue(cert.getExtensionValue(Extension.cRLDistributionPoints.getId()));
+ // extract distribution point extension
+ CRLDistPoint distPoint = CRLDistPoint.getInstance(primitive);
+ DistributionPoint[] dp = distPoint.getDistributionPoints();
+ // each distribution point extension can hold number of distribution points
+ for (DistributionPoint d : dp) {
+ DistributionPointName dpName = d.getDistributionPoint();
+ // Look for URIs in fullName
+ if (dpName != null && dpName.getType() == DistributionPointName.FULL_NAME) {
+ GeneralName[] genNames = GeneralNames.getInstance(dpName.getName()).getNames();
+ // Look for an URI
+ for (GeneralName genName : genNames) {
+ // extract url
+ URL url = new URL(genName.getName().toString());
+ try {
+ // download from Internet to the collection
+ crlCollection.add(downloadCRL(url));
+ } catch (CertificateException | CRLException e) {
+ throw new CRLException("Couldn't download CRL");
+ }
+ }
+ }
+ }
+ } else {
+ Log.warning("Certificate " + cert.getSubjectX500Principal().getName().toString() + " have no CRLs");
+ }
+ // parameters for cert store is collection type, using collection with crl create parameters
+ CollectionCertStoreParameters params = new CollectionCertStoreParameters(crlCollection);
+ // this parameters are next used for creation of certificate store with crls
+ crlStore = CertStore.getInstance("Collection", params);
+ }
+ }
+
+ /**
+ * Move certificate to the blacklist of the revoked certificates.
+ *
+ * @param cert
+ * - certificate which is meant to move into blacklist
+ * @throws FileNotFoundException
+ * @throws KeyStoreException
+ * @throws NoSuchAlgorithmException
+ * @throws CertificateException
+ * @throws IOException
+ */
+ private void moveToBlackList(X509Certificate cert) throws FileNotFoundException, KeyStoreException,
+ NoSuchAlgorithmException, CertificateException, IOException {
+ certControll.moveCertificateToBlackList(trustStore.getCertificateAlias(cert));
+ }
+
+ /**
+ * Downloads a CRL from given URL
+ *
+ * @param url
+ * - the web address with given CRL
+ * @throws IOException
+ * @throws CertificateException
+ * @throws CRLException
+ */
+ private X509CRL downloadCRL(URL url) throws IOException, CertificateException, CRLException {
+
+ try (InputStream crlStream = url.openStream()) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509CRL) cf.generateCRL(crlStream);
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/jivesoftware/sparkimpl/settings/local/LocalPreferences.java b/core/src/main/java/org/jivesoftware/sparkimpl/settings/local/LocalPreferences.java
index e437e9d44..c53b936af 100644
--- a/core/src/main/java/org/jivesoftware/sparkimpl/settings/local/LocalPreferences.java
+++ b/core/src/main/java/org/jivesoftware/sparkimpl/settings/local/LocalPreferences.java
@@ -1420,5 +1420,61 @@ public class LocalPreferences {
public void setPswdAutologin(boolean ccPswdAutologin) {
props.setProperty("ccPswdAutologin", Boolean.toString(ccPswdAutologin));
}
+
+ public boolean isAcceptSelfSigned() {
+ return Boolean.parseBoolean(props.getProperty("acceptSelfSigned", "false"));
+ }
+ public void setAcceptSelfSigned(boolean acceptSelfSigned) {
+ props.setProperty("acceptSelfSigned", Boolean.toString(acceptSelfSigned));
+ }
+
+ public boolean isAcceptRevoked() {
+ return Boolean.parseBoolean(props.getProperty("acceptRevoked", "false"));
+ }
+
+ public void setAcceptRevoked(boolean acceptRevoked) {
+ props.setProperty("acceptRevoked", Boolean.toString(acceptRevoked));
+ }
+
+ public boolean isAcceptExpired() {
+ return Boolean.parseBoolean(props.getProperty("acceptExpired", "false"));
+ }
+
+ public void setAcceptExpired(boolean acceptExpired) {
+ props.setProperty("acceptExpired", Boolean.toString(acceptExpired));
+ }
+
+ public boolean isAcceptNotValidYet() {
+ return Boolean.parseBoolean(props.getProperty("acceptNotValidYet", "false"));
+ }
+
+ public void setAcceptNotValidYet(boolean acceptNotValidYet) {
+ props.setProperty("acceptNotValidYet", Boolean.toString(acceptNotValidYet));
+ }
+
+ public boolean isCheckCRL() {
+ return Boolean.parseBoolean(props.getProperty("checkCRL", "true"));
+ }
+
+ public void setCheckCRL(boolean checkCRL) {
+ props.setProperty("checkCRL", Boolean.toString(checkCRL));
+ }
+
+ public boolean isCheckOCSP() {
+ return Boolean.parseBoolean(props.getProperty("checkOCSP", "true"));
+ }
+
+ public void setCheckOCSP(boolean checkOCSP) {
+ props.setProperty("checkOCSP", Boolean.toString(checkOCSP));
+ }
+ public boolean isAllowSoftFail() {
+ return Boolean.parseBoolean(props.getProperty("allowSoftFail", "true"));
+ }
+
+ public void setAllowSoftFail(boolean allowSoftFail) {
+ props.setProperty("allowSoftFail", Boolean.toString(allowSoftFail));
+ }
+
+
}
diff --git a/core/src/main/resources/i18n/spark_i18n.properties b/core/src/main/resources/i18n/spark_i18n.properties
index 2d8a023e9..a204ac145 100644
--- a/core/src/main/resources/i18n/spark_i18n.properties
+++ b/core/src/main/resources/i18n/spark_i18n.properties
@@ -393,10 +393,12 @@ checkbox.accept.all.certificates = Accept all certificates (self-signed/expired/
checkbox.disable.hostname.verification = Disable certificate hostname verification (not recommended)
checkbox.accept.all = Accept all
checkbox.accept.expired = Accept expired
+checkbox.accept.not.valid.yet = Accept not valid yet
checkbox.accept.self.signed = Accept self-signed
-checkbox.accept.invalid = Accept invalid
+checkbox.accept.revoked = Accept revoked
checkbox.check.crl = Check CRL
checkbox.check.ocsp = Check OCSP
+checkbox.allow.soft.fail = Allow soft fail policy
checkbox.on.exception.list = On the exception list
radio.encryptionmode.required = Required
radio.encryptionmode.ifpossible = If possible
diff --git a/core/src/main/security/exceptions b/core/src/main/security/exceptions
index c40846550..f314364fc 100644
Binary files a/core/src/main/security/exceptions and b/core/src/main/security/exceptions differ
diff --git a/core/src/main/security/truststore b/core/src/main/security/truststore
index 1480d6cb8..f4de542f8 100644
Binary files a/core/src/main/security/truststore and b/core/src/main/security/truststore differ