You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2017/02/08 15:05:39 UTC

svn commit: r1782199 - in /chemistry/opencmis/trunk: chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/ chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemi...

Author: fmui
Date: Wed Feb  8 15:05:38 2017
New Revision: 1782199

URL: http://svn.apache.org/viewvc?rev=1782199&view=rev
Log:
added support for client certificate authentication

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java   (with props)
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java
    chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java
    chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/BasicLoginTab.java
    chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/DiscoverLoginTab.java
    chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/model/ClientSession.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java?rev=1782199&r1=1782198&r2=1782199&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-api/src/main/java/org/apache/chemistry/opencmis/client/SessionParameterMap.java Wed Feb  8 15:05:38 2017
@@ -399,6 +399,35 @@ public class SessionParameterMap extends
     }
 
     /**
+     * Turns Client Certificate authentication on and and basic authentication
+     * and UsernameToken authentication off.
+     * 
+     * @param keyfilePath
+     *            the path to the JKS key file
+     * @param passphrase
+     *            the pass phrase for the key file
+     */
+    public void setCertificateAuthentication(String keyfilePath, String passphrase) {
+        if (keyfilePath == null) {
+            throw new IllegalArgumentException("Key file path mut be set!");
+        }
+
+        put(SessionParameter.AUTH_HTTP_BASIC, false);
+        put(SessionParameter.AUTH_SOAP_USERNAMETOKEN, false);
+        put(SessionParameter.AUTH_OAUTH_BEARER, false);
+
+        put(SessionParameter.CLIENT_CERT_KEYFILE, keyfilePath);
+        if (passphrase == null) {
+            remove(SessionParameter.CLIENT_CERT_PASSPHRASE);
+        } else {
+            put(SessionParameter.CLIENT_CERT_PASSPHRASE, passphrase);
+        }
+
+        put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS,
+                "org.apache.chemistry.opencmis.client.bindings.spi.ClientCertificateAuthenticationProvider");
+    }
+
+    /**
      * Turns simple OAuth 2.0 bearer token authentication on and basic
      * authentication and UsernameToken authentication off.
      * <p>

Added: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java?rev=1782199&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java Wed Feb  8 15:05:38 2017
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.chemistry.opencmis.client.bindings.spi;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.impl.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Client Certificate Authentication Provider.
+ * 
+ * Enables the use of SSL client certificates for authentication. It requires
+ * the path to a JKS key file and its pass phrase.
+ * 
+ * <pre>
+ * {@code
+ * SessionFactory factory = ...
+ * 
+ * Map<String, String> parameter = new HashMap<String, String>();
+ * 
+ * parameter.put(SessionParameter.ATOMPUB_URL, "https://localhost/cmis/atom");
+ * parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
+ * parameter.put(SessionParameter.REPOSITORY_ID, "myRepository");
+ * 
+ * parameter.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS, "org.apache.chemistry.opencmis.client.bindings.spi.ClientCertificateAuthenticationProvider");
+ * 
+ * parameter.put(SessionParameter.CLIENT_CERT_KEYFILE, "/path/to/mycert.jks");
+ * parameter.put(SessionParameter.CLIENT_CERT_PASSPHRASE, "changeme");
+ * 
+ * ...
+ * Session session = factory.createSession(parameter);
+ * }
+ * </pre>
+ * 
+ */
+public class ClientCertificateAuthenticationProvider extends StandardAuthenticationProvider {
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(ClientCertificateAuthenticationProvider.class);
+
+    private SSLSocketFactory socketFactory;
+
+    @Override
+    public void setSession(BindingSession session) {
+        super.setSession(session);
+
+        if (socketFactory == null) {
+            Object keyfile = getSession().get(SessionParameter.CLIENT_CERT_KEYFILE);
+            if (keyfile instanceof String) {
+                Object passphrase = getSession().get(SessionParameter.CLIENT_CERT_PASSPHRASE);
+
+                String keyfileStr = ((String) keyfile).trim();
+                String passphraseStr = passphrase instanceof String ? ((String) passphrase).trim() : null;
+
+                socketFactory = createSSLSocketFactory(keyfileStr, passphraseStr);
+            }
+        }
+    }
+
+    @Override
+    public SSLSocketFactory getSSLSocketFactory() {
+        return socketFactory;
+    }
+
+    protected SSLSocketFactory createSSLSocketFactory(String keyFile, String passphrase) {
+        assert keyFile != null;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Using key file '{}'", keyFile);
+        }
+
+        try {
+            char[] passphraseChars = passphrase == null ? null : passphrase.toCharArray();
+
+            KeyStore keyStore;
+
+            String ext = getExtension(keyFile);
+            if ("p12".equals(ext) || "pfx".equals(ext)) {
+                keyStore = KeyStore.getInstance("PKCS12");
+            } else {
+                keyStore = KeyStore.getInstance("JKS");
+            }
+
+            // read key store
+            InputStream keyStream = null;
+            try {
+                keyStream = new BufferedInputStream(new FileInputStream(keyFile));
+                keyStore.load(keyStream, passphraseChars);
+            } finally {
+                IOUtils.closeQuietly(keyStream);
+            }
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Key store type: {}", keyStore.getType());
+
+                StringBuilder sb = new StringBuilder();
+                Enumeration<String> aliases = keyStore.aliases();
+                while (aliases.hasMoreElements()) {
+                    if (sb.length() > 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(aliases.nextElement());
+                }
+
+                LOG.debug("Aliases in key store: {}", sb.toString());
+            }
+
+            // create socket factory
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
+            keyManagerFactory.init(keyStore, passphraseChars);
+
+            SSLContext context = SSLContext.getInstance("TLS");
+            context.init(keyManagerFactory.getKeyManagers(), null, null);
+
+            return context.getSocketFactory();
+        } catch (FileNotFoundException fnfe) {
+            throw new CmisRuntimeException("Key file '" + keyFile + "' not found!", fnfe);
+        } catch (Exception e) {
+            throw new CmisRuntimeException("Cannot set up client certificate: " + e.toString(), e);
+        }
+    }
+
+    private String getExtension(String filename) {
+        int x = filename.lastIndexOf('.');
+        if (x > -1) {
+            return filename.substring(x + 1).toLowerCase(Locale.ENGLISH);
+        }
+
+        return null;
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-client/chemistry-opencmis-client-bindings/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/ClientCertificateAuthenticationProvider.java
------------------------------------------------------------------------------
    svn:eol-style = LF

Modified: chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java?rev=1782199&r1=1782198&r2=1782199&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-commons/chemistry-opencmis-commons-api/src/main/java/org/apache/chemistry/opencmis/commons/SessionParameter.java Wed Feb  8 15:05:38 2017
@@ -705,6 +705,11 @@ public final class SessionParameter {
     public static final String OAUTH_EXPIRATION_TIMESTAMP = "org.apache.chemistry.opencmis.oauth.expirationTimestamp";
     public static final String OAUTH_DEFAULT_TOKEN_LIFETIME = "org.apache.chemistry.opencmis.oauth.defaultTokenLifetime";
 
+    // --- client certificates ---
+
+    public static final String CLIENT_CERT_KEYFILE = "org.apache.chemistry.opencmis.clientcerts.keyfile";
+    public static final String CLIENT_CERT_PASSPHRASE = "org.apache.chemistry.opencmis.clientcerts.passphrase";
+
     // --- connection ---
 
     public static final String HTTP_INVOKER_CLASS = "org.apache.chemistry.opencmis.binding.httpinvoker.classname";

Modified: chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/BasicLoginTab.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/BasicLoginTab.java?rev=1782199&r1=1782198&r2=1782199&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/BasicLoginTab.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/BasicLoginTab.java Wed Feb  8 15:05:38 2017
@@ -65,6 +65,7 @@ public class BasicLoginTab extends Abstr
     private JRadioButton authenticationStandardButton;
     private JRadioButton authenticationNTLMButton;
     private JRadioButton authenticationOAuthButton;
+    private JRadioButton authenticationCertButton;
     private JRadioButton compressionOnButton;
     private JRadioButton compressionOffButton;
     private JRadioButton clientCompressionOnButton;
@@ -90,7 +91,8 @@ public class BasicLoginTab extends Abstr
 
         createBindingButtons(this);
 
-        usernameField = createTextField(this, "Username:", "Enter the user name.");
+        usernameField = createTextField(this, "Username:",
+                "<html>Enter the user name.<br>(Or bearer token for OAuth authentication or key file path for client certificate authentication.)");
         usernameField.setText(System.getProperty(SYSPROP_USER, ""));
 
         passwordField = createPasswordField(this, "Password:", "Enter the users password.");
@@ -167,16 +169,19 @@ public class BasicLoginTab extends Abstr
                 .equals("standard"));
         boolean ntlm = (System.getProperty(SYSPROP_AUTHENTICATION, "").toLowerCase(Locale.ENGLISH).equals("ntlm"));
         boolean oauth = (System.getProperty(SYSPROP_AUTHENTICATION, "").toLowerCase(Locale.ENGLISH).equals("oauth"));
-        boolean none = !standard && !ntlm;
+        boolean cert = (System.getProperty(SYSPROP_AUTHENTICATION, "").toLowerCase(Locale.ENGLISH).equals("cert"));
+        boolean none = !standard && !ntlm && !oauth && !cert;
         authenticationNoneButton = new JRadioButton("None", none);
         authenticationStandardButton = new JRadioButton("Standard", standard);
         authenticationNTLMButton = new JRadioButton("NTLM", ntlm);
         authenticationOAuthButton = new JRadioButton("OAuth 2.0 (Bearer Token)", oauth);
+        authenticationCertButton = new JRadioButton("Client Certificate", cert);
         ButtonGroup authenticationGroup = new ButtonGroup();
         authenticationGroup.add(authenticationNoneButton);
         authenticationGroup.add(authenticationStandardButton);
         authenticationGroup.add(authenticationNTLMButton);
         authenticationGroup.add(authenticationOAuthButton);
+        authenticationGroup.add(authenticationCertButton);
         authenticationContainer.add(authenticationNoneButton);
         authenticationContainer.add(Box.createRigidArea(WorkbenchScale.scaleDimension(new Dimension(10, 0))));
         authenticationContainer.add(authenticationStandardButton);
@@ -184,14 +189,17 @@ public class BasicLoginTab extends Abstr
         authenticationContainer.add(authenticationNTLMButton);
         authenticationContainer.add(Box.createRigidArea(WorkbenchScale.scaleDimension(new Dimension(10, 0))));
         authenticationContainer.add(authenticationOAuthButton);
+        authenticationContainer.add(Box.createRigidArea(WorkbenchScale.scaleDimension(new Dimension(10, 0))));
+        authenticationContainer.add(authenticationCertButton);
         JLabel authenticatioLabel = new JLabel("Authentication:", SwingConstants.TRAILING);
 
         pane.add(authenticatioLabel);
         pane.add(createHelp("<html>Select the authentication method.<br>"
                 + "The <b>Standard authentication</b> is Basic Auth and should work with most repositories.<br>"
                 + "The <b>NTLM authentication</b> should be used with caution! It's very likely that some CMIS operations will fail.<br>"
-                + "The <b>OAuth authentication</b> requires a bearer token in the username field. The token will not be refreshed when it expires."
-                + "Use the OAuthAuthenticationProvider for full OAuth support."));
+                + "The <b>OAuth authentication</b> requires a bearer token in the username field. The token will not be refreshed when it expires. "
+                + "Use the OAuthAuthenticationProvider for full OAuth support.<br>"
+                + "The <b>Client Certificate authentication</b> requires a JKS key file path the username field and the passphrase in the password field."));
         pane.add(authenticationContainer);
     }
 
