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는 여기에서 확인 가능합니다.

댓글남기기