리눅스

Total 331
Today 0
profile_image
슈퍼아이피
26-08-08 21:47 0개 4회
TIP
Rocky Linux 8.10에서 SSH ssh-rsa(SHA-1) 인증 테스트 및 JSch 연동 정리

배경

레거시 시스템(구형 JSch 라이브러리 등)과의 SFTP 연동을 앞두고, 대상 서버(Rocky Linux, OpenSSH 8.0)가 ssh-rsa(SHA-1 서명) 방식의 공개키 인증을 실제로 지원하는지 검증이 필요했다. 단순히 되나 안 되나를 확인하는 걸 넘어서, OS의 crypto-policy가 SSH 프로토콜 협상에 어떻게 관여하는지까지 확인하게 된 과정을 정리한다.


1. ssh-rsa(SHA-1)와 rsa-sha2-*의 차이

SSH 키 자체에 "SHA-1 키"라는 종류가 따로 있는 게 아니다. RSA 키 하나로 접속 시 서명 알고리즘을 아래 중 무엇으로 쓸지가 매 연결마다 협상된다.

  • ssh-rsa — RSA + SHA-1 서명 (레거시)
  • rsa-sha2-256 / rsa-sha2-512 — RSA + SHA-2 서명 (권장)

OpenSSH 클라이언트는 기본적으로 가장 안전한 알고리즘을 자동 선택하므로, -i 옵션으로 RSA 키만 지정해서 접속하면 서버가 ssh-rsa를 지원하더라도 실제로는 rsa-sha2-512로 인증이 이뤄진다. 순수 SHA-1 협상을 강제하려면 명시적 옵션이 필요하다.

# 이렇게 하면 SHA-2로 자동 승격되어 SHA-1 테스트가 안 됨
ssh -i id_rsa_sha1test user@host

# 서명 알고리즘 목록을 통째로 교체(=)해야 SHA-1만 남는다 (+ 는 추가일 뿐 우선순위상 SHA-2가 먼저 시도됨)
ssh -vvv -o PubkeyAcceptedAlgorithms=ssh-rsa -i id_rsa_sha1test user@host

-vvv 로그에서 아래 줄로 실제 사용된 서명 알고리즘을 확인할 수 있다.

debug3: sign_and_send_pubkey: signing using ssh-rsa SHA256:...

2. 테스트 절차 요약

2-1. 서버 지원 여부 확인

sudo sshd -T | grep -i pubkeyacceptedalgorithms

2-2. 테스트용 RSA 키 생성 및 배포

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_sha1test
ssh-copy-id -i ~/.ssh/id_rsa_sha1test.pub user@host

ssh-copy-id 결과에서 Number of key(s) added: 1이 나오면 공개키가 authorized_keys에 정상 등록된 것이다.

2-3. SHA-1 강제 접속 테스트

ssh -vvv \
  -o PubkeyAcceptedAlgorithms=ssh-rsa \
  -o HostKeyAlgorithms=+ssh-rsa \
  -i ~/.ssh/id_rsa_sha1test \
  user@host

3. 부딪힌 문제: OpenSSL "invalid digest" 오류

옵션을 정확히 지정했음에도 아래 오류로 접속이 끊겼다.

debug1: Host 'jinhan2-splunk' is known and matches the RSA host key.
debug1: openssl error ...: digital envelope routines:do_sigver_init:invalid digest
ssh_dispatch_run_fatal: Connection to ... error in libcrypto

원인: SSH 프로토콜 레벨(ssh_config)에서는 ssh-rsa를 허용하도록 강제했지만, 더 하위 계층인 OpenSSL 라이브러리(libcrypto)가 시스템 crypto-policy(RHEL/Rocky 계열의 DEFAULT 정책)에 의해 SHA-1 다이제스트 생성 자체를 차단하고 있었다. 즉 SSH 설정과 무관하게 OS 레벨에서 SHA-1이 막혀 있던 것이다.

에러 발생 지점이 "호스트 키 서명을 SHA-1로 검증하려는 단계"였다는 점이 힌트였다 — 서버 문제가 아니라 접속을 시도하는 클라이언트 쪽 crypto-policy 문제였다.

해결

클라이언트에서 crypto-policy를 LEGACY로 일시 전환.

sudo update-crypto-policies --set LEGACY
# 반드시 새 세션(재로그인)에서 재시도 — 기존 쉘엔 정책이 캐시돼 있을 수 있음

적용 후 KEXINIT 제안에 diffie-hellman-group14-sha1, gss-gex-sha1- 등 SHA-1 계열 KEX가 포함되는 것을 확인했고, 재시도 결과:

debug1: Server host key: ssh-rsa SHA256:...
debug1: Host '...' is known and matches the RSA host key.
...
debug3: sign_and_send_pubkey: signing using ssh-rsa SHA256:...
Authenticated to ... using "publickey".

정상적으로 SHA-1(ssh-rsa) 서명으로 호스트 키 검증과 사용자 인증이 모두 완료됐다.

