Java Secure Channel(JSch)
업데이트:
Java Secure Channel(JSch)
- 순수 SSH2의 Java 구현체로, sshd 서버에 연결하여 포트 포워딩, X11 포워딩, 파일 전송 등을 사용할 수 있다.
Connect
- 서버에 접속하기 위해 사용되는 사용자 인증 방식은 password, publickey, keyboard-interactive, gssapi-with-mic이 있다.
password
- 서버에 등록된 계정으로 로그인하는 방식이다.
publickey
- SSH Key를 이용하여 로그인하는 방식이다.
- DSA, RSA, ECDSA 기반으로 암호화된 Public key를 서버에 등록하여 Local에 보관하고 있는 Private key를 이용하여 로그인한다.
ssh-keygen
- OpenSSH에 포함된 ssh key-pair를 만들기 위한 도구이다.
사용
키 생성
키 생성 알고리즘 지정
C:\Users\GracefulSoul>ssh-keygen -t rsa
Generating public/private rsa key pair.
- ssh-keygen 명령어를 통해 키 생성이 가능하다.
- -t 키워드를 통해 rsa, dsa, esdsa 등의 암호화 알고리즘을 활용하여 키 생성이 가능하다.
키 생성 위치 지정
Enter file in which to save the key (C:\Users\GracefulSoul/.ssh/id_rsa):
Created directory 'C:\Users\GracefulSoul/.ssh'.
- 키는 기본 생성 위치를 지정하여 생성이 가능하다.
- Linux : /home/{userName}/.ssh/
- Window : C:\Users{userName}/.ssh/
키 passpharse 입력
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\GracefulSoul/.ssh/id_rsa.
Your public key has been saved in C:\Users\GracefulSoul/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:EtIy5fyPVizDk0KYA+3x83b1HPFyNfem2cuGQDb5cn0 gracefulsoul@GracefulSoul-PC
The key's randomart image is:
+---[RSA 2048]----+
| .. . |
| .oB ..o|
| .Bo* . o=|
| .*o+ o =. o =|
| ooS =.oo.O |
| ooB.o o=.E|
| .o.. + o o|
| . . + |
| . |
+----[SHA256]-----+
- 키에 대한 보안성을 위해 Passphrase를 사용 할 수 있으며, 생성된 키에 대한 암복호화에 사용되는 키워드로 생각하면된다.
키 등록
Publickey 확인
C:\Users\GracefulSoul>type .\.ssh\id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9lcezehweKlB9WEp6vRmVgqwBVuFK/i3jTbkq3U/GW0VcN0Jgct104usL/hZjBFr9/eizwyePnmmYOWs9O90/Tr6tQN3pJCxHpvwZIED9L+pQNyK5Sa6Mfs63q1WWmndXxznouW17KYEfbPZPAzSVsklqkutecVVDYt8kotkID/HrqakOTa0SeAvUWXyhLW8/hCjMFD4dr+MmYWEf6kZ0xnAYK/DPHa3afp6sb7NBjc+baptw2HYFIUmBfZyToZ6Vy7oRb/2dT8fimP6V6C6vov0UcXlQYkE1oVSMxNYY4f0Ct/2Jw1R7flI4jz054SaL3WJRM7xxrshHFNSDnMw/ gracefulsoul@GracefulSoul-PC
- 1-2에서 설명한 키 생성 위치에 저장된 Public key 파일을 확인하고, 복사한다.
접속하려는 서버에 Publickey 등록
$ cat /home/gracefulsoul/id_rsa.pub >> /home/gracefulsoul/.ssh/authorized_keys
- 2-1의 Public key 내용을 서버의 접속하려는 계정의 ssh 설정을 추가한다.
- authorized_keys에 등록된 Public key는 서버에 Private Key 기반으로 접속하려는 시도가 있으면 해당 File을 통해서 인증을 수행한다.
- 위의 설정은 ‘~/.ssh/sshd_config’에 기본 설정이 되어있다.
keyboard-interactive
- 임의 순서의 요청 및 응답을 사용하는 유연한 인증 방법으로, 올바른 응답을 요청하여 로그인하는 방법이다.
- OTP 기반 로그인 등으로 사용한다.
gssapi-with-mic
- 일반적으로 Kerberos의 인증방식을 이용하여 로그인하는 방법이다.
예제
public class JschConnector {
/* ... */
public void connect(InstanceVo instanceVo) {
JSch jsch = new JSch();
try {
// If you use privateKey. It is safer than using password.
if (StringUtils.isNotBlank(instanceVo.getPrivateKey())) {
this.initJschIdentity(jsch, instanceVo);
}
this.connectSession(jsch, instanceVo);
this.connectChannel();
} catch (JSchException jschException) {
jschException.printStackTrace();
this.disconnect();
}
}
private void initJschIdentity(JSch jsch, InstanceVo instanceVo) throws JSchException {
if (StringUtils.isNotBlank(instanceVo.getPassphrase())) {
// If you used passphrase to create a privateKey.
jsch.addIdentity(instanceVo.getPrivateKey(), instanceVo.getPassphrase());
} else {
// If you did not use passphrase when creating a privateKey.
jsch.addIdentity(instanceVo.getPrivateKey());
}
}
/* ... */
- connect() 메서드는 JSch 라이브러리를 이용하여 연결하는 코드이다.
- Private key 기반의 로그인 방식을 사용하는 경우, initJschIdentity() 메서드 내용과 같이 Passphrase 유무에 따라 Identity 설정을 같이 해주어야 한다.
/* ... */
private void connectSession(JSch jsch, InstanceVo instanceVo) throws JSchException {
this.session = jsch.getSession(instanceVo.getUserName(), instanceVo.getHost(), instanceVo.getPort());
// If privateKey is not entered, use it as a password-based login.
if (StringUtils.isBlank(instanceVo.getPrivateKey())) {
this.session.setPassword(instanceVo.getPassword());
}
this.session.setConfig("StrictHostKeyChecking", "no");
this.session.connect();
}
- 세션을 연결하는 경우 Private key 기반의 로그인 시도가 아니면, Password 기반의 로그인 시도로 인식하도록 하였다.
- 세션의 “StrictHostKeyChecking” 설정을 “no”로 한 이유는, 서버 측에 등록된 Host가 아니어도 엄격하게 확인하지 않도록 한 것이다.
- 만일 Host를 등록하여 엄격하게 확인하여야 한다면 “yes” 혹은 설정을 하지 않으면 된다.
- 일정 기간 연결하여 반복 사용이 될 경우, connect(int connectTimeout) 메서드를 사용하도록 한다. (단, 보안성에 위배되지 않도록 사용하도록 한다.)
/* ... */
private void connectChannel() throws JSchException {
this.channel = (ChannelSftp) this.session.openChannel("sftp");
this.channel.connect();
}
/* ... */
- 최종으로 sftp 테스트를 진행 할 예정이므로, sftp 채널로 연결한다.
- 일정 기간 연결하여 반복 사용이 될 경우, connect(int connectTimeout) 메서드를 사용하도록 한다. (단, 보안성에 위배되지 않도록 사용하도록 한다.)
SFTP(Secure File transfer protocol)
- PC와 서버 간의 데이터 전송을 암호화하여 해킹이나 보안상의 문제점을 방지하는 안전한 서비스를 이용할 수 있다.
예제
/* ... */
public boolean check(ActionVo actionVo) {
try {
return null != this.channel.stat(String.join(File.separator, actionVo.getTargetDir(), actionVo.getFileName()));
} catch (SftpException e) {
e.printStackTrace();
return false;
}
}
/* ... */
- Sftp의 stat(String path) 메서드를 활용하여 해당 위치에 있는 파일이 존재하는지 확인하는 코드이다.
/* ... */
public void download(ActionVo actionVo) {
try {
this.channel.cd(actionVo.getTargetDir());
FileUtils.mkdirIfNotExists(actionVo.getDestinationDir());
this.copy(actionVo);
} catch (SftpException e) {
e.printStackTrace();
}
}
private void copy(ActionVo actionVo) throws SftpException {
try (InputStream inputStream = this.channel.get(actionVo.getFileName());
OutputStream outputStream = new FileOutputStream(new File(actionVo.getDestinationDir(), actionVo.getFileName()))) {
FileUtils.copy(inputStream, outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/* ... */
- Sftp의 cd(String path) 메서드와 get(String src) 메서드로 지정된 위치에 있는 파일을 가져와 목표 지점에 복사하는 코드이다.
/* ... */
public void upload(ActionVo actionVo) {
File file = new File(actionVo.getTargetDir(), actionVo.getFileName());
try (InputStream InputStream = new FileInputStream(file)) {
FileUtils.mkdirIfNotExists(actionVo.getDestinationDir());
this.channel.cd(actionVo.getDestinationDir());
this.channel.put(InputStream, actionVo.getFileName());
} catch (IOException e) {
e.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
}
}
/* ... */
- Sftp의 cd(String path) 메서드와 put(InputStream src, String dst) 메서드로 지정된 위치에 파일을 저장하는 코드이다.
Disconnect
- 사용하지 않는 경우에는 반드시 Session과 Channel을 닫아주어야 한다.
예제
public class JschConnector {
/* ... */
public void disconnect() {
this.disconnectSession();
this.disconnectChannel();
}
private void disconnectSession() {
if (this.session != null && this.session.isConnected()) {
this.session.disconnect();
}
}
private void disconnectChannel() {
if (this.channel != null && this.channel.isConnected()) {
this.channel.disconnect();
}
}
}
- 만일 위에서 일정 기간 연결하여 반복 사용하는 경우, 반드시 필요한 경우에만 disconnect 시켜주어 연결의 지속성을 확보한다. (단, 보안성에 위배되지 않도록 사용하도록 한다.)
Reference
※ Sample Code는 여기에서 확인 가능합니다.
댓글남기기