@@ -281,6 +289,8 @@ public class BasicLoginTab extends Abstr
             authentication = ClientSession.Authentication.NTLM;
         } else if (authenticationOAuthButton.isSelected()) {
             authentication = ClientSession.Authentication.OAUTH_BEARER;
+        } else if (authenticationCertButton.isSelected()) {
+            authentication = ClientSession.Authentication.CLIENT_CERT;
         }
 
         Locale locale = null;

Modified: chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/DiscoverLoginTab.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/DiscoverLoginTab.java?rev=1782199&r1=1782198&r2=1782199&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/DiscoverLoginTab.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/DiscoverLoginTab.java Wed Feb  8 15:05:38 2017
@@ -51,6 +51,8 @@ import javax.swing.table.DefaultTableCel
 import javax.swing.table.TableColumn;
 
 import org.apache.chemistry.opencmis.client.api.CmisEndpointDocumentReader;
+import org.apache.chemistry.opencmis.client.bindings.spi.ClientCertificateAuthenticationProvider;
+import org.apache.chemistry.opencmis.client.bindings.spi.OAuthAuthenticationProvider;
 import org.apache.chemistry.opencmis.client.runtime.CmisEndpointDocumentReaderImpl;
 import org.apache.chemistry.opencmis.commons.SessionParameter;
 import org.apache.chemistry.opencmis.commons.endpoints.CmisAuthentication;
