/**
* 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.hadoop.gateway.services.security.impl;
import org.apache.hadoop.gateway.GatewayMessages;
import org.apache.hadoop.gateway.GatewayResources;
import org.apache.hadoop.gateway.config.GatewayConfig;
import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
import org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
import org.apache.hadoop.gateway.services.Service;
import org.apache.hadoop.gateway.services.ServiceLifecycleException;
import org.apache.hadoop.gateway.services.security.KeystoreService;
import org.apache.hadoop.gateway.services.security.KeystoreServiceException;
import org.apache.hadoop.gateway.services.security.impl.X509CertificateUtil;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Map;
public class DefaultKeystoreService extends BaseKeystoreService implements KeystoreService, Service {
private static final String dnTemplate = "CN={0},OU=Test,O=Hadoop,L=Test,ST=Test,C=US";
private static final String CREDENTIALS_SUFFIX = "-credentials.jceks";
public static final String GATEWAY_KEYSTORE = "gateway.jks";
private static final String CERT_GEN_MODE = "hadoop.gateway.cert.gen.mode";
private static final String CERT_GEN_MODE_LOCALHOST = "localhost";
private static final String CERT_GEN_MODE_HOSTNAME = "hostname";
private static GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
private static GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
private String signingKeystoreName = null;
private String signingKeyAlias = null;
@Override
public void init(GatewayConfig config, Map<String, String> options)
throws ServiceLifecycleException {
this.keyStoreDir = config.getGatewaySecurityDir() + File.separator + "keystores" + File.separator;
File ksd = new File(this.keyStoreDir);
if (!ksd.exists()) {
if( !ksd.mkdirs() ) {
throw new ServiceLifecycleException( RES.failedToCreateKeyStoreDirectory( ksd.getAbsolutePath() ) );
}
}
signingKeystoreName = config.getSigningKeystoreName();
// ensure that the keystore actually exists and fail to start if not
if (signingKeystoreName != null) {
File sks = new File(this.keyStoreDir, signingKeystoreName);
if (!sks.exists()) {
throw new ServiceLifecycleException("Configured signing keystore does not exist.");
}
signingKeyAlias = config.getSigningKeyAlias();
if (signingKeyAlias != null) {
// ensure that the signing key alias exists in the configured keystore
KeyStore ks;
try {
ks = getSigningKeystore();
if (ks != null) {
if (!ks.containsAlias(signingKeyAlias)) {
throw new ServiceLifecycleException("Configured signing key alias does not exist.");
}
}
} catch (KeystoreServiceException e) {
throw new ServiceLifecycleException("Unable to get the configured signing keystore.", e);
} catch (KeyStoreException e) {
throw new ServiceLifecycleException("Signing keystore has not been loaded.", e);
}
}
}
}
@Override
public void start() throws ServiceLifecycleException {
}
@Override
public void stop() throws ServiceLifecycleException {
}
@Override
public void createKeystoreForGateway() throws KeystoreServiceException {
String filename = getKeystorePath();
createKeystore(filename, "JKS");
}
@Override
public KeyStore getKeystoreForGateway() throws KeystoreServiceException {
final File keyStoreFile = new File( keyStoreDir + GATEWAY_KEYSTORE );
return getKeystore(keyStoreFile, "JKS");
}
@Override
public KeyStore getSigningKeystore() throws KeystoreServiceException {
File keyStoreFile = null;
if (signingKeystoreName == null) {
keyStoreFile = new File(keyStoreDir + GATEWAY_KEYSTORE);
}
else {
keyStoreFile = new File(keyStoreDir + signingKeystoreName);
// make sure the keystore exists
if (!keyStoreFile.exists()) {
throw new KeystoreServiceException("Configured signing keystore does not exist.");
}
}
return getKeystore(keyStoreFile, "JKS");
}
@Override
public void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException {
addSelfSignedCertForGateway(alias, passphrase, null);
}
@Override
public void addSelfSignedCertForGateway(String alias, char[] passphrase, String hostname)
throws KeystoreServiceException {
KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair KPair = keyPairGenerator.generateKeyPair();
if (hostname == null) {
hostname = System.getProperty(CERT_GEN_MODE, CERT_GEN_MODE_LOCALHOST);
}
X509Certificate cert = null;
if(hostname.equals(CERT_GEN_MODE_HOSTNAME)) {
String dn = buildDistinguishedName(InetAddress.getLocalHost().getHostName());
cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
}
else {
String dn = buildDistinguishedName(hostname);
cert = X509CertificateUtil.generateCertificate(dn, KPair, 365, "SHA1withRSA");
}
KeyStore privateKS = getKeystoreForGateway();
privateKS.setKeyEntry(alias, KPair.getPrivate(),
passphrase,
new java.security.cert.Certificate[]{cert});
writeKeystoreToFile(privateKS, new File( keyStoreDir + GATEWAY_KEYSTORE ));
//writeCertificateToFile( cert, new File( keyStoreDir + alias + ".pem" ) );
} catch (NoSuchAlgorithmException e) {
LOG.failedToAddSeflSignedCertForGateway( alias, e );
throw new KeystoreServiceException(e);
} catch (GeneralSecurityException e) {
LOG.failedToAddSeflSignedCertForGateway( alias, e );
throw new KeystoreServiceException(e);
} catch (IOException e) {
LOG.failedToAddSeflSignedCertForGateway( alias, e );
throw new KeystoreServiceException(e);
}
}
private String buildDistinguishedName(String hostname) {
MessageFormat headerFormatter = new MessageFormat(dnTemplate);
String[] paramArray = new String[1];
paramArray[0] = hostname;
String dn = headerFormatter.format(paramArray);
return dn;
}
@Override
public void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException {
String filename = keyStoreDir + clusterName + CREDENTIALS_SUFFIX;
createKeystore(filename, "JCEKS");
}
@Override
public boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException {
boolean rc = false;
final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX );
try {
rc = isKeystoreAvailable(keyStoreFile, "JCEKS");
} catch (KeyStoreException e) {
throw new KeystoreServiceException(e);
} catch (IOException e) {
throw new KeystoreServiceException(e);
}
return rc;
}
@Override
public boolean isKeystoreForGatewayAvailable() throws KeystoreServiceException {
boolean rc = false;
final File keyStoreFile = new File( keyStoreDir + GATEWAY_KEYSTORE );
try {
rc = isKeystoreAvailable(keyStoreFile, "JKS");
} catch (KeyStoreException e) {
throw new KeystoreServiceException(e);
} catch (IOException e) {
throw new KeystoreServiceException(e);
}
return rc;
}
@Override
public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException {
Key key = null;
KeyStore ks = getKeystoreForGateway();
if (passphrase == null) {
passphrase = masterService.getMasterSecret();
LOG.assumingKeyPassphraseIsMaster();
}
if (ks != null) {
try {
key = ks.getKey(alias, passphrase);
} catch (UnrecoverableKeyException e) {
LOG.failedToGetKeyForGateway( alias, e );
} catch (KeyStoreException e) {
LOG.failedToGetKeyForGateway( alias, e );
} catch (NoSuchAlgorithmException e) {
LOG.failedToGetKeyForGateway( alias, e );
}
}
return key;
}
@Override
public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException {
Key key = null;
KeyStore ks = getSigningKeystore();
if (passphrase == null) {
passphrase = masterService.getMasterSecret();
LOG.assumingKeyPassphraseIsMaster();
}
if (ks != null) {
try {
key = ks.getKey(alias, passphrase);
} catch (UnrecoverableKeyException e) {
LOG.failedToGetKeyForGateway( alias, e );
} catch (KeyStoreException e) {
LOG.failedToGetKeyForGateway( alias, e );
} catch (NoSuchAlgorithmException e) {
LOG.failedToGetKeyForGateway( alias, e );
}
}
return key;
}
public KeyStore getCredentialStoreForCluster(String clusterName)
throws KeystoreServiceException {
final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX );
return getKeystore(keyStoreFile, "JCEKS");
}
public void addCredentialForCluster(String clusterName, String alias, String value)
throws KeystoreServiceException {
KeyStore ks = getCredentialStoreForCluster(clusterName);
addCredential(alias, value, ks);
final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX );
try {
writeKeystoreToFile(ks, keyStoreFile);
} catch (KeyStoreException e) {
LOG.failedToAddCredentialForCluster( clusterName, e );
} catch (NoSuchAlgorithmException e) {
LOG.failedToAddCredentialForCluster( clusterName, e );
} catch (CertificateException e) {
LOG.failedToAddCredentialForCluster( clusterName, e );
} catch (IOException e) {
LOG.failedToAddCredentialForCluster( clusterName, e );
}
}
@Override
public char[] getCredentialForCluster(String clusterName, String alias)
throws KeystoreServiceException {
char[] credential = null;
KeyStore ks = getCredentialStoreForCluster(clusterName);
if (ks != null) {
try {
char[] masterSecret = masterService.getMasterSecret();
Key credentialKey = ks.getKey( alias, masterSecret );
if (credentialKey != null) {
byte[] credentialBytes = credentialKey.getEncoded();
String credentialString = new String( credentialBytes );
credential = credentialString.toCharArray();
}
} catch (UnrecoverableKeyException e) {
LOG.failedToGetCredentialForCluster( clusterName, e );
} catch (KeyStoreException e) {
LOG.failedToGetCredentialForCluster( clusterName, e );
} catch (NoSuchAlgorithmException e) {
LOG.failedToGetCredentialForCluster( clusterName, e );
}
}
return credential;
}
@Override
public void removeCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException {
KeyStore ks = getCredentialStoreForCluster(clusterName);
removeCredential(alias, ks);
final File keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX );
try {
writeKeystoreToFile(ks, keyStoreFile);
} catch (KeyStoreException e) {
LOG.failedToRemoveCredentialForCluster(clusterName, e);
} catch (NoSuchAlgorithmException e) {
LOG.failedToRemoveCredentialForCluster(clusterName, e);
} catch (CertificateException e) {
LOG.failedToRemoveCredentialForCluster(clusterName, e);
} catch (IOException e) {
LOG.failedToRemoveCredentialForCluster(clusterName, e);
}
}
@Override
public String getKeystorePath() {
return keyStoreDir + GATEWAY_KEYSTORE;
}
}