Michael Pasternak has uploaded a new change for review.

Change subject: sdk: implement host CA certificate validation
......................................................................

sdk: implement host CA certificate validation

oVirt Java-SDK provides now full support for HTTP over Secure Sockets Layer 
(SSL)
or IETF Transport Layer Security (TLS) protocols by leveraging the Java Secure 
Socket
Extension (JSSE). JSSE has been integrated into the Java 2 platform as of 
version 1.4
and works with Java-SDK out of the box. On older Java 2 versions JSSE needs to 
be manually
installed and configured.

Once you have JSSE correctly installed, secure HTTP communication over SSL 
should be as
simple as plain HTTP communication, however SDK need to be supplied with 
KeyStore containing
host CA certeficate in order to validate the destination host identity, more 
details can be
found at [1]

[1] http://www.ovirt.org/Java-sdk#Working_with_SSL_.28Secure_Socket_Layer.29

Change-Id: Ibb8bb422a21e1f2c1263835e7d650e1e1a3633d2
Signed-off-by: Michael pasternak <mpast...@redhat.com>
---
M 
ovirt-engine-sdk-java-codegen/src/main/java/org/ovirt/engine/sdk/codegen/templates/ApiTemplate
M ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/Api.java
A 
ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/ProtocolException.java
A 
ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/SocketFactoryException.java
M 
ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/web/ConnectionsPoolBuilder.java
5 files changed, 440 insertions(+), 16 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine-sdk-java 
refs/changes/38/16538/1

diff --git 
a/ovirt-engine-sdk-java-codegen/src/main/java/org/ovirt/engine/sdk/codegen/templates/ApiTemplate
 