@@ -188,6 +190,13 @@ public class DiscoverLoginTab extends Ab
             parameters.put(SessionParameter.OAUTH_CLIENT_ID, "");
             parameters.put(SessionParameter.OAUTH_CLIENT_SECRET, "");
             parameters.put(SessionParameter.OAUTH_CODE, "");
+            parameters.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS, OAuthAuthenticationProvider.class.getName());
+        } else if (CmisAuthentication.AUTH_CERT.equals(auth.getType())) {
+            // client cert parameters
+            parameters.put(SessionParameter.CLIENT_CERT_KEYFILE, "");
+            parameters.put(SessionParameter.CLIENT_CERT_PASSPHRASE, "");
+            parameters.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS,
+                    ClientCertificateAuthenticationProvider.class.getName());
         } else if (!CmisAuthentication.AUTH_NONE.equals(auth.getType())
                 && !parameters.containsKey(SessionParameter.AUTHENTICATION_PROVIDER_CLASS)) {
             // a custom authentication provider is required here
@@ -243,8 +252,8 @@ public class DiscoverLoginTab extends Ab
             allEnpointsMenuItem.addActionListener(new ActionListener() {
                 @Override
                 public void actionPerformed(ActionEvent e) {
-                    String json = CmisEndpointsDocumentHelper.write(((CmisAuthenticationModel) getModel())
-                            .getCmisEndpointsDocument());
+                    String json = CmisEndpointsDocumentHelper
+                            .write(((CmisAuthenticationModel) getModel()).getCmisEndpointsDocument());
                     copyTableToClipboard(json);
                 }
             });
@@ -407,8 +416,8 @@ public class DiscoverLoginTab extends Ab
         }
 
         @Override
