| /** |
| * 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); |
| } |
| 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); |
| } |
| |
| 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); |
| } |
| } |
| |
| File finalTrustStoreFile; |
| try { |
| finalTrustStoreFile = saveKeyStore(truststoreFile, trustStore, password); |
| } catch (Exception e5) { |
| throw new HasException("Failed to generate trust store files.", e5); |
| } |
| |
| // 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); |
| } |
| |
| 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); |
| } |
| |
| for (File file : files) { |
| try { |
| channel.put(file.getAbsolutePath(), file.getName()); |
| } catch (SftpException e10) { |
| throw new HasException("Failed to send the https cert files.", e10); |
| } |
| } |
| channel.disconnect(); |
| } |
| } |
| } |