b/ovirt-engine-sdk-java-codegen/src/main/java/org/ovirt/engine/sdk/codegen/templates/ApiTemplate
index fd7ff8f..b9fb9e2 100644
--- 
a/ovirt-engine-sdk-java-codegen/src/main/java/org/ovirt/engine/sdk/codegen/templates/ApiTemplate
+++ 
b/ovirt-engine-sdk-java-codegen/src/main/java/org/ovirt/engine/sdk/codegen/templates/ApiTemplate
@@ -60,6 +60,79 @@
     /**
      * @param url
      *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured site
+     *             using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String 
keyStorePath)
+            throws ClientProtocolException, ServerException, IOException,
+                   UnsecuredConnectionAttemptError {
+
+        configureLog4J();
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .keyStorePath(keyStorePath)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        this.initResources();
+    }
+
+    /**
+     * @param url
+     *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     * @param keyStorePassword
+     *            password for the CA certificate KeyStore
+     * @param filter
+     *            enables filtering based on user's permissions
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured site
+     *             using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String 
keyStorePath, String keyStorePassword, Boolean filter)
+            throws ClientProtocolException, ServerException, 
UnsecuredConnectionAttemptError, IOException {
+
+        configureLog4J();
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .keyStorePath(keyStorePath)
+                .keyStorePassword(keyStorePassword)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .filter(filter)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        initResources();
+    }
+
+    /**
+     * @param url
+     *            oVirt api url
      * @param sessionid
      *            oVirt api sessionid to authenticate the user with
      *            (used as SSO solution instead of username+password)
@@ -244,6 +317,63 @@
     }
 
     /**
+     * @param url
+     *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param sessionid
+     *            oVirt api sessionid to authenticate the user with
+     *            (used as SSO solution instead of username+password)
+     * @param port
+     *            oVirt api port
+     * @param timeout
+     *            request timeout
+     * @param persistentAuth
+     *            disable persistent authentication
+     *            (will be used auth. per request)
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     * @param keyStorePassword
+     *            password for the CA certificate KeyStore
+     * @param filter
+     *            enables filtering based on user's permissions
+     * @param debug
+     *            enables debug mode
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured
+     *             site using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String sessionid, 
Integer port, Integer timeout,
+            Boolean persistentAuth, String keyStorePath, String 
keyStorePassword, Boolean filter, Boolean debug)
+            throws ClientProtocolException, ServerException, 
UnsecuredConnectionAttemptError, IOException {
+
+        configureLog4J(debug);
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .port(port)
+                .timeout(timeout)
+                .keyStorePath(keyStorePath)
+                .keyStorePassword(keyStorePassword)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .sessionid(sessionid)
+                .persistentAuth(persistentAuth)
+                .filter(filter)
+                .debug(debug)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        initResources();
+    }
+
+    /**
      * Configures log4j
      */
     private void configureLog4J() {
diff --git a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/Api.java 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/Api.java
index eaa866b..e522443 100644
--- a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/Api.java
+++ b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/Api.java
@@ -96,6 +96,79 @@
     /**
      * @param url
      *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured site
+     *             using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String 
keyStorePath)
+            throws ClientProtocolException, ServerException, IOException,
+                   UnsecuredConnectionAttemptError {
+
+        configureLog4J();
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .keyStorePath(keyStorePath)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        this.initResources();
+    }
+
+    /**
+     * @param url
+     *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     * @param keyStorePassword
+     *            password for the CA certificate KeyStore
+     * @param filter
+     *            enables filtering based on user's permissions
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured site
+     *             using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String 
keyStorePath, String keyStorePassword, Boolean filter)
+            throws ClientProtocolException, ServerException, 
UnsecuredConnectionAttemptError, IOException {
+
+        configureLog4J();
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .keyStorePath(keyStorePath)
+                .keyStorePassword(keyStorePassword)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .filter(filter)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        initResources();
+    }
+
+    /**
+     * @param url
+     *            oVirt api url
      * @param sessionid
      *            oVirt api sessionid to authenticate the user with
      *            (used as SSO solution instead of username+password)
@@ -280,6 +353,63 @@
     }
 
     /**
+     * @param url
+     *            oVirt api url
+     * @param username
+     *            oVirt api username
+     * @param password
+     *            oVirt api password
+     * @param sessionid
+     *            oVirt api sessionid to authenticate the user with
+     *            (used as SSO solution instead of username+password)
+     * @param port
+     *            oVirt api port
+     * @param timeout
+     *            request timeout
+     * @param persistentAuth
+     *            disable persistent authentication
+     *            (will be used auth. per request)
+     * @param keyStorePath
+     *            path to CA certificate KeyStore
+     * @param keyStorePassword
+     *            password for the CA certificate KeyStore
+     * @param filter
+     *            enables filtering based on user's permissions
+     * @param debug
+     *            enables debug mode
+     *
+     * @throws ClientProtocolException
+     *             Signals that HTTP/S protocol error has occurred.
+     * @throws ServerException
+     *             Signals that an oVirt api error has occurred.
+     * @throws IOException
+     *             Signals that an I/O exception of some sort has occurred.
+     * @throws UnsecuredConnectionAttemptError
+     *             Signals that attempt of connecting to SSL secured
+     *             site using HTTP protocol has occurred.
+     */
+    public Api(String url, String username, String password, String sessionid, 
Integer port, Integer timeout,
+            Boolean persistentAuth, String keyStorePath, String 
keyStorePassword, Boolean filter, Boolean debug)
+            throws ClientProtocolException, ServerException, 
UnsecuredConnectionAttemptError, IOException {
+
+        configureLog4J(debug);
+        ConnectionsPool pool = new ConnectionsPoolBuilder(url, username, 
password)
+                .port(port)
+                .timeout(timeout)
+                .keyStorePath(keyStorePath)
+                .keyStorePassword(keyStorePassword)
+                .build();
+        HttpProxy httpProxy = new HttpProxyBuilder(pool)
+                .sessionid(sessionid)
+                .persistentAuth(persistentAuth)
+                .filter(filter)
+                .debug(debug)
+                .build();
+        this.proxy = new HttpProxyBroker(httpProxy);
+        initResources();
+    }
+
+    /**
      * Configures log4j
      */
     private void configureLog4J() {
diff --git 
a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/ProtocolException.java
 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/ProtocolException.java
new file mode 100644
index 0000000..f3d286e
--- /dev/null
+++ 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/ProtocolException.java
@@ -0,0 +1,38 @@
+package org.ovirt.engine.sdk.exceptions;
+
+/**
+ * Thrown during any kind of protocol failures
+ */
+public class ProtocolException extends OvirtSdkRuntimeException {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 6566413883513675015L;
+
+    /**
+     * @param message
+     *            exception message
+     * @param cause
+     *            exception cause
+     */
+    public ProtocolException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * @param message
+     *            exception message
+     */
+    public ProtocolException(String message) {
+        super(message);
+    }
+
+    /**
+     * @param cause
+     *            exception cause
+     */
+    public ProtocolException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git 
a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/SocketFactoryException.java
 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/SocketFactoryException.java
new file mode 100644
index 0000000..e0118de
--- /dev/null
+++ 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/exceptions/SocketFactoryException.java
@@ -0,0 +1,39 @@
+package org.ovirt.engine.sdk.exceptions;
+
+/**
+ * Thrown during socket initialization failures
+ */
+public class SocketFactoryException extends OvirtSdkRuntimeException {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = -6815039672561541599L;
+
+    /**
+     * @param message
+     *            exception message
+     * @param cause
+     *            exception cause
+     */
+    public SocketFactoryException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * @param message
+     *            exception message
+     */
+    public SocketFactoryException(String message) {
+        super(message);
+    }
+
+    /**
+     * @param cause
+     *            exception cause
+     */
+    public SocketFactoryException(Throwable cause) {
+        super(cause);
+    }
+
+}
\ No newline at end of file
diff --git 
a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/web/ConnectionsPoolBuilder.java
 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/web/ConnectionsPoolBuilder.java
index dec227e..f5159a9 100644
--- 
a/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/web/ConnectionsPoolBuilder.java
+++ 
b/ovirt-engine-sdk-java/src/main/java/org/ovirt/engine/sdk/web/ConnectionsPoolBuilder.java
@@ -16,10 +16,19 @@
 
 package org.ovirt.engine.sdk.web;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
@@ -36,6 +45,8 @@
 import org.apache.http.conn.ssl.SSLSocketFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.ovirt.engine.sdk.exceptions.ProtocolException;
+import org.ovirt.engine.sdk.exceptions.SocketFactoryException;
 import org.ovirt.engine.sdk.utils.StringUtils;
 
 /**
@@ -43,9 +54,15 @@
  */
 public class ConnectionsPoolBuilder {
 
+    private static final String DEFAULT_KEYSTORE_TRUSTSTORE = 
"ovirtsdk-keystore.truststore";
     private static final String BAD_PROTOCOL_ERROR = "Unsupported protocol ";
     private static final String BAD_KEY_ERROR = "SSL context initiation has 
failed because of key error.";
     private static final String NO_TLS_ERROR = "SSL context initiation has 
failed locating TLS slgorithm.";
+    private static final String KEY_STORE_ERROR = "CA certeficate keysotore 
initiation has failed.";
+    private static final String KEY_STORE_FILE_NOT_FOUND_ERROR = "CA 
certeficate keysotore was not found.";
+    private static final String CERTEFICATE_ERROR = "CA certeficate error.";
+    private static final String IO_ERROR = "I/O error occured, is your 
keysotore password correct?";
+    private static final String UNRECOVERABLE_KEY_ERROR = "Unrecoverable key 
error has occured.";
 
     private static int MAX_CONNECTIONS = 20;
     private static int MAX_CONNECTIONS_PER_HOST = 50;
@@ -66,6 +83,8 @@
     private int port = -1;
     private int timeout = -1;
     private boolean noHostVerification = false;
+    private String keyStorePath;
+    private String keyStorePassword;
 
     private URL urlobj = null;
 
@@ -150,6 +169,24 @@
      */
     public ConnectionsPoolBuilder ca_file(String ca_file) {
         this.ca_file = ca_file;
+        return this;
+    }
+
+    /**
+     * @param keyStorePath
+     *            path to server CA KeyStore
+     */
+    public ConnectionsPoolBuilder keyStorePath(String keyStorePath) {
+        this.keyStorePath = keyStorePath;
+        return this;
+    }
+
+    /**
+     * @param keyStorePassword
+     *            server CA KeyStore password
+     */
+    public ConnectionsPoolBuilder keyStorePassword(String keyStorePassword) {
+        this.keyStorePassword = keyStorePassword;
         return this;
     }
 
@@ -260,37 +297,63 @@
                             port,
                             PlainSocketFactory.getSocketFactory()));
         } else if (HTTPS_PROTOCOL.equals(protocol)) {
-            SSLContext sslcontext;
             try {
-                sslcontext = SSLContext.getInstance("TLS");
-
                 if (this.noHostVerification) {
+                    SSLContext sslcontext = SSLContext.getInstance("TLS");
                     sslcontext.init(null, new TrustManager[] { 
noCaTrustManager }, null);
                     sf = new SSLSocketFactory(
                             sslcontext,
                             SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                 } else {
-                    sslcontext.init(null, null, null);
-                    sf = new SSLSocketFactory(
-                            sslcontext,
-                            // This hostname verifier that works the same way 
as Curl and Firefox. The hostname must
-                            // match either the first CN, or any of the 
subject-alts. A wildcard can occur in the CN,
-                            // and in any of the subject-alts. The only 
difference between BrowserCompatHostnameVerifier
-                            // and StrictHostnameVerifier is that a wildcard 
(such as "*.foo.com") with
-                            // BrowserCompatHostnameVerifier matches all 
subdomains, including "a.b.foo.com".
-                            
SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
+                    KeyStore truststore = null;
+                    InputStream in = null;
+
+                    if (this.keyStorePath != null) {
+                        truststore = 
KeyStore.getInstance(KeyStore.getDefaultType());
+                        try {
+                            in = new FileInputStream(this.keyStorePath);
+                            truststore.load(
+                                    in,
+                                    this.keyStorePassword != null ?
+                                            this.keyStorePassword.toCharArray()
+                                            :
+                                            null);
+
+                        } finally {
+                            if (in != null) {
+                                in.close();
+                            }
+                        }
+                    }
+                    sf = new SSLSocketFactory(SSLSocketFactory.TLS,
+                            null,
+                            null,
+                            truststore,
+                            null,
+                            null,
+                            SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
                 }
 
                 schemeRegistry.register(
                         new Scheme(HTTPS_PROTOCOL, port, sf));
+
             } catch (NoSuchAlgorithmException e) {
-                e.printStackTrace();
-                throw new RuntimeException(NO_TLS_ERROR, e);
+                throw new SocketFactoryException(NO_TLS_ERROR, e);
             } catch (KeyManagementException e) {
-                throw new RuntimeException(BAD_KEY_ERROR, e);
+                throw new SocketFactoryException(BAD_KEY_ERROR, e);
+            } catch (KeyStoreException e) {
+                throw new SocketFactoryException(KEY_STORE_ERROR, e);
+            } catch (FileNotFoundException e) {
+                throw new 
SocketFactoryException(KEY_STORE_FILE_NOT_FOUND_ERROR, e);
+            } catch (CertificateException e) {
+                throw new SocketFactoryException(CERTEFICATE_ERROR, e);
+            } catch (IOException e) {
+                throw new SocketFactoryException(IO_ERROR, e);
+            } catch (UnrecoverableKeyException e) {
+                throw new SocketFactoryException(UNRECOVERABLE_KEY_ERROR, e);
             }
         } else {
-            throw new RuntimeException(BAD_PROTOCOL_ERROR + protocol);
+            throw new ProtocolException(BAD_PROTOCOL_ERROR + protocol);
         }
 
         return schemeRegistry;
@@ -325,6 +388,7 @@
      * @return ConnectionsPool
      */
     public ConnectionsPool build() {
+        this.keyStorePath = resolveKeyStorePath();
         return new ConnectionsPool(
                 createDefaultHttpClient(url,
                         username,
@@ -341,6 +405,29 @@
     }
 
     /**
+     * Trying to resolve keyStorePath if it's not defined
+     * according to the DEFAULT_KEYSTORE_TRUSTSTORE
+     * 
+     * on NX environment it may look like:
+     * /home/mpastern/.ovirtsdk/ovirtsdk-keystore.truststore
+     * 
+     * @return keyStorePath or NULL
+     */
+    private String resolveKeyStorePath() {
+        if (this.keyStorePath == null) {
+            String keyStorePathCandidate =
+                    System.getProperty("user.home") +
+                            File.separator + ".ovirtsdk" +
+                            File.separator + DEFAULT_KEYSTORE_TRUSTSTORE;
+
+            if (new File(keyStorePathCandidate).exists()) {
+                return keyStorePathCandidate;
+            }
+        }
+        return this.keyStorePath;
+    }
+
+    /**
      * This TrustManager used to ignore CA cert validation and should not be 
used unless user explicitly asks to ignore
      * host identity validation.
      */


-- 
To view, visit http://gerrit.ovirt.org/16538
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibb8bb422a21e1f2c1263835e7d650e1e1a3633d2
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine-sdk-java
Gerrit-Branch: master
Gerrit-Owner: Michael Pasternak <mpast...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to