/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.recoverpoint.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.net.www.protocol.http.AuthCacheImpl;
import sun.net.www.protocol.http.AuthCacheValue;
import com.emc.fapiclient.ws.FunctionalAPIImpl;
import com.emc.fapiclient.ws.FunctionalAPIImplService;
public class RecoverPointConnection {
private static final String FAPI_URL = "http://impl.version4_1.fapi.emc.com/";
private static final String FAPI_SERVICENAME = "FunctionalAPIImplService";
private static final String SYSPROPS_HTTP_MAX_REDIRECTS = "http.maxRedirects";
private static final String NEW_MAX_REDIRECTS = "2";
private static Logger logger = LoggerFactory.getLogger(RecoverPointConnection.class);
/**
* Connect to RP and return a handle that can be used for FAPI calls
*
* @param endpoint - Address to connect to
* @param username - Username for credentials
* @param password - Password for credentials
*
* @return FunctionalAPIImpl - A handle for FAPI access
*
**/
public FunctionalAPIImpl connect(URI endpoint, String username, String password) {
try {
ignoreCertifications();
// interceptCertificates();
} catch (Exception e) {
// so what?
}
String destAddress = endpoint.toASCIIString();
try {
URL baseUrl = FunctionalAPIImplService.class.getResource(".");
URL url = new URL(baseUrl, destAddress);
final String finalUser = username;
final String finalPassword = password;
// Modify the System Property for Max Redirects to a smaller number so we don't hammer
// the device over and over unnecessarily with bad credentials (if they're bad) as this
// takes a long time. Default is 20 redirects.
// However we will save the old redirect value and restore it after we're done.
String oldMaxRedirectsValue = null;
try {
oldMaxRedirectsValue = System.getProperty(SYSPROPS_HTTP_MAX_REDIRECTS);
} catch (NullPointerException npe) {
logger.warn("The System property " + SYSPROPS_HTTP_MAX_REDIRECTS + " does not already exist for some reason.");
}
// Set the Property for http.maxRedirects to 2 to prevent unnecessary retries
System.setProperty(SYSPROPS_HTTP_MAX_REDIRECTS, NEW_MAX_REDIRECTS);
// If we're creating a new connection, we want to clear the cache as it may be holding
// onto a previously valid connection.
AuthCacheValue.setAuthCache(new AuthCacheImpl());
Authenticator.setDefault(null);
// Create a PasswordAuthentication so when the request is asked via HTTP for authentication,
// the below will be automatically returned. This is set here, but invoked behind the scenes.
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(finalUser, finalPassword.toCharArray());
}
});
logger.info("Attempting to connect to service " + FAPI_SERVICENAME + " at url " + FAPI_URL + " using auth credentials for: "
+ finalUser);
// Connect to the service
FunctionalAPIImplService service = new FunctionalAPIImplService(url, new QName(FAPI_URL, FAPI_SERVICENAME));
FunctionalAPIImpl impl = service.getFunctionalAPIImplPort();
BindingProvider bp = (BindingProvider) impl;
Map<String, Object> map = bp.getRequestContext();
logger.info("RecoverPoint service: Dest: " + destAddress + ", user: " + username);
map.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, destAddress);
map.put(BindingProvider.USERNAME_PROPERTY, username);
map.put(BindingProvider.PASSWORD_PROPERTY, password);
// Reset the System Property for http.maxRedirects, but only if an existing value was present
if (oldMaxRedirectsValue != null && !oldMaxRedirectsValue.isEmpty())
{
System.setProperty(SYSPROPS_HTTP_MAX_REDIRECTS, oldMaxRedirectsValue);
}
logger.info("Connected.");
return impl;
} catch (MalformedURLException e) {
logger.error("Failed to create URL for the wsdl Location: " + destAddress);
logger.error(e.getMessage());
return null;
}
}
// Generate a trust manager
/**
* @return
*/
private X509TrustManager getTrustMangerForCertificates() {
X509TrustManager tm = new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] ax509certificates, String arg1)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
// for (X509Certificate certificate : ax509certificates) {
// // Add to the certificate to keystore
// addCertToKeyStore(certificate, alias);
//
//
// }
if (ax509certificates == null) {
logger.error("Null certificate received");
throw new java.security.cert.CertificateException();
}
for (java.security.cert.X509Certificate certificate : ax509certificates) {
// Don't display this, for security reasons. Uncomment if
// necessary.
// logger.info("Client certificate information:");
// logger.info(" Subject DN: " +
// certificate.getSubjectDN());
// logger.info(" Issuer DN: " + certificate.getIssuerDN());
// logger.info(" Serial number: " +
// certificate.getSerialNumber());
// logger.info(" Public Key: " +
// certificate.getPublicKey());
// logger.info("");
try {
addCertToKeyStore(certificate, certificate.getSerialNumber().toString());
break;
} catch (NoSuchAlgorithmException e) {
logger.error("NoSuchAlgorithmException Exception. Failed to add certificate to keystore");
throw new java.security.cert.CertificateException(); // NOSONAR
} catch (KeyStoreException e) {
logger.error("KeyStoreException Exception. Failed to add certificate to keystore");
throw new java.security.cert.CertificateException(); // NOSONAR
} catch (NoSuchProviderException e) {
logger.error("NoSuchProviderException Exception. Failed to add certificate to keystore");
throw new java.security.cert.CertificateException(); // NOSONAR
} catch (IOException e) {
logger.warn("IOException Exception. Failed to add certificate to keystore. Assuming other thread did it.");
}
}
}
};
return tm;
}
// Add a certificate to the keystore
/**
* @param cert
* @param alias
* @throws NoSuchAlgorithmException
* @throws java.security.cert.CertificateException
* @throws KeyStoreException
* @throws NoSuchProviderException
* @throws IOException
*/
private void addCertToKeyStore(java.security.cert.Certificate cert, String alias) throws NoSuchAlgorithmException,
java.security.cert.CertificateException, KeyStoreException, NoSuchProviderException, IOException {
// CertificateFactory cf = CertificateFactory.getInstance("X.509");
// change this if you want another keystorefile by default
String keystoreName = System.getProperty("keystore");
// logger.info("addCertToKeyStore called for alias: " + alias);
// logger.info("Cert: " + cert.toString());
if (keystoreName == null) {
// especially this ;-)
keystoreName = System.getProperty("user.home") + System.getProperty("file.separator") + "keystore.ImportKey";
}
String keystore_pass = "changeit";
// initializing keystore
KeyStore ks = createAndLoadKeyStore(keystoreName, keystore_pass);
java.security.cert.Certificate ksCrt = ks.getCertificate(alias);
if (ksCrt != null) {
ks.deleteEntry(alias);
}
// Add the certificate
ks.setCertificateEntry(alias, cert);
// Save the new keystore contents
FileOutputStream out = new FileOutputStream(keystoreName);
try {
ks.store(out, keystore_pass.toCharArray());
} finally {
out.close();
}
}
/**
* @param keyStorePath
* @param storePass
* @return
* @throws NoSuchAlgorithmException
* @throws java.security.cert.CertificateException
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchProviderException
*/
private KeyStore createAndLoadKeyStore(String keyStorePath, String storePass) throws NoSuchAlgorithmException,
java.security.cert.CertificateException, IOException, KeyStoreException, NoSuchProviderException {
// TODO Auto-generated method stub
// initializing keystore
KeyStore ks = KeyStore.getInstance("JKS", "SUN");
ks.load(null, storePass.toCharArray());
File storeFile = new File(keyStorePath);
// logger.info("Create and load key store: " + keyStorePath +
// " using password: " + storePass);
if (!storeFile.exists()) {
FileOutputStream osStorePath = new FileOutputStream(keyStorePath);
try {
ks.store(osStorePath, storePass.toCharArray());
} finally {
osStorePath.close();
}
}
FileInputStream isStorePath = new FileInputStream(keyStorePath);
try {
ks.load(isStorePath, storePass.toCharArray());
} finally {
isStorePath.close();
}
return ks;
}
/**
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
@SuppressWarnings("unused")
private void interceptCertificates() throws NoSuchAlgorithmException, KeyManagementException {
// logger.info("Intercept certificates");
TrustManager[] trustAllCerts = new TrustManager[1];
trustAllCerts[0] = getTrustMangerForCertificates();
// SSLContext sc = SSLContext.getInstance("SSL");
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier());
// HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);
System.setProperty("org.jboss.security.ignoreHttpsHost", "true");
}
private static class NullHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
/**
* Sets the https connection to trust all certifications.
*
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
// TODO - I don't think we want to ignore certificates in the final product.
// May need to use 'keytool' to import certificates
private void ignoreCertifications() throws KeyManagementException, NoSuchAlgorithmException {
System.setProperty("javax.net.debug", "true");
System.setProperty("org.jboss.security.ignoreHttpsHost", "true");
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
});
trustAllHttpsCertificates();
}
/**
* @throws KeyManagementException
* @throws NoSuchAlgorithmException
*/
private void trustAllHttpsCertificates() throws KeyManagementException, NoSuchAlgorithmException {
// Create a trust manager that does not validate certificate chains:
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new AllTM();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
/**
* This is a trust manager that trusts all certificates
*/
private static class AllTM implements TrustManager, X509TrustManager {
@SuppressWarnings("unused")
public boolean isServerTrusted(X509Certificate[] certs) {
return true;
}
@SuppressWarnings("unused")
public boolean isClientTrusted(X509Certificate[] certs) {
return true;
}
@SuppressWarnings("unused")
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
@SuppressWarnings("unused")
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
}
}