diff --git a/src/java/davmail/Settings.java b/src/java/davmail/Settings.java index f6083778..bf30a34b 100644 --- a/src/java/davmail/Settings.java +++ b/src/java/davmail/Settings.java @@ -128,6 +128,9 @@ public final class Settings { SETTINGS.put("davmail.ssl.keystoreFile", ""); SETTINGS.put("davmail.ssl.keystorePass", ""); SETTINGS.put("davmail.ssl.keyPass", ""); + SETTINGS.put("davmail.ssl.clientKeystoreType", ""); + SETTINGS.put("davmail.ssl.clientKeystoreFile", ""); + SETTINGS.put("davmail.ssl.clientKeystorePass", ""); SETTINGS.put("davmail.ssl.pkcs11Library", ""); SETTINGS.put("davmail.ssl.pkcs11Config", ""); diff --git a/src/java/davmail/http/DavGatewaySSLProtocolSocketFactory.java b/src/java/davmail/http/DavGatewaySSLProtocolSocketFactory.java index 5441f1e4..b5f7bc7e 100644 --- a/src/java/davmail/http/DavGatewaySSLProtocolSocketFactory.java +++ b/src/java/davmail/http/DavGatewaySSLProtocolSocketFactory.java @@ -33,12 +33,14 @@ import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; +import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.security.*; +import java.util.ArrayList; /** * Manual Socket Factory. @@ -66,12 +68,36 @@ public class DavGatewaySSLProtocolSocketFactory implements SecureProtocolSocketF } } + private KeyStore.ProtectionParameter getProtectionParameter(String password) { + if (password != null && password.length() > 0) { + // password provided: create a PasswordProtection + return new KeyStore.PasswordProtection(password.toCharArray()); + } else { + // request password at runtime through a callback + return new KeyStore.CallbackHandlerProtection(new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + if (callbacks.length > 0 && callbacks[0] instanceof PasswordCallback) { + PasswordPromptDialog passwordPromptDialog = new PasswordPromptDialog(((PasswordCallback) callbacks[0]).getPrompt()); + ((PasswordCallback) callbacks[0]).setPassword(passwordPromptDialog.getPassword()); + } + } + }); + } + } + private SSLContext sslcontext; private SSLContext createSSLContext() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, KeyManagementException, KeyStoreException { // PKCS11 client certificate settings String pkcs11Library = Settings.getProperty("davmail.ssl.pkcs11Library"); - if (pkcs11Library != null && pkcs11Library.length() > 0) { + + String clientKeystoreType = Settings.getProperty("davmail.ssl.clientKeystoreType"); + // set default keystore type + if (clientKeystoreType == null || clientKeystoreType.length() == 0) { + clientKeystoreType = "PKCS11"; + } + + if (pkcs11Library != null && pkcs11Library.length() > 0 && "PKCS11".equals(clientKeystoreType)) { StringBuilder pkcs11Buffer = new StringBuilder(); pkcs11Buffer.append("name=DavMail\n"); pkcs11Buffer.append("library=").append(pkcs11Library).append('\n'); @@ -83,16 +109,23 @@ public class DavGatewaySSLProtocolSocketFactory implements SecureProtocolSocketF } KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(/*KeyManagerFactory.getDefaultAlgorithm()*/"NewSunX509"); - KeyStore.Builder scBuilder = KeyStore.Builder.newInstance("PKCS11", null, - new KeyStore.CallbackHandlerProtection(new CallbackHandler() { - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - if (callbacks.length > 0 && callbacks[0] instanceof PasswordCallback) { - PasswordPromptDialog passwordPromptDialog = new PasswordPromptDialog(((PasswordCallback) callbacks[0]).getPrompt()); - ((PasswordCallback) callbacks[0]).setPassword(passwordPromptDialog.getPassword()); - } - } - })); - ManagerFactoryParameters keyStoreBuilderParameters = new KeyStoreBuilderParameters(scBuilder); + + ArrayList keyStoreBuilders = new ArrayList(); + // PKCS11 (smartcard) keystore with password callback + KeyStore.Builder scBuilder = KeyStore.Builder.newInstance("PKCS11", null, getProtectionParameter(null)); + keyStoreBuilders.add(scBuilder); + + String clientKeystoreFile = Settings.getProperty("davmail.ssl.clientKeystoreFile"); + String clientKeystorePass = Settings.getProperty("davmail.ssl.clientKeystorePass"); + if (clientKeystoreFile != null && clientKeystoreFile.length() > 0 + && ("PKCS12".equals(clientKeystoreType) || "JKS".equals(clientKeystoreType))) { + // PKCS12 file based keystore + KeyStore.Builder fsBuilder = KeyStore.Builder.newInstance(clientKeystoreType, null, + new File(clientKeystoreFile), getProtectionParameter(clientKeystorePass)); + keyStoreBuilders.add(fsBuilder); + } + + ManagerFactoryParameters keyStoreBuilderParameters = new KeyStoreBuilderParameters(keyStoreBuilders); keyManagerFactory.init(keyStoreBuilderParameters); SSLContext context = SSLContext.getInstance("SSL"); @@ -100,7 +133,7 @@ public class DavGatewaySSLProtocolSocketFactory implements SecureProtocolSocketF return context; } - private SSLContext getSSLContext() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, InvalidAlgorithmParameterException { + private SSLContext getSSLContext() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, InvalidAlgorithmParameterException { if (this.sslcontext == null) { this.sslcontext = createSSLContext(); } diff --git a/src/java/davmail/ui/SettingsFrame.java b/src/java/davmail/ui/SettingsFrame.java index e39d2bdb..8324481b 100644 --- a/src/java/davmail/ui/SettingsFrame.java +++ b/src/java/davmail/ui/SettingsFrame.java @@ -29,6 +29,8 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; /** * DavMail settings frame @@ -67,6 +69,9 @@ public class SettingsFrame extends JFrame { JPasswordField keystorePassField; JPasswordField keyPassField; + JComboBox clientKeystoreTypeCombo; + JTextField clientKeystoreFileField; + JPasswordField clientKeystorePassField; JTextField pkcs11LibraryField; JTextArea pkcs11ConfigField; @@ -257,20 +262,53 @@ public class SettingsFrame extends JFrame { } protected JPanel getSmartCardPanel() { - JPanel smartCardPanel = new JPanel(new GridLayout(2, 2)); - smartCardPanel.setBorder(BorderFactory.createTitledBorder(BundleMessage.format("UI_CLIENT_CERTIFICATE"))); + JPanel clientKeystorePanel = new JPanel(new GridLayout(2, 1)); + clientKeystorePanel.setLayout(new BoxLayout(clientKeystorePanel, BoxLayout.Y_AXIS)); + clientKeystorePanel.setBorder(BorderFactory.createTitledBorder(BundleMessage.format("UI_CLIENT_CERTIFICATE"))); + + clientKeystoreTypeCombo = new JComboBox(new String[]{"PKCS11", "JKS", "PKCS12"}); + clientKeystoreTypeCombo.setSelectedItem(Settings.getProperty("davmail.ssl.clientKeystoreType")); + clientKeystoreFileField = new JTextField(Settings.getProperty("davmail.ssl.clientKeystoreFile"), 17); + clientKeystorePassField = new JPasswordField(Settings.getProperty("davmail.ssl.clientKeystorePass"), 15); pkcs11LibraryField = new JTextField(Settings.getProperty("davmail.ssl.pkcs11Library"), 17); pkcs11ConfigField = new JTextArea(2, 17); + pkcs11ConfigField.setText(Settings.getProperty("davmail.ssl.pkcs11Config")); pkcs11ConfigField.setBorder(pkcs11LibraryField.getBorder()); pkcs11ConfigField.setFont(pkcs11LibraryField.getFont()); - addSettingComponent(smartCardPanel, BundleMessage.format("UI_PKCS11_LIBRARY"), pkcs11LibraryField, - BundleMessage.format("UI_PKCS11_LIBRARY_HELP")); - addSettingComponent(smartCardPanel, BundleMessage.format("UI_PKCS11_CONFIG"), pkcs11ConfigField, - BundleMessage.format("UI_PKCS11_CONFIG_HELP")); + JPanel clientKeystoreTypePanel = new JPanel(new GridLayout(1, 2)); + addSettingComponent(clientKeystoreTypePanel, BundleMessage.format("UI_CLIENT_KEY_STORE_TYPE"), clientKeystoreTypeCombo, + BundleMessage.format("UI_CLIENT_KEY_STORE_TYPE_HELP")); + clientKeystorePanel.add(clientKeystoreTypePanel); - return smartCardPanel; + final JPanel cardPanel = new JPanel(new CardLayout()); + clientKeystorePanel.add(cardPanel); + + JPanel clientKeystoreFilePanel = new JPanel(new GridLayout(2, 2)); + addSettingComponent(clientKeystoreFilePanel, BundleMessage.format("UI_CLIENT_KEY_STORE"), clientKeystoreFileField, + BundleMessage.format("UI_CLIENT_KEY_STORE_HELP")); + addSettingComponent(clientKeystoreFilePanel, BundleMessage.format("UI_CLIENT_KEY_STORE_PASSWORD"), clientKeystorePassField, + BundleMessage.format("UI_CLIENT_KEY_STORE_PASSWORD_HELP")); + cardPanel.add(clientKeystoreFilePanel, "PKCS12"); + cardPanel.add(clientKeystoreFilePanel, "JKS"); + + JPanel pkcs11Panel = new JPanel(new GridLayout(2, 2)); + addSettingComponent(pkcs11Panel, BundleMessage.format("UI_PKCS11_LIBRARY"), pkcs11LibraryField, + BundleMessage.format("UI_PKCS11_LIBRARY_HELP")); + addSettingComponent(pkcs11Panel, BundleMessage.format("UI_PKCS11_CONFIG"), pkcs11ConfigField, + BundleMessage.format("UI_PKCS11_CONFIG_HELP")); + cardPanel.add(pkcs11Panel, "PKCS11"); + + ((CardLayout)cardPanel.getLayout()).show(cardPanel, (String) clientKeystoreTypeCombo.getSelectedItem()); + + clientKeystoreTypeCombo.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent event) { + CardLayout cardLayout = (CardLayout) (cardPanel.getLayout()); + cardLayout.show(cardPanel, (String) event.getItem()); + } + }); + return clientKeystorePanel; } protected JPanel getNetworkSettingsPanel() { @@ -372,6 +410,7 @@ public class SettingsFrame extends JFrame { keystorePassField.setText(Settings.getProperty("davmail.ssl.keystorePass")); keyPassField.setText(Settings.getProperty("davmail.ssl.keyPass")); + clientKeystoreTypeCombo.setSelectedItem(Settings.getProperty("davmail.ssl.clientKeystoreType")); pkcs11LibraryField.setText(Settings.getProperty("davmail.ssl.pkcs11Library")); pkcs11ConfigField.setText(Settings.getProperty("davmail.ssl.pkcs11Config")); @@ -463,6 +502,9 @@ public class SettingsFrame extends JFrame { Settings.setProperty("davmail.ssl.keystorePass", String.valueOf(keystorePassField.getPassword())); Settings.setProperty("davmail.ssl.keyPass", String.valueOf(keyPassField.getPassword())); + Settings.setProperty("davmail.ssl.clientKeystoreType", (String) clientKeystoreTypeCombo.getSelectedItem()); + Settings.setProperty("davmail.ssl.clientKeystoreFile", clientKeystoreFileField.getText()); + Settings.setProperty("davmail.ssl.clientKeystorePass", String.valueOf(clientKeystorePassField.getPassword())); Settings.setProperty("davmail.ssl.pkcs11Library", pkcs11LibraryField.getText()); Settings.setProperty("davmail.ssl.pkcs11Config", pkcs11ConfigField.getText()); diff --git a/src/java/davmailmessages.properties b/src/java/davmailmessages.properties index 9676e00f..8def190f 100644 --- a/src/java/davmailmessages.properties +++ b/src/java/davmailmessages.properties @@ -180,10 +180,16 @@ UI_KEY_STORE_PASSWORD=Key store password: UI_KEY_STORE_PASSWORD_HELP=Key store password UI_KEY_STORE_TYPE=Key store type: UI_KEY_STORE_TYPE_HELP=Choose key store type +UI_CLIENT_KEY_STORE=Client key store: +UI_CLIENT_KEY_STORE_HELP=SSL client certificate key store file path +UI_CLIENT_KEY_STORE_PASSWORD=Client key store password: +UI_CLIENT_KEY_STORE_PASSWORD_HELP=Client key store password, leave empty for runtime prompt +UI_CLIENT_KEY_STORE_TYPE=Client key store type: +UI_CLIENT_KEY_STORE_TYPE_HELP=Choose client certificate key store type, choose PKCS11 for smartcard UI_CLIENT_CERTIFICATE=Client Certificate -UI_PKCS11_LIBRARY=PKCS11 library +UI_PKCS11_LIBRARY=PKCS11 library: UI_PKCS11_LIBRARY_HELP=PKCS11 (smartcard) library path (.so or .dll) -UI_PKCS11_CONFIG=PKCS11 config +UI_PKCS11_CONFIG=PKCS11 config: UI_PKCS11_CONFIG_HELP=Optional additional PKCS11 settings (slot, nssArgs, ...) UI_LAST_LOG=Last log UI_LAST_MESSAGE=Last message diff --git a/src/java/davmailmessages_fr.properties b/src/java/davmailmessages_fr.properties index da52f0ad..deabc6df 100644 --- a/src/java/davmailmessages_fr.properties +++ b/src/java/davmailmessages_fr.properties @@ -213,9 +213,9 @@ UI_VALID_FROM=Emis le UI_VALID_UNTIL=Expire le UI_PASSWORD_PROMPT=DavMail : Entrer le mot de passe UI_PKCS11_LIBRARY_HELP=Chemin de la librarie PKCS11 (carte à puce) (.so or .dll) -UI_PKCS11_LIBRARY=Librairie PKCS11 +UI_PKCS11_LIBRARY=Librairie PKCS11 : UI_PKCS11_CONFIG_HELP=Configuration PKCS11 complémentaire optionnelle (slot, nssArgs, ...) -UI_PKCS11_CONFIG=Configuration PKCS11 +UI_PKCS11_CONFIG=Configuration PKCS11 : UI_CLIENT_CERTIFICATE=Certificat client UI_LOG_FILE_PATH=Chemin du fichier de traces : LOG_GATEWAY_INTERRUPTED=Arrêt de la passerelle DavMail en cours @@ -231,4 +231,10 @@ LOG_FOLDER_ACCESS_FORBIDDEN=Acc LOG_FOLDER_NOT_FOUND=Dossier {0} introuvable LOG_FOLDER_ACCESS_ERROR=Erreur lors de l''accès au dossier {0} : {1} UI_OTP_PASSWORD_PROMPT=Mot de passe du jeton : -EXCEPTION_SESSION_EXPIRED=Session Exchange expirée \ No newline at end of file +EXCEPTION_SESSION_EXPIRED=Session Exchange expirée +UI_CLIENT_KEY_STORE_TYPE=Type de stockage : +UI_CLIENT_KEY_STORE_TYPE_HELP=Choisir le type de stockage du certificat client, PKCS11 pour une carte à puce +UI_CLIENT_KEY_STORE=Fichier certificat client : +UI_CLIENT_KEY_STORE_HELP=Chemin du fichier contenant le certificat client SSL +UI_CLIENT_KEY_STORE_PASSWORD=Mot de passe certificat client : +UI_CLIENT_KEY_STORE_PASSWORD_HELP=Mot de passe du certificat client, laisser vide pour fournir le mot de passe mode interactif \ No newline at end of file