diff --git a/api/src/com/cloud/api/commands/CreateSSHKeyPairCmd.java b/api/src/com/cloud/api/commands/CreateSSHKeyPairCmd.java new file mode 100644 index 00000000000..dc5dfb1b298 --- /dev/null +++ b/api/src/com/cloud/api/commands/CreateSSHKeyPairCmd.java @@ -0,0 +1,52 @@ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.SSHKeyPairResponse; +import com.cloud.user.SSHKeyPair; + +@Implementation(description="Create a new keypair and returns the private key", responseObject=SSHKeyPairResponse.class) +public class CreateSSHKeyPairCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreateSSHKeyPairCmd.class.getName()); + private static final String s_name = "createkeypairresponse"; + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="Name of the keypair") + private String name; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + SSHKeyPair r = _mgr.createSSHKeyPair(this); + SSHKeyPairResponse response = new SSHKeyPairResponse(r.getName(), r.getFingerprint(), r.getPrivateKey()); + response.setResponseName(getCommandName()); + response.setObjectName("keypair"); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/api/src/com/cloud/api/commands/DeleteSSHKeyPairCmd.java b/api/src/com/cloud/api/commands/DeleteSSHKeyPairCmd.java new file mode 100644 index 00000000000..47a0e24358e --- /dev/null +++ b/api/src/com/cloud/api/commands/DeleteSSHKeyPairCmd.java @@ -0,0 +1,50 @@ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.SuccessResponse; + +@Implementation(description="Deletes a keypair by name", responseObject=SuccessResponse.class) +public class DeleteSSHKeyPairCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreateSSHKeyPairCmd.class.getName()); + private static final String s_name = "deletekeypairresponse"; + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="Name of the keypair") + private String name; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteSSHKeyPair(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java index c3848f72cfd..8e0bfab0088 100644 --- a/api/src/com/cloud/api/commands/DeployVMCmd.java +++ b/api/src/com/cloud/api/commands/DeployVMCmd.java @@ -91,6 +91,9 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine") private List networkIds; + @Parameter(name="keypair", type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine") + private String sshKeyPairName; + // unexposed parameter needed for serializing/deserializing the command @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, expose=false) private String password; @@ -170,6 +173,10 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { return name; } + public String getSSHKeyPairName() { + return sshKeyPairName; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/com/cloud/api/commands/GetVMPasswordCmd.java b/api/src/com/cloud/api/commands/GetVMPasswordCmd.java new file mode 100644 index 00000000000..45ecafcba7a --- /dev/null +++ b/api/src/com/cloud/api/commands/GetVMPasswordCmd.java @@ -0,0 +1,54 @@ +package com.cloud.api.commands; + +import java.security.InvalidParameterException; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.GetVMPasswordResponse; + +@Implementation(responseObject=GetVMPasswordResponse.class, description="Returns an encrypted password for the VM") +public class GetVMPasswordCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(GetVMPasswordCmd.class.getName()); + private static final String s_name = "getvmpasswordresponse"; + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="The ID of the virtual machine") + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + String passwd = _mgr.getVMPassword(this); + if (passwd == null || passwd.equals("")) + throw new InvalidParameterException("No password for VM with id '" + getId() + "' found."); + + this.setResponseObject(new GetVMPasswordResponse(getCommandName(), passwd)); + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/api/src/com/cloud/api/commands/ListSSHKeyPairsCmd.java b/api/src/com/cloud/api/commands/ListSSHKeyPairsCmd.java new file mode 100644 index 00000000000..4db3355dabe --- /dev/null +++ b/api/src/com/cloud/api/commands/ListSSHKeyPairsCmd.java @@ -0,0 +1,71 @@ +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.SSHKeyPairResponse; +import com.cloud.user.SSHKeyPair; + +@Implementation(description="List registered keypairs", responseObject=SSHKeyPairResponse.class) +public class ListSSHKeyPairsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListSSHKeyPairsCmd.class.getName()); + private static final String s_name = "listsshkeypairsresponse"; + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=false, description="A key pair name to look for") + private String name; + + @Parameter(name="fingerprint", type=CommandType.STRING, required=false, description="A public key fingerprint to look for") + private String fingerprint; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getFingerprint() { + return fingerprint; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + List resultList = _mgr.listSSHKeyPairs(this); + List responses = new ArrayList(); + for (SSHKeyPair result : resultList) { + SSHKeyPairResponse r = new SSHKeyPairResponse(result.getName(), result.getFingerprint()); + r.setObjectName("keypair"); + responses.add(r); + } + + ListResponse response = new ListResponse(); + response.setResponses(responses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/api/src/com/cloud/api/commands/RegisterSSHKeyPairCmd.java b/api/src/com/cloud/api/commands/RegisterSSHKeyPairCmd.java new file mode 100644 index 00000000000..6ef442f3e58 --- /dev/null +++ b/api/src/com/cloud/api/commands/RegisterSSHKeyPairCmd.java @@ -0,0 +1,60 @@ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.SSHKeyPairResponse; +import com.cloud.user.SSHKeyPair; + +@Implementation(description="Register a public key in a keypair under a certain name", responseObject=SSHKeyPairResponse.class) +public class RegisterSSHKeyPairCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(RegisterSSHKeyPairCmd.class.getName()); + private static final String s_name = "registerkeypairresponse"; + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="Name of the keypair") + private String name; + + @Parameter(name="publickey", type=CommandType.STRING, required=true, description="Public key material of the keypair") + private String publicKey; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getPublicKey() { + return publicKey; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + SSHKeyPair result = _mgr.registerSSHKeyPair(this); + SSHKeyPairResponse response = new SSHKeyPairResponse(result.getName(), result.getFingerprint()); + response.setResponseName(getCommandName()); + response.setObjectName("keypair"); + this.setResponseObject(response); + } + + @Override + public String getCommandName() { + return s_name; + } + +} diff --git a/api/src/com/cloud/api/response/GetVMPasswordResponse.java b/api/src/com/cloud/api/response/GetVMPasswordResponse.java new file mode 100644 index 00000000000..1c826649fd2 --- /dev/null +++ b/api/src/com/cloud/api/response/GetVMPasswordResponse.java @@ -0,0 +1,27 @@ +package com.cloud.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class GetVMPasswordResponse extends BaseResponse { + + @SerializedName("encryptedpassword") @Param(description="The encrypted password of the VM") + private String encryptedPassword; + + public GetVMPasswordResponse() {} + + public GetVMPasswordResponse(String responseName, String encryptedPassword) { + setResponseName(responseName); + setObjectName("password"); + setEncryptedPassword(encryptedPassword); + } + + public String getEncryptedPassword() { + return encryptedPassword; + } + + public void setEncryptedPassword(String encryptedPassword) { + this.encryptedPassword = encryptedPassword; + } + +} diff --git a/api/src/com/cloud/api/response/SSHKeyPairResponse.java b/api/src/com/cloud/api/response/SSHKeyPairResponse.java new file mode 100644 index 00000000000..de103eda715 --- /dev/null +++ b/api/src/com/cloud/api/response/SSHKeyPairResponse.java @@ -0,0 +1,54 @@ +package com.cloud.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class SSHKeyPairResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) @Param(description="Name of the keypair") + private String name; + + @SerializedName("fingerprint") @Param(description="Fingerprint of the public key") + private String fingerprint; + + @SerializedName("privatekey") @Param(description="Private key") + private String privateKey; + + public SSHKeyPairResponse() {} + + public SSHKeyPairResponse(String name, String fingerprint) { + this(name, fingerprint, null); + } + + public SSHKeyPairResponse(String name, String fingerprint, String privateKey) { + this.name = name; + this.fingerprint = fingerprint; + this.privateKey = privateKey; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFingerprint() { + return fingerprint; + } + + public void setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + +} diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index 8ca4895b8a9..3b5f929e48e 100644 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -26,10 +26,13 @@ import java.util.Set; import com.cloud.alert.Alert; import com.cloud.api.ServerApiException; import com.cloud.api.commands.CreateDomainCmd; +import com.cloud.api.commands.CreateSSHKeyPairCmd; import com.cloud.api.commands.DeleteDomainCmd; import com.cloud.api.commands.DeletePreallocatedLunCmd; +import com.cloud.api.commands.DeleteSSHKeyPairCmd; import com.cloud.api.commands.ExtractVolumeCmd; import com.cloud.api.commands.GetCloudIdentifierCmd; +import com.cloud.api.commands.GetVMPasswordCmd; import com.cloud.api.commands.ListAccountsCmd; import com.cloud.api.commands.ListAlertsCmd; import com.cloud.api.commands.ListAsyncJobsCmd; @@ -50,6 +53,7 @@ import com.cloud.api.commands.ListPodsByCmd; import com.cloud.api.commands.ListPreallocatedLunsCmd; import com.cloud.api.commands.ListPublicIpAddressesCmd; import com.cloud.api.commands.ListRoutersCmd; +import com.cloud.api.commands.ListSSHKeyPairsCmd; import com.cloud.api.commands.ListServiceOfferingsCmd; import com.cloud.api.commands.ListStoragePoolsCmd; import com.cloud.api.commands.ListSystemVMsCmd; @@ -64,6 +68,7 @@ import com.cloud.api.commands.ListZonesByCmd; import com.cloud.api.commands.RebootSystemVmCmd; import com.cloud.api.commands.RegisterCmd; import com.cloud.api.commands.RegisterPreallocatedLunCmd; +import com.cloud.api.commands.RegisterSSHKeyPairCmd; import com.cloud.api.commands.StartSystemVMCmd; import com.cloud.api.commands.StopSystemVmCmd; import com.cloud.api.commands.UpdateDomainCmd; @@ -96,6 +101,7 @@ import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; +import com.cloud.user.SSHKeyPair; import com.cloud.user.UserAccount; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; @@ -406,4 +412,40 @@ public interface ManagementService { public Long saveCompletedEvent(Long userId, Long accountId, String level, String type, String description, long startEventId); + /** + * Search registered key pairs for the logged in user. + * @param cmd The api command class. + * @return The list of key pairs found. + */ + List listSSHKeyPairs(ListSSHKeyPairsCmd cmd); + + /** + * Registers a key pair for a given public key. + * @param cmd The api command class. + * @return A VO with the key pair name and a finger print for the public key. + */ + SSHKeyPair registerSSHKeyPair(RegisterSSHKeyPairCmd cmd); + + /** + * Creates a new + * @param cmd The api command class. + * @return A VO containing the key pair name, finger print for the public key + * and the private key material of the key pair. + */ + SSHKeyPair createSSHKeyPair(CreateSSHKeyPairCmd cmd); + + /** + * Deletes a key pair by name. + * @param cmd The api command class. + * @return True on success. False otherwise. + */ + boolean deleteSSHKeyPair(DeleteSSHKeyPairCmd cmd); + + /** + * Finds and returns an encrypted password for a VM. + * @param cmd The api command class. + * @return The encrypted password. + */ + String getVMPassword(GetVMPasswordCmd cmd); + } diff --git a/api/src/com/cloud/user/SSHKeyPair.java b/api/src/com/cloud/user/SSHKeyPair.java new file mode 100644 index 00000000000..205b3cc2ec5 --- /dev/null +++ b/api/src/com/cloud/user/SSHKeyPair.java @@ -0,0 +1,32 @@ +package com.cloud.user; + +import com.cloud.acl.ControlledEntity; + +public interface SSHKeyPair extends ControlledEntity { + + /** + * @return The id of the key pair. + */ + public long getId(); + + /** + * @return The given name of the key pair. + */ + public String getName(); + + /** + * @return The finger print of the public key. + */ + public String getFingerprint(); + + /** + * @return The public key of the key pair. + */ + public String getPublicKey(); + + /** + * @return The private key of the key pair. + */ + public String getPrivateKey(); + +} diff --git a/api/src/com/cloud/uservm/UserVm.java b/api/src/com/cloud/uservm/UserVm.java index b9589752d7f..9f7096c4b56 100755 --- a/api/src/com/cloud/uservm/UserVm.java +++ b/api/src/com/cloud/uservm/UserVm.java @@ -68,4 +68,10 @@ public interface UserVm extends VirtualMachine, ControlledEntity { Long getDomainRouterId(); void setUserData(String userData); + + String getEncryptedPassword(); + + Long getSSHKeyPairId(); + + String getSSHPublicKey(); } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index a0f2bb7b083..fd403afbcef 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -45,6 +45,7 @@ changeServiceForVirtualMachine=com.cloud.api.commands.UpgradeVMCmd;15 updateVirtualMachine=com.cloud.api.commands.UpdateVMCmd;15 recoverVirtualMachine=com.cloud.api.commands.RecoverVMCmd;3 listVirtualMachines=com.cloud.api.commands.ListVMsCmd;15 +getVMPassword=com.cloud.api.commands.GetVMPasswordCmd;15 #### snapshot commands createSnapshot=com.cloud.api.commands.CreateSnapshotCmd;15 @@ -247,3 +248,10 @@ listNetworkOfferings=com.cloud.api.commands.ListNetworkOfferingsCmd;15 createNetwork=com.cloud.api.commands.CreateNetworkCmd;15 deleteNetwork=com.cloud.api.commands.DeleteNetworkCmd;15 listNetworks=com.cloud.api.commands.ListNetworksCmd;15 + +#### SSH key pair commands +registerSSHKeyPair=com.cloud.api.commands.RegisterSSHKeyPairCmd;15 +createSSHKeyPair=com.cloud.api.commands.CreateSSHKeyPairCmd;15 +deleteSSHKeyPair=com.cloud.api.commands.DeleteSSHKeyPairCmd;15 +listSSHKeyPairs=com.cloud.api.commands.ListSSHKeyPairsCmd;15 + diff --git a/cloud.spec b/cloud.spec index 386e0beea41..f74a7ef541e 100644 --- a/cloud.spec +++ b/cloud.spec @@ -484,6 +484,9 @@ fi %{_javadir}/%{name}-commons-discovery.jar %{_javadir}/%{name}-iControl.jar %{_javadir}/%{name}-wsdl4j.jar +%{_javadir}/%{name}-bcprov-jdk16-1.45.jar +%{_javadir}/%{name}-jsch-0.1.42.jar + %files core %defattr(0644,root,root,0755) diff --git a/core/src/com/cloud/user/SSHKeyPairVO.java b/core/src/com/cloud/user/SSHKeyPairVO.java new file mode 100644 index 00000000000..bfec3bc3830 --- /dev/null +++ b/core/src/com/cloud/user/SSHKeyPairVO.java @@ -0,0 +1,101 @@ +package com.cloud.user; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Transient; + +@Entity +@Table(name="ssh_keypairs") +public class SSHKeyPairVO implements SSHKeyPair { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private Long id = null; + + @Column(name="account_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="keypair_name") + private String name; + + @Column(name="fingerprint") + private String fingerprint; + + @Column(name="public_key") + private String publicKey; + + @Transient + private String privateKey; + + @Override + public long getId() { + return id; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public String getFingerprint() { + return fingerprint; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getPublicKey() { + return publicKey; + } + + @Override + public String getPrivateKey() { + return privateKey; + } + + public void setId(Long id) { + this.id = id; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public void setName(String name) { + this.name = name; + } + + public void setFingerprint(String fingerprint) { + this.fingerprint = fingerprint; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + +} diff --git a/core/src/com/cloud/vm/UserVmVO.java b/core/src/com/cloud/vm/UserVmVO.java index 40ac9912bda..b0cf27361b2 100755 --- a/core/src/com/cloud/vm/UserVmVO.java +++ b/core/src/com/cloud/vm/UserVmVO.java @@ -64,8 +64,17 @@ public class UserVmVO extends VMInstanceVO implements UserVm { @Column(name="display_name", updatable=true, nullable=true) private String displayName; + @Column(name="encrypted_password", updatable=true, nullable=true) + private String encryptedPassword; + + @Column(name="ssh_keypair_id", updatable=true, nullable=true) + private Long sshKeyPairId; + + @Column(name="ssh_public_key", updatable=true, nullable=true) + private String sshPublicKey; + transient String password; - + public String getPassword() { return password; } @@ -75,6 +84,33 @@ public class UserVmVO extends VMInstanceVO implements UserVm { } @Override + public String getSSHPublicKey() { + return sshPublicKey; + } + + public void setSSHPublicKey(String publicKey) { + this.sshPublicKey = publicKey; + } + + @Override + public String getEncryptedPassword() { + return encryptedPassword; + } + + @Override + public Long getSSHKeyPairId() { + return sshKeyPairId; + } + + public void setEncryptedPassword(String encryptedPassword) { + this.encryptedPassword = encryptedPassword; + } + + public void setSSHKeyPairId(Long sshKeyPairId) { + this.sshKeyPairId = sshKeyPairId; + } + + @Override public String getGuestIpAddress() { return guestIpAddress; } @@ -182,6 +218,24 @@ public class UserVmVO extends VMInstanceVO implements UserVm { this.isoId = null; this.displayName = displayName; } + + public UserVmVO(long id, + String instanceName, + String displayName, + long templateId, + long guestOsId, + boolean haEnabled, + long domainId, + long accountId, + long serviceOfferingId, + String userData, + String name, + Long sshKeyPairId, + String sshPublicKey) { + this(id, instanceName, displayName, templateId, guestOsId, haEnabled, domainId, accountId, serviceOfferingId, userData, name); + this.sshKeyPairId = sshKeyPairId; + this.sshPublicKey = sshPublicKey; + } protected UserVmVO() { super(); diff --git a/deps/cloud-bcprov-jdk16-1.45.jar b/deps/cloud-bcprov-jdk16-1.45.jar new file mode 100644 index 00000000000..38685d51051 Binary files /dev/null and b/deps/cloud-bcprov-jdk16-1.45.jar differ diff --git a/deps/cloud-jsch-0.1.42.jar b/deps/cloud-jsch-0.1.42.jar new file mode 100644 index 00000000000..c65eff0954f Binary files /dev/null and b/deps/cloud-jsch-0.1.42.jar differ diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 04a13bfb1ee..be8cd7b5db5 100644 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -110,6 +110,7 @@ import com.cloud.storage.upload.UploadMonitorImpl; import com.cloud.template.TemplateManagerImpl; import com.cloud.user.AccountManagerImpl; import com.cloud.user.dao.AccountDaoImpl; +import com.cloud.user.dao.SSHKeyPairDaoImpl; import com.cloud.user.dao.UserAccountDaoImpl; import com.cloud.user.dao.UserDaoImpl; import com.cloud.user.dao.UserStatisticsDaoImpl; @@ -234,6 +235,7 @@ public class DefaultComponentLibrary implements ComponentLibrary { addDao("ItWorkDao", ItWorkDaoImpl.class); addDao("FirewallRulesDao", FirewallRulesDaoImpl.class); addDao("PortForwardingRulesDao", PortForwardingRulesDaoImpl.class); + addDao("SSHKeyPairDao", SSHKeyPairDaoImpl.class); addDao("UsageEventDao", UsageEventDaoImpl.class); addDao("ClusterDetailsDao", ClusterDetailsDaoImpl.class); } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 615adc0bd56..7fedecfac4a 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -915,7 +915,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } private VmDataCommand generateVmDataCommand(VirtualRouter router, String vmPrivateIpAddress, - String userData, String serviceOffering, String zoneName, String guestIpAddress, String vmName, String vmInstanceName, long vmId) { + String userData, String serviceOffering, String zoneName, String guestIpAddress, String vmName, String vmInstanceName, long vmId, String publicKey) { VmDataCommand cmd = new VmDataCommand(vmPrivateIpAddress); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); @@ -930,6 +930,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmd.addVmData("metadata", "public-hostname", router.getPublicIpAddress()); cmd.addVmData("metadata", "instance-id", vmInstanceName); cmd.addVmData("metadata", "vm-id", String.valueOf(vmId)); + cmd.addVmData("metadata", "public-keys", publicKey); return cmd; } @@ -1453,7 +1454,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmds.addCommand( "vmdata", generateVmDataCommand(router, nic.getIp4Address(), userData, serviceOffering, zoneName, - nic.getIp4Address(), profile.getVirtualMachine().getName(), profile.getVirtualMachine().getInstanceName(), profile.getId())); + nic.getIp4Address(), profile.getVirtualMachine().getName(), profile.getVirtualMachine().getInstanceName(), profile.getId(), profile.getVirtualMachine().getSSHPublicKey())); try { _agentMgr.send(router.getHostId(), cmds); @@ -1681,7 +1682,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmds.addCommand( "vmdata", generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, - nic.getIp4Address(), vm.getName(), vm.getInstanceName(), vm.getId())); + nic.getIp4Address(), vm.getName(), vm.getInstanceName(), vm.getId(), null)); } } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 20944dcd134..8d71bc64264 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -75,10 +75,13 @@ import com.cloud.api.ApiDBUtils; import com.cloud.api.BaseCmd; import com.cloud.api.ServerApiException; import com.cloud.api.commands.CreateDomainCmd; +import com.cloud.api.commands.CreateSSHKeyPairCmd; import com.cloud.api.commands.DeleteDomainCmd; import com.cloud.api.commands.DeletePreallocatedLunCmd; +import com.cloud.api.commands.DeleteSSHKeyPairCmd; import com.cloud.api.commands.ExtractVolumeCmd; import com.cloud.api.commands.GetCloudIdentifierCmd; +import com.cloud.api.commands.GetVMPasswordCmd; import com.cloud.api.commands.ListAccountsCmd; import com.cloud.api.commands.ListAlertsCmd; import com.cloud.api.commands.ListAsyncJobsCmd; @@ -99,6 +102,7 @@ import com.cloud.api.commands.ListPodsByCmd; import com.cloud.api.commands.ListPreallocatedLunsCmd; import com.cloud.api.commands.ListPublicIpAddressesCmd; import com.cloud.api.commands.ListRoutersCmd; +import com.cloud.api.commands.ListSSHKeyPairsCmd; import com.cloud.api.commands.ListServiceOfferingsCmd; import com.cloud.api.commands.ListStoragePoolsCmd; import com.cloud.api.commands.ListSystemVMsCmd; @@ -112,6 +116,7 @@ import com.cloud.api.commands.ListZonesByCmd; import com.cloud.api.commands.RebootSystemVmCmd; import com.cloud.api.commands.RegisterCmd; import com.cloud.api.commands.RegisterPreallocatedLunCmd; +import com.cloud.api.commands.RegisterSSHKeyPairCmd; import com.cloud.api.commands.StartSystemVMCmd; import com.cloud.api.commands.StopSystemVmCmd; import com.cloud.api.commands.UpdateDomainCmd; @@ -222,12 +227,15 @@ import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; +import com.cloud.user.SSHKeyPair; +import com.cloud.user.SSHKeyPairVO; import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.user.UserAccountVO; import com.cloud.user.UserContext; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.EnumUtils; @@ -249,6 +257,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.net.MacAddress; import com.cloud.utils.net.NetUtils; +import com.cloud.utils.ssh.SSHKeysHelper; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroupVO; @@ -320,6 +329,8 @@ public class ManagementServerImpl implements ManagementServer { private final UploadMonitor _uploadMonitor; private final UploadDao _uploadDao; private final CertificateDao _certDao; + private final SSHKeyPairDao _sshKeyPairDao; + private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); @@ -391,6 +402,7 @@ public class ManagementServerImpl implements ManagementServer { _asyncMgr = locator.getManager(AsyncJobManager.class); _tmpltMgr = locator.getManager(TemplateManager.class); _uploadMonitor = locator.getManager(UploadMonitor.class); + _sshKeyPairDao = locator.getDao(SSHKeyPairDao.class); _userAuthenticators = locator.getAdapters(UserAuthenticator.class); if (_userAuthenticators == null || !_userAuthenticators.isSet()) { @@ -4663,4 +4675,88 @@ public class ManagementServerImpl implements ManagementServer { } return _hashKey; } + + @Override + public SSHKeyPair createSSHKeyPair(CreateSSHKeyPairCmd cmd) { + Account account = UserContext.current().getCaller(); + SSHKeyPairVO s = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getName()); + if (s != null) + throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' already exists."); + + SSHKeysHelper keys = new SSHKeysHelper(); + + String name = cmd.getName(); + String publicKey = keys.getPublicKey(); + String fingerprint = keys.getPublicKeyFingerPrint(); + String privateKey = keys.getPrivateKey(); + + return createAndSaveSSHKeyPair(name, fingerprint, publicKey, privateKey); + } + + @Override + public boolean deleteSSHKeyPair(DeleteSSHKeyPairCmd cmd) { + Account account = UserContext.current().getCaller(); + SSHKeyPairVO s = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getName()); + if (s == null) + throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist."); + + return _sshKeyPairDao.deleteByName(account.getAccountId(), account.getDomainId(), cmd.getName()); + } + + @Override + public List listSSHKeyPairs(ListSSHKeyPairsCmd cmd) { + Account account = UserContext.current().getCaller(); + + if (cmd.getName() != null && cmd.getName().length() > 0) + return _sshKeyPairDao.listKeyPairsByName(account.getAccountId(), account.getDomainId(), cmd.getName()); + + if (cmd.getFingerprint() != null && cmd.getFingerprint().length() > 0) + return _sshKeyPairDao.listKeyPairsByFingerprint(account.getAccountId(), account.getDomainId(), cmd.getFingerprint()); + + return _sshKeyPairDao.listKeyPairs(account.getAccountId(), account.getDomainId()); + } + + @Override + public SSHKeyPair registerSSHKeyPair(RegisterSSHKeyPairCmd cmd) { + Account account = UserContext.current().getCaller(); + SSHKeyPairVO s = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getName()); + if (s != null) + throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' already exists."); + + String name = cmd.getName(); + String publicKey = SSHKeysHelper.getPublicKeyFromKeyMaterial(cmd.getPublicKey()); + String fingerprint = SSHKeysHelper.getPublicKeyFingerprint(publicKey); + + if (publicKey == null) + throw new InvalidParameterValueException("Public key is invalid"); + + return createAndSaveSSHKeyPair(name, fingerprint, publicKey, null); + } + + private SSHKeyPair createAndSaveSSHKeyPair(String name, String fingerprint, String publicKey, String privateKey) { + Account account = UserContext.current().getCaller(); + SSHKeyPairVO newPair = new SSHKeyPairVO(); + + newPair.setAccountId(account.getAccountId()); + newPair.setDomainId(account.getDomainId()); + newPair.setName(name); + newPair.setFingerprint(fingerprint); + newPair.setPublicKey(publicKey); + newPair.setPrivateKey(privateKey); // transient; not saved. + + _sshKeyPairDao.persist(newPair); + + return newPair; + } + + @Override + public String getVMPassword(GetVMPasswordCmd cmd) { + Account account = UserContext.current().getCaller(); + UserVmVO vm = _userVmDao.findById(cmd.getId()); + if (vm == null || vm.getEncryptedPassword() == null || vm.getEncryptedPassword().equals("") || vm.getAccountId() != account.getAccountId()) + throw new InvalidParameterValueException("No password for VM with id '" + getId() + "' found."); + + return vm.getEncryptedPassword(); + } + } diff --git a/server/src/com/cloud/user/dao/SSHKeyPairDao.java b/server/src/com/cloud/user/dao/SSHKeyPairDao.java new file mode 100644 index 00000000000..7f7aece27d2 --- /dev/null +++ b/server/src/com/cloud/user/dao/SSHKeyPairDao.java @@ -0,0 +1,20 @@ +package com.cloud.user.dao; + +import java.util.List; + +import com.cloud.user.SSHKeyPairVO; +import com.cloud.utils.db.GenericDao; + +public interface SSHKeyPairDao extends GenericDao { + + public List listKeyPairs(long accountId, long domainId); + + public List listKeyPairsByName(long accountId, long domainId, String name); + + public List listKeyPairsByFingerprint(long accountId, long domainId, String fingerprint); + + public SSHKeyPairVO findByName(long accountId, long domainId, String name); + + public boolean deleteByName(long accountId, long domainId, String name); + +} diff --git a/server/src/com/cloud/user/dao/SSHKeyPairDaoImpl.java b/server/src/com/cloud/user/dao/SSHKeyPairDaoImpl.java new file mode 100644 index 00000000000..1f2ff899e70 --- /dev/null +++ b/server/src/com/cloud/user/dao/SSHKeyPairDaoImpl.java @@ -0,0 +1,59 @@ +package com.cloud.user.dao; + +import java.util.List; + +import javax.ejb.Local; + +import com.cloud.user.SSHKeyPairVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchCriteria; + +@Local(value={SSHKeyPairDao.class}) +public class SSHKeyPairDaoImpl extends GenericDaoBase implements SSHKeyPairDao { + + @Override + public List listKeyPairs(long accountId, long domainId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + return listBy(sc); + } + + @Override + public List listKeyPairsByName(long accountId, long domainId, String name) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + sc.addAnd("name", SearchCriteria.Op.EQ, name); + return listBy(sc); + } + + @Override + public List listKeyPairsByFingerprint(long accountId, long domainId, String fingerprint) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + sc.addAnd("fingerprint", SearchCriteria.Op.EQ, fingerprint); + return listBy(sc); + } + + @Override + public SSHKeyPairVO findByName(long accountId, long domainId, String name) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + sc.addAnd("name", SearchCriteria.Op.EQ, name); + return findOneBy(sc); + } + + @Override + public boolean deleteByName(long accountId, long domainId, String name) { + SSHKeyPairVO pair = findByName(accountId, domainId, name); + if (pair == null) + return false; + + expunge(pair.getId()); + return true; + } + +} diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 7afa42ecdb2..8028efe8f4b 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -171,10 +171,12 @@ import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountService; import com.cloud.user.AccountVO; +import com.cloud.user.SSHKeyPair; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; @@ -186,6 +188,7 @@ import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.crypt.RSAHelper; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GlobalLock; @@ -260,6 +263,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Inject RulesManager _rulesMgr; @Inject LoadBalancingRulesManager _lbMgr; @Inject UsageEventDao _usageEventDao; + @Inject SSHKeyPairDao _sshKeyPairDao; private IpAddrAllocator _IpAllocator; ScheduledExecutorService _executor = null; @@ -2232,6 +2236,19 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } } + // Find an SSH public key corresponding to the key pair name, if one is given + Long sshKeyPairId = null; + String sshPublicKey = null; + if (cmd.getSSHKeyPairName() != null && !cmd.getSSHKeyPairName().equals("")) { + Account account = UserContext.current().getCaller(); + SSHKeyPair pair = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getSSHKeyPairName()); + if (pair == null) + throw new InvalidParameterValueException("A key pair with name '" + cmd.getSSHKeyPairName() + "' was not found."); + + sshKeyPairId = pair.getId(); + sshPublicKey = pair.getPublicKey(); + } + _accountMgr.checkAccess(caller, template); DataCenterDeployment plan = new DataCenterDeployment(dc.getId()); @@ -2288,7 +2305,8 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), - template.getId(), template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), userData, hostName); + template.getId(), template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), + userData, hostName, sshKeyPairId, sshPublicKey); if (_itMgr.allocate(vm, template, offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, cmd.getHypervisor(), owner) == null) { @@ -2335,6 +2353,16 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); } vm.setPassword(password); + + // Check if an SSH key pair was selected for the instance and if so use it to encrypt & save the vm password + if (vm.getSSHKeyPairId() != null && vm.getSSHPublicKey() != null && password != null && !password.equals("saved_password") ) { + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(vm.getSSHPublicKey(), password); + if (encryptedPasswd == null) + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Error encrypting password"); + + vm.setEncryptedPassword(encryptedPasswd); + _vmDao.update(vm.getId(), vm); + } long userId = UserContext.current().getCallerUserId(); UserVO caller = _userDao.findById(userId); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index c310221e626..1d5436969b2 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -762,6 +762,7 @@ CREATE TABLE `cloud`.`user_vm` ( `user_data` varchar(2048), `encrypted_password` varchar(1024) COMMENT 'vm password encrypted with the public key referenced in ssh_keypair', `ssh_keypair_id` bigint unsigned COMMENT 'id of the ssh keypair used to access the vm and/or encrypt the password', + `ssh_public_key` varchar(5120) COMMENT 'ssh public key', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/utils/src/com/cloud/utils/crypt/RSAHelper.java b/utils/src/com/cloud/utils/crypt/RSAHelper.java new file mode 100644 index 00000000000..9d404063acd --- /dev/null +++ b/utils/src/com/cloud/utils/crypt/RSAHelper.java @@ -0,0 +1,61 @@ +package com.cloud.utils.crypt; + +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.SecureRandom; +import java.security.Security; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.KeySpec; +import java.security.spec.RSAPublicKeySpec; + +import javax.crypto.Cipher; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class RSAHelper { + + private static RSAPublicKey readKey(String key) throws Exception { + byte[] encKey = Base64.decodeBase64(key.split(" ")[1]); + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(encKey)); + + byte[] header = readElement(dis); + String pubKeyFormat = new String(header); + if (!pubKeyFormat.equals("ssh-rsa")) + throw new RuntimeException("Unsupported format"); + + byte[] publicExponent = readElement(dis); + byte[] modulus = readElement(dis); + + KeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(spec); + + return pubKey; + } + + private static byte[] readElement(DataInput dis) throws IOException { + int len = dis.readInt(); + byte[] buf = new byte[len]; + dis.readFully(buf); + return buf; + } + + public static String encryptWithSSHPublicKey(String sshPublicKey, String content) { + String returnString = null; + try { + Security.addProvider(new BouncyCastleProvider()); + RSAPublicKey publicKey = readKey(sshPublicKey); + Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", BouncyCastleProvider.PROVIDER_NAME); + cipher.init(Cipher.ENCRYPT_MODE, publicKey , new SecureRandom()); + byte[] encrypted = cipher.doFinal(content.getBytes()); + returnString = Base64.encodeBase64String(encrypted); + } catch (Exception e) {} + + return returnString; + } +} diff --git a/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java b/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java new file mode 100644 index 00000000000..f0d5cc40fca --- /dev/null +++ b/utils/src/com/cloud/utils/ssh/SSHKeysHelper.java @@ -0,0 +1,90 @@ +package com.cloud.utils.ssh; + +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.KeyPair; +import com.jcraft.jsch.JSch; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.binary.Base64; + +public class SSHKeysHelper { + + private KeyPair keyPair; + private static final char[] hexChars = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + + private static String toHexString(byte[] b) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + sb.append(hexChars[ (int)(((int)b[i] >> 4) & 0x0f)]); + sb.append(hexChars[ (int)(((int)b[i]) & 0x0f)]); + } + return sb.toString(); + } + + public SSHKeysHelper() { + try { + keyPair = KeyPair.genKeyPair(new JSch(), KeyPair.RSA); + } catch (JSchException e) { + e.printStackTrace(); + } + } + + public String getPublicKeyFingerPrint() { + return getPublicKeyFingerprint(getPublicKey()); + } + + public static String getPublicKeyFingerprint(String publicKey) { + String key[] = publicKey.split(" "); + byte[] keyBytes = Base64.decodeBase64(key[1]); + + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + String sumString = toHexString(md5.digest(keyBytes)); + String rString = ""; + + for (int i = 2; i <= sumString.length(); i += 2) { + rString += sumString.substring(i-2, i); + if (i != sumString.length()) + rString += ":"; + } + + return rString; + } + + public static String getPublicKeyFromKeyMaterial(String keyMaterial) { + if (!keyMaterial.contains(" ")) + keyMaterial = new String(Base64.decodeBase64(keyMaterial.getBytes())); + + if (!keyMaterial.startsWith("ssh-rsa") || !keyMaterial.contains(" ")) + return null; + + String[] key = keyMaterial.split(" "); + if (key.length < 2) + return null; + + return key[0].concat(" ").concat(key[1]); + } + + public String getPublicKey() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + keyPair.writePublicKey(baos, ""); + + return baos.toString(); + } + + public String getPrivateKey() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + keyPair.writePrivateKey(baos); + + return baos.toString(); + } + +}