blob: 0b54bb71619f46f4df4ea43bdd84bd82f50a86bf [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.kerby.kerberos.tool.admin.local.cmd;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import org.apache.commons.text.CharacterPredicates;
import org.apache.commons.text.RandomStringGenerator;
import org.apache.kerby.has.common.HasException;
import org.apache.kerby.has.server.admin.LocalHadmin;
import org.apache.kerby.util.IOUtil;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import javax.security.auth.x500.X500Principal;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* HTTPS certifications deploy tool.
*/
public class DeployHTTPSCertsCommand extends HadminCommand {
private static final String USAGE
= "\nUsage: deploy_certs [Hosts-File] [truststore_file] [truststore_password]"
+ " [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
+ "\tExample:\n"
+ "\t\tdeploy_https hosts.txt /etc/has/truststore.jks 123456 /etc/has 22 username password\n";
public DeployHTTPSCertsCommand(LocalHadmin hadmin) {
super(hadmin);
}
private static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
return keyGen.genKeyPair();
}
private static X509Certificate generateCertificate(String args, KeyPair pair)
throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
NoSuchAlgorithmException, SignatureException {
Date from = new Date();
Date to = new Date(from.getTime() + 90 * 86400000L);
BigInteger sn = new BigInteger(64, new SecureRandom());
X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
X500Principal dnName = new X500Principal(args);
certGen.setSerialNumber(sn);
certGen.setIssuerDN(dnName);
certGen.setNotBefore(from);
certGen.setNotAfter(to);
certGen.setSubjectDN(dnName);
certGen.setPublicKey(pair.getPublic());
certGen.setSignatureAlgorithm("SHA1withRSA");
return certGen.generate(pair.getPrivate());
}
private static File saveKeyStore(String fileName, KeyStore ks, String password)
throws GeneralSecurityException, IOException {
File keystoreFile = new File(fileName);
if (keystoreFile.exists() && !keystoreFile.delete()) {
throw new IOException("Failed to delete original file: " + fileName);
}
FileOutputStream out = new FileOutputStream(keystoreFile);
ks.store(out, password.toCharArray());
out.close();
return keystoreFile;
}
private File createClientSSLConfig(String trustStorePath, String trustStorePassword,
String keyStorePassword) throws HasException {
String resourcePath = "/ssl-client.conf.template";
InputStream templateResource = getClass().getResourceAsStream(resourcePath);
File sslConfigFile = new File("ssl-client.conf");
try {
String content = IOUtil.readInput(templateResource);
content = content.replaceAll("_location_", trustStorePath);
content = content.replaceAll("_password_", trustStorePassword);
content = content.replaceAll("_keyPassword_", keyStorePassword);
IOUtil.writeFile(content, sslConfigFile);
return sslConfigFile;
} catch (IOException e) {
throw new HasException("Failed to create client ssl configuration file", e);
}
}
private final class KeyStoreInfo {
KeyStore keyStore;
String keyPasswd;
private KeyStoreInfo(KeyStore keyStore, String keyPasswd) {
this.keyStore = keyStore;
this.keyPasswd = keyPasswd;
}
private String getKeyPasswd() {
return this.keyPasswd;
}
private KeyStore getKeyStore() {
return this.keyStore;
}
}
@Override
public void execute(String[] items) throws HasException {
if (items.length < 7 || items.length > 8) {
System.err.println(USAGE);
return;
}
File hostFile = new File(items[1]);
if (!hostFile.exists()) {
throw new HasException("Host file is not exist.");
}
String truststoreFile = items[2];
String truststoreSecret = items[3];
String pathToDeploy = items[4];
int port = Integer.valueOf(items[5]);
String username = items[6];
String password = "";
if (items.length == 8) {
password = items[7];
}
// Get hosts from host file
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(hostFile));
} catch (FileNotFoundException e) {
throw new HasException("The hosts file: " + hostFile
+ "is not exist. " + e.getMessage());
}
StringBuilder sb = new StringBuilder();
String tempString;
try {
while ((tempString = reader.readLine()) != null) {
sb.append(tempString);
}
} catch (IOException e1) {
throw new HasException("Failed to read file. " + e1.getMessage());
}
String[] hostArray = sb.toString().replace(" ", "").split(",");
// Get truststore from truststore file
Map<String, KeyStoreInfo> keyStoreInfoMap = new HashMap<>(16);
KeyStore trustStore;
try {
trustStore = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream(truststoreFile);
trustStore.load(in, truststoreSecret.toCharArray());
} catch (Exception e2) {
throw new HasException("Failed to get truststore from the file: "
+ truststoreFile + ". " + e2.getMessage());
}
RandomStringGenerator generator = new RandomStringGenerator.Builder()
.withinRange('a', 'z')
.filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
.build();
// Generate keystore map
for (String hostname : hostArray) {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
String localHostname = inetAddress.getHostName();
if (hostname.equals(localHostname)) {
continue;
}
} catch (UnknownHostException e3) {
throw new HasException("Failed to get local hostname. " + e3.getMessage());
}
KeyStore ks;
try {
KeyPair cKP = generateKeyPair();
String keyPassword = generator.generate(15);
X509Certificate cert = generateCertificate("CN=" + hostname + ", O=has", cKP);
ks = KeyStore.getInstance("JKS");
ks.load(null, null);
ks.setKeyEntry(hostname, cKP.getPrivate(), keyPassword.toCharArray(),
new Certificate[]{cert});
KeyStoreInfo keyStoreInfo = new KeyStoreInfo(ks, keyPassword);
keyStoreInfoMap.put(hostname, keyStoreInfo);
trustStore.setCertificateEntry(hostname, cert);
} catch (Exception e4) {
throw new HasException("Failed to generate keystore. " + e4.getMessage());
}
}
File finalTrustStoreFile;
try {
finalTrustStoreFile = saveKeyStore(truststoreFile, trustStore, password);
} catch (Exception e5) {
throw new HasException("Failed to generate trust store files. " + e5.getMessage());
}
// Generate keystore, truststore, ssl config files and transfer them to destination
for (String hostname : hostArray) {
List<File> files = new ArrayList<>(3);
try {
KeyStoreInfo keyStoreInfo = keyStoreInfoMap.get(hostname);
File file = saveKeyStore(hostname + "_keystore.jks",
keyStoreInfo.getKeyStore(), keyStoreInfo.getKeyPasswd());
files.add(file);
files.add(finalTrustStoreFile);
files.add(createClientSSLConfig(pathToDeploy + "/truststore.jks",
truststoreSecret, keyStoreInfo.getKeyPasswd()));
} catch (Exception e6) {
throw new HasException("Failed to generate key store files. " + e6.getMessage());
}
JSch jsch = new JSch();
Session session;
try {
session = jsch.getSession(username, hostname, port);
} catch (JSchException e7) {
throw new HasException(e7.getMessage());
}
session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
ChannelSftp channel;
try {
session.connect();
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect();
} catch (JSchException e8) {
throw new HasException("Failed to set the session: " + e8.getMessage());
}
try {
String path = "";
String[] paths = pathToDeploy.split("/");
for (int i = 1; i < paths.length; i++) {
path = path + "/" + paths[i];
try {
channel.cd(path);
} catch (SftpException e9) {
if (e9.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
channel.mkdir(path);
} else {
throw new HasException(e9.getMessage());
}
}
}
} catch (SftpException e10) {
throw new HasException("Failed to mkdir path: " + e10.getMessage());
}
for (File file : files) {
try {
channel.put(file.getAbsolutePath(), file.getName());
} catch (SftpException e11) {
throw new HasException("Failed to send the https cert files. "
+ e11.getMessage());
}
}
channel.disconnect();
}
}
}