blob: c48d6941a06cae22137c901d95fb4a198b45a89c [file] [log] [blame]
/**
* 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.zookeeper.common;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.zookeeper.common.X509Exception.KeyManagerException;
import static org.apache.zookeeper.common.X509Exception.SSLContextException;
import static org.apache.zookeeper.common.X509Exception.TrustManagerException;
/**
* Utility code for X509 handling
*/
public class X509Util {
private static final Logger LOG = LoggerFactory.getLogger(X509Util.class);
/**
* @deprecated Use {@link ZKConfig#SSL_KEYSTORE_LOCATION}
* instead.
*/
@Deprecated
public static final String SSL_KEYSTORE_LOCATION = "zookeeper.ssl.keyStore.location";
/**
* @deprecated Use {@link ZKConfig#SSL_KEYSTORE_PASSWD}
* instead.
*/
@Deprecated
public static final String SSL_KEYSTORE_PASSWD = "zookeeper.ssl.keyStore.password";
/**
* @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_LOCATION}
* instead.
*/
@Deprecated
public static final String SSL_TRUSTSTORE_LOCATION = "zookeeper.ssl.trustStore.location";
/**
* @deprecated Use {@link ZKConfig#SSL_TRUSTSTORE_PASSWD}
* instead.
*/
@Deprecated
public static final String SSL_TRUSTSTORE_PASSWD = "zookeeper.ssl.trustStore.password";
/**
* @deprecated Use {@link ZKConfig#SSL_AUTHPROVIDER}
* instead.
*/
@Deprecated
public static final String SSL_AUTHPROVIDER = "zookeeper.ssl.authProvider";
public static SSLContext createSSLContext() throws SSLContextException {
/**
* Since Configuration initializes the key store and trust store related
* configuration from system property. Reading property from
* configuration will be same reading from system property
*/
ZKConfig config=new ZKConfig();
return createSSLContext(config);
}
public static SSLContext createSSLContext(ZKConfig config) throws SSLContextException {
KeyManager[] keyManagers = null;
TrustManager[] trustManagers = null;
String keyStoreLocationProp = config.getProperty(ZKConfig.SSL_KEYSTORE_LOCATION);
String keyStorePasswordProp = config.getProperty(ZKConfig.SSL_KEYSTORE_PASSWD);
// There are legal states in some use cases for null KeyManager or TrustManager.
// But if a user wanna specify one, location and password are required.
if (keyStoreLocationProp == null && keyStorePasswordProp == null) {
LOG.warn("keystore not specified for client connection");
} else {
if (keyStoreLocationProp == null) {
throw new SSLContextException("keystore location not specified for client connection");
}
if (keyStorePasswordProp == null) {
throw new SSLContextException("keystore password not specified for client connection");
}
try {
keyManagers = new KeyManager[]{
createKeyManager(keyStoreLocationProp, keyStorePasswordProp)};
} catch (KeyManagerException e) {
throw new SSLContextException("Failed to create KeyManager", e);
}
}
String trustStoreLocationProp = config.getProperty(ZKConfig.SSL_TRUSTSTORE_LOCATION);
String trustStorePasswordProp = config.getProperty(ZKConfig.SSL_TRUSTSTORE_PASSWD);
if (trustStoreLocationProp == null && trustStorePasswordProp == null) {
LOG.warn("keystore not specified for client connection");
} else {
if (trustStoreLocationProp == null) {
throw new SSLContextException("keystore location not specified for client connection");
}
if (trustStorePasswordProp == null) {
throw new SSLContextException("keystore password not specified for client connection");
}
try {
trustManagers = new TrustManager[]{
createTrustManager(trustStoreLocationProp, trustStorePasswordProp)};
} catch (TrustManagerException e) {
throw new SSLContextException("Failed to create KeyManager", e);
}
}
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(keyManagers, trustManagers, null);
} catch (Exception e) {
throw new SSLContextException(e);
}
return sslContext;
}
public static X509KeyManager createKeyManager(String keyStoreLocation, String keyStorePassword)
throws KeyManagerException {
FileInputStream inputStream = null;
try {
char[] keyStorePasswordChars = keyStorePassword.toCharArray();
File keyStoreFile = new File(keyStoreLocation);
KeyStore ks = KeyStore.getInstance("JKS");
inputStream = new FileInputStream(keyStoreFile);
ks.load(inputStream, keyStorePasswordChars);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, keyStorePasswordChars);
for (KeyManager km : kmf.getKeyManagers()) {
if (km instanceof X509KeyManager) {
return (X509KeyManager) km;
}
}
throw new KeyManagerException("Couldn't find X509KeyManager");
} catch (Exception e) {
throw new KeyManagerException(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {}
}
}
}
public static X509TrustManager createTrustManager(String trustStoreLocation, String trustStorePassword)
throws TrustManagerException {
FileInputStream inputStream = null;
try {
char[] trustStorePasswordChars = trustStorePassword.toCharArray();
File trustStoreFile = new File(trustStoreLocation);
KeyStore ts = KeyStore.getInstance("JKS");
inputStream = new FileInputStream(trustStoreFile);
ts.load(inputStream, trustStorePasswordChars);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
throw new TrustManagerException("Couldn't find X509TrustManager");
} catch (Exception e) {
throw new TrustManagerException(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {}
}
}
}
}