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