/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package se.kth.karamel.common.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import org.apache.log4j.Logger;
import org.jclouds.ssh.SshKeys;
import se.kth.karamel.common.exception.KaramelException;
import se.kth.karamel.common.exception.SshKeysNotfoundException;
/**
*
* @author kamal
*/
public class SshKeyService {
private static final Logger logger = Logger.getLogger(SshKeyService.class);
public static boolean checkIfPasswordNeeded(SshKeyPair sshKey) {
// http://serverfault.com/questions/52732/find-out-if-a-ssh-private-key-requires-a-password
// OpenSSH Keys with passwords contain this string
if (sshKey.getPublicKey().isEmpty() || sshKey.getPrivateKey().isEmpty()) {
return true;
}
return sshKey.getPrivateKey().contains("Proc-Type: 4,ENCRYPTED");
}
public static SshKeyPair generateAndStoreSshKeys() throws KaramelException {
File folder = new File(Settings.KARAMEL_SSH_PATH);
return generateAndStoreSshKeys(folder);
}
public static SshKeyPair generateAndStoreSshKeys(String clusterName) throws KaramelException {
File folder = new File(Settings.CLUSTER_SSH_PATH(clusterName));
return generateAndStoreSshKeys(folder);
}
public static SshKeyPair generateAndStoreSshKeys(File folder) throws KaramelException {
if (!folder.exists()) {
folder.mkdirs();
}
File pubFile = new File(folder, Settings.SSH_PUBKEY_FILENAME);
File priFile = new File(folder, Settings.SSH_PRIVKEY_FILENAME);
Map<String, String> keys = SshKeys.generate();
String pub = keys.get("public");
String pri = keys.get("private");
try {
FileOutputStream pubOut = new FileOutputStream(pubFile);
Writer out = new OutputStreamWriter(pubOut, "UTF8");
out.write(pub);
out.close();
} catch (IOException ex) {
logger.error("", ex);
}
try {
FileOutputStream priOut = new FileOutputStream(priFile);
Writer out = new OutputStreamWriter(priOut, "UTF8");
out.write(pri);
out.close();
} catch (IOException ex) {
logger.error("", ex);
}
if (System.getProperty("os.name").toLowerCase().indexOf("win") == -1) {
// public key permissions 644 (-rw-r--r--), private key 700 (drwx------)
Set<PosixFilePermission> privatePerms = new HashSet<>();
privatePerms.add(PosixFilePermission.OWNER_READ);
privatePerms.add(PosixFilePermission.OWNER_WRITE);
privatePerms.add(PosixFilePermission.OWNER_EXECUTE);
privatePerms.remove(PosixFilePermission.GROUP_READ);
privatePerms.remove(PosixFilePermission.GROUP_WRITE);
privatePerms.remove(PosixFilePermission.GROUP_EXECUTE);
privatePerms.remove(PosixFilePermission.OTHERS_READ);
privatePerms.remove(PosixFilePermission.OTHERS_WRITE);
privatePerms.remove(PosixFilePermission.OTHERS_EXECUTE);
try {
Files.setPosixFilePermissions(priFile.toPath(), privatePerms);
} catch (IOException ex) {
logger.error("Failed to set posix permissions on generated private ssh-key. ", ex);
throw new KaramelException(ex);
}
Set<PosixFilePermission> publicPerms = new HashSet<>();
publicPerms.add(PosixFilePermission.OWNER_READ);
publicPerms.add(PosixFilePermission.OWNER_WRITE);
publicPerms.remove(PosixFilePermission.OWNER_EXECUTE);
publicPerms.add(PosixFilePermission.GROUP_READ);
publicPerms.remove(PosixFilePermission.GROUP_EXECUTE);
publicPerms.remove(PosixFilePermission.GROUP_WRITE);
publicPerms.add(PosixFilePermission.OTHERS_READ);
publicPerms.remove(PosixFilePermission.OTHERS_EXECUTE);
publicPerms.remove(PosixFilePermission.OTHERS_WRITE);
try {
Files.setPosixFilePermissions(pubFile.toPath(), publicPerms);
} catch (IOException ex) {
logger.error("Failed to set posix permissions on generated public ssh-key. ", ex);
throw new KaramelException(ex);
}
}
SshKeyPair keyPair = new SshKeyPair();
keyPair.setPrivateKey(pri);
keyPair.setPublicKey(pub);
keyPair.setPrivateKeyPath(folder + File.separator + Settings.SSH_PRIVKEY_FILENAME);
keyPair.setPublicKeyPath(folder + File.separator + Settings.SSH_PUBKEY_FILENAME);
keyPair.setNeedsPassword(checkIfPasswordNeeded(keyPair));
return keyPair;
}
public static SshKeyPair loadSshKeys(Confs confs) throws SshKeysNotfoundException {
String pubkeyPath = confs.getProperty(Settings.SSH_PUBKEY_PATH_KEY);
String privKeyPath = confs.getProperty(Settings.SSH_PRIVKEY_PATH_KEY);
return loadSshKeys(pubkeyPath, privKeyPath, "");
}
public static SshKeyPair loadSshKeys(String pubkeyPath, String prikeyPath, String passphrase)
throws SshKeysNotfoundException {
String pubKey = null;
String priKey = null;
try {
BufferedReader r1;
r1 = new BufferedReader(new FileReader(new File(pubkeyPath)));
pubKey = r1.readLine();
r1.close();
try (Scanner scanner = new Scanner(new File(prikeyPath))) {
priKey = scanner.useDelimiter("\\A").next();
}
} catch (IOException ex) {
throw new SshKeysNotfoundException(String.format("Unsuccessful to load ssh keys from '%s' and/or '%s'",
pubkeyPath, prikeyPath), ex);
}
if (pubKey != null && priKey != null) {
SshKeyPair keypair = new SshKeyPair();
keypair.setPublicKeyPath(pubkeyPath);
keypair.setPublicKey(pubKey);
keypair.setPrivateKeyPath(prikeyPath);
keypair.setPrivateKey(priKey);
keypair.setPassphrase(passphrase);
keypair.setNeedsPassword(checkIfPasswordNeeded(keypair));
return keypair;
}
throw new SshKeysNotfoundException(String.format("Unsuccessful to load ssh keys from '%s' and/or '%s'",
pubkeyPath, prikeyPath));
}
}