-        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
-                boolean hasFocus, int row, int column) {
+        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
+                int row, int column) {
             Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
 
             // make sure that the text fit into the row

Modified: chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/model/ClientSession.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/model/ClientSession.java?rev=1782199&r1=1782198&r2=1782199&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/model/ClientSession.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-workbench/chemistry-opencmis-workbench/src/main/java/org/apache/chemistry/opencmis/workbench/model/ClientSession.java Wed Feb  8 15:05:38 2017
@@ -73,7 +73,7 @@ public class ClientSession {
     public static final String MAX_FOLDER_CHILDREN = FOLDER_PREFIX + "maxChildren";
 
     public enum Authentication {
-        NONE, STANDARD, NTLM, OAUTH_BEARER
+        NONE, STANDARD, NTLM, OAUTH_BEARER, CLIENT_CERT
     }
 
     private static final Set<String> FOLDER_PROPERTY_SET = new HashSet<String>();
@@ -153,6 +153,9 @@ public class ClientSession {
         case OAUTH_BEARER:
             parameters.setOAuthBearerTokenAuthentication(username);
             break;
+        case CLIENT_CERT:
+            parameters.setCertificateAuthentication(username, password);
+            break;
         default:
             parameters.setNoAuthentication();
         }
@@ -411,7 +414,7 @@ public class ClientSession {
         };
 
         try {
-            SSLContext sc = SSLContext.getInstance("SSL");
+            SSLContext sc = SSLContext.getInstance("TSL");
             sc.init(null, trustAllCerts, new java.security.SecureRandom());
             HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
             HttpsURLConnection.setDefaultHostnameVerifier(accepctAllHostnames);