KeyPair와 KeyFactory: RSA를 활용한 공개키 기반 인증 구현기

임채령·2025년 2월 2일

이번 포스팅에서는 KeyPairKeyFactory에 대해 설명하고, 실제 프로젝트에서 어떻게 활용했는지 공유해 보려고 합니다 ! 특히, JWT 인증을 구현하는 과정에서 이 두 가지 클래스가 어떤 역할을 했고, 이를 통해 인증 흐름을 어떻게 설계했는지 제가 구현한것을 기반으로 구체적으로 다룰 예정입니다 !

RSA와 공개키 암호화 개념

RSA는 대표적인 공개키 암호화 알고리즘으로, 비대칭 키를 사용하는 암호화 기법입니다. 여기서 비대칭 키란, 개인키(Private Key)공개키(Public Key)를 한 쌍으로 생성하여 사용하는 방식을 의미하는데 RSA의 가장 큰 특징은 개인키와 공개키를 서로 다르게 사용한다는 점입니다. 개인키로 암호화한 데이터를 공개키로 복호화할 수 있고, 반대로 공개키로 암호화한 데이터는 개인키로만 복호화할 수 있다. 이를 활용해 암호화, 디지털 서명, 인증 등의 다양한 기능을 구현할 수 있다 !

여기서 왜 개인키와 공개키가 필요한지 의문이 들수있다 ! 이 의문은 제가 이전에 포스팅한 PKI에 대해서 읽어보시면 조금이나마 도움이 될 것 같습니다 !!

https://velog.io/@1im_chaereong/posts?tag=PKI

KeyPair와 KeyFactory의 개념

본격적인 구현에 앞서, KeyPairKeyFactory에 대해 알아보겠습니다.

KeyPair

KeyPair공개키개인키를 쌍으로 생성하는 클래스다. 이를 통해 RSA 기반의 비대칭 키를 쉽게 생성할 수 있다. KeyPairGenerator 클래스를 사용하여 키 쌍을 생성하고, 여기서 나온 KeyPair 객체를 통해 공개키개인키를 얻을 수 있다. 즉, KeyPair는 RSA 알고리즘을 활용해 우리에게 필요한 암호화 키들을 제공하는 역할을 한다.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // 키 길이 2048비트 설정
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();

이렇게 생성된 개인키공개키는 각각 데이터 서명 및 검증에 사용된다.

KeyFactory

KeyFactory키의 형식을 변환해주는 클래스다. 일반적으로 키를 저장하거나 전달할 때는 문자열 형태로 인코딩하여 사용하게 된다. KeyFactory는 인코딩된 키를 다시 키 객체(Key Object)로 변환하는 데 사용된다. 즉, 외부에서 제공된 키를 실제 암호화 작업에 사용할 수 있도록 변환하는 것이 KeyFactory의 주된 역할이다.

아래와 같이 Base64로 인코딩된 키 문자열을 KeyFactory를 통해 PublicKeyPrivateKey 객체로 변환할 수 있다.

byte[] keyBytes = Base64.getDecoder().decode(publicKeyString);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);

이 과정에서 X509EncodedKeySpec은 공개키의 인코딩 형식을 나타내며, 이를 통해 문자열 형태의 공개키를 실제 PublicKey 객체로 변환하게 된다.

구현한 인증 구조 설명

이번에는 JWT 인증을 사용해 SSO(Single Sign-On) 구조를 구현하였다. IDP(Identity Provider) 서버와 SP(Service Provider) 서버를 구성하고, IDP 서버에서 JWT를 발급하고 SP 서버에서 이를 검증하는 방식으로 인증을 구현했다. 이 과정에서 KeyPairKeyFactory를 활용하여 공개키 기반 서명 검증을 구현하였다.

IDP 서버: KeyPair를 통한 키 생성과 서명

먼저 IDP 서버에서 KeyPair를 사용해 공개키개인키를 생성하였다. 생성된 키들은 각각 다음과 같은 역할을 한다:

  • 개인키: JWT를 서명하는 데 사용된다. 이를 통해 IDP 서버가 발급한 토큰임을 증명할 수 있다.
  • 공개키: SP 서버에 제공되어 서명을 검증하는 데 사용된다.

IDP 서버의 KeyPairProvider 클래스는 다음과 같이 공개키개인키를 생성하고 관리한다.

@Component
public class KeyPairProvider {
    private final PrivateKey privateKey;
    private final PublicKey publicKey;

    public KeyPairProvider() {
        KeyPair keyPair = generateKeyPair();
        this.privateKey = keyPair.getPrivate();
        this.publicKey = keyPair.getPublic();
    }

    private KeyPair generateKeyPair() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            return keyPairGenerator.generateKeyPair();
        } catch (Exception e) {
            throw new RuntimeException("Error generating KeyPair", e);
        }
    }

    public String getEncodedPublicKey() {
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }
}

이렇게 생성된 공개키는 엔드포인트(/api/publicKey)를 통해 SP 서버에 제공된다.

SP 서버: KeyFactory를 통한 서명 검증

SP 서버는 IDP 서버에서 제공한 JWT의 서명을 검증하여 유효성을 판단한다. 이때, KeyFactory를 사용해 공개키 문자열PublicKey 객체로 변환한 후 서명 검증에 활용한다.

아래는 SP 서버의 필터에서 JWT의 서명을 검증하는 과정이다.

private boolean verifySignature(String data, String signature) {
    try {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyValue);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        sig.update(data.getBytes(StandardCharsets.UTF_8));

        return sig.verify(Base64.getUrlDecoder().decode(signature));
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}
  • IDP 서버에서 받은 Base64 인코딩된 공개키를 디코딩하고, 이를 KeyFactoryX509EncodedKeySpec을 사용해 PublicKey 객체로 변환한다.
  • 변환된 PublicKey 객체를 통해 JWT의 서명이 올바른지 검증한다.

KeyPair와 KeyFactory의 역할 요약

  • KeyPair공개키와 개인키를 생성하는 역할을 한다. 이 프로젝트에서는 IDP 서버에서 JWT 서명에 사용할 개인키와 SP 서버에서 서명 검증에 사용할 공개키를 생성했다.
  • KeyFactory는 외부에서 제공된 인코딩된 키를 실제 암호화에 사용할 수 있는 키 객체로 변환하는 역할을 한다. SP 서버에서는 IDP 서버에서 받은 Base64 인코딩된 공개키KeyFactory를 통해 PublicKey 객체로 변환하고, 이를 서명 검증에 활용하였다.

이번 프로젝트에서는 RSA 기반의 공개키 암호화디지털 서명을 활용해 JWT 인증을 구현하였다. KeyPair를 통해 공개키와 개인키를 생성하고, KeyFactory를 사용해 외부에서 제공된 공개키를 검증에 사용할 수 있는 PublicKey 객체로 변환하였다. 이 과정을 통해 IDP 서버가 발급한 JWT의 무결성을 SP 서버에서 확인할 수 있었다.

이를 통해 안전한 인증 시스템을 구현할 수 있었고, 각 키의 역할과 그 활용법을 명확히 이해하게 되었다. 공개키와 개인키의 올바른 사용은 보안에서 매우 중요하며, 이를 적절히 활용함으로써 신뢰할 수 있는 인증 흐름을 구축할 수 있었다.

0개의 댓글