⚠️ LEGACY 정책은 SHA-1 외에 3DES 등 다른 취약 알고리즘도 함께 열리므로, 테스트 후에는 반드시 원복. bash sudo update-crypto-policies --set DEFAULT


4. 트러블슈팅 요약표

단계 증상 원인 해결
1차 접속은 되나 SHA-1 테스트 아님 PubkeyAcceptedAlgorithms 옵션 누락 → 자동으로 rsa-sha2-512 선택 옵션에 =ssh-rsa(완전 교체)로 지정
2차 invalid digest / error in libcrypto 클라이언트 OpenSSL/crypto-policy가 SHA-1 다이제스트 생성 차단 클라이언트 update-crypto-policies --set LEGACY + 재로그인
3차 정상 인증 성공 - -

5. 응용: 구버전 JSch(0.1.54 이하) 라이브러리로 SFTP 연동 시 영향은?

이 테스트 결과를 구버전 JSch 기반 SFTP 연동에 적용할 수 있는지 검토했다.

핵심 차이점: OpenSSH가 겪은 invalid digest 문제는 OS의 OpenSSL(libcrypto)이 시스템 crypto-policy에 의해 SHA-1을 막은 것이었다. 반면 JSch는 순수 Java 구현체로 OS OpenSSL이 아니라 JVM 내장 JCE를 사용하므로, OS crypto-policy(FIPS/DEFAULT/LEGACY)의 영향을 받지 않는다. 대부분의 JDK는 SHA1withRSA 서명 알고리즘을 기본적으로 활성화해두고 있어, OS를 LEGACY로 바꾸지 않아도 JSch는 SHA-1 서명을 정상적으로 만들어낼 가능성이 높다.

다만 아래는 별도 확인이 필요하다.

  • 서버가 PubkeyAcceptedAlgorithmsssh-rsa를 계속 허용하고 있어야 함 (JSch 0.1.54 이하는 rsa-sha2-*를 아예 모르고 무조건 ssh-rsa로만 서명 시도 — 서버가 이를 막으면 접속 자체가 불가)
  • 서버가 구형 KEX(diffie-hellman-group14-sha1 등)를 함께 제공해야 구버전 JSch와 KEX 협상이 성립
  • 최신 OpenSSH가 보내는 kex-strict-s-v00@openssh.com 같은 확장 이름을 구버전 JSch가 무시하고 넘어가는지는 라이브러리 버전마다 다를 수 있어 별도 검증 필요

최소 연결 검증 코드

import com.jcraft.jsch.*;

public class SftpTest {
    public static void main(String[] args) throws Exception {
        JSch jsch = new JSch();
        jsch.addIdentity("/home/jinhan2/.ssh/id_rsa_sha1test");
        Session session = jsch.getSession("jinhan2", "jinhan2-splunk", 22);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect(10000);

        ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
        sftp.connect();
        System.out.println("SFTP 연결 성공, pwd=" + sftp.pwd());

        sftp.disconnect();
        session.disconnect();
    }
}

6. Rocky Linux에서 Java 소스 실행하기 (JDK 설치부터)

javac: command not found — JDK 미설치가 원인이었다.

# 사용 가능한 패키지 검색 (module 목록이 비어도 정상, 패키지로 바로 검색)
dnf search openjdk

# JDK 17(LTS) 설치 — devel 패키지가 있어야 javac(컴파일러)가 포함됨
sudo dnf install -y java-17-openjdk-devel

# 설치 확인
javac -version
java -version

만약 AppStream 리포지토리가 비활성화되어 패키지를 못 찾는다면:

dnf repolist
sudo dnf config-manager --set-enabled appstream
sudo dnf makecache

컴파일 및 실행

# JSch jar 다운로드
wget https://repo1.maven.org/maven2/com/jcraft/jsch/0.1.54/jsch-0.1.54.jar

# 컴파일
javac -cp jsch-0.1.54.jar SftpTest.java

# 실행
java -cp .:jsch-0.1.54.jar SftpTest

Java 11 이상이면 컴파일 단계 없이 소스 파일을 바로 실행하는 것도 가능하다.

java -cp jsch-0.1.54.jar SftpTest.java

정리

  • ssh-rsa(SHA-1) 테스트는 PubkeyAcceptedAlgorithms=ssh-rsa(완전 교체)로 명시해야 정확히 검증된다.
  • SSH 설정과 별개로 OS crypto-policy가 OpenSSL 레벨에서 SHA-1을 차단할 수 있으며, 이 경우 SSH 옵션만으로는 해결되지 않고 update-crypto-policies 조정이 필요하다.
  • 구버전 JSch는 OS crypto-policy와 무관하게 JVM 자체 JCE로 서명하므로 OpenSSH client 테스트와는 별도로 검증이 필요하지만, 서버가 ssh-rsa와 legacy KEX를 허용하는 현재 상태라면 정상 동작할 가능성이 높다.
  • 레거시 알고리즘 테스트 후에는 보안을 위해 crypto-policy를 원래 상태로 되돌리는 것을 잊지 말 것.

댓글목록

등록된 댓글이 없습니다.