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