ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JWT 토큰 암호화 방식 및 키 저장 방식
    웹 개발 2024. 6. 26. 23:05
    반응형

    사용자 인증시에 jwt 토큰을 많이 사용하고 있지만 어떤 구성과 어떤 역할인지는 많이 확인하였지만 직접 암호화는 방식과 암호화를 위한 키 저장 방식에 대해 스스로 모르고 있다 생각하여 정리해본다.

     

    > JWT 토큰 생성 방식

    1. 대칭 키 암호화 방식 (HMAC)

    대칭 키 암호화 방식은 동일한 비밀 키를 사용하여 JWT 토큰을 생성하고 검증합니다. 주로 HS256, HS384, HS512 알고리즘이 사용됩니다. 이 방식은 설정과 사용이 간단하지만, 비밀 키가 유출될 경우 보안에 취약할 수 있습니다.

     { 
            ...
            
            String secretKey = "your-256-bit-secret";
    
            // JWT 생성
            String jwt = Jwts.builder()
                    .setSubject("user")
                    .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1시간 유효
                    .signWith(SignatureAlgorithm.HS256, secretKey)
                    .compact();
    
            // JWT 검증
            Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt);
            System.out.println("JWT is valid");
            
            ...
        }

     - 대칭 키 암호화 방식을 사용한 회사
    ex) 우아한형제들

     

    2. 비대칭 키 암호화 방식 (RSA 또는 ECDSA)

    비대칭 키 암호화 방식은 개인 키로 서명하고 공개 키로 검증하는 방식을 사용합니다. 주로 RS256, RS384, RS512, ES256, ES384, ES512 알고리즘이 사용됩니다. 이 방식은 키를 안전하게 관리할 수 있으며, 검증 키를 공개적으로 사용할 수 있어 보안성이 높습니다.

    1) RSA

    • 특징: RSA는 공개키 암호화 방식 중 하나로, 두 개의 큰 소수를 기반으로 한 암호화 알고리즘입니다.
    • 장점:
      • 널리 사용되고 검증된 알고리즘입니다.
      • 비교적 구현이 쉽고, 라이브러리 지원이 풍부합니다.
      • 더 긴 키를 사용할 수 있어 보안성이 높습니다.

    2) ECDSA (Elliptic Curve Digital Signature Algorithm)

    • 특징: ECDSA는 타원 곡선을 기반으로 한 공개키 암호화 방식입니다.
    • 장점:
      • 같은 보안 수준에서 더 짧은 키 길이를 사용하여 효율적입니다.
      • 빠른 키 생성과 서명, 검증 속도를 제공합니다.
      • 경량 환경에서도 사용 가능하여 모바일이나 IoT 장치에서 적합합니다.

     - 비대칭 키 암호화 방식을 사용한 회사
    ex) 쿠팡

     

    대칭키, 비대칭키 암호 방식이란?

    * 대칭키 암호화 (Symmetric-key encryption):
    1. 공통 키를 사용: 암호화와 복호화에 동일한 키를 사용합니다.
    2. 속도가 빠름: 대칭키 암호화는 처리 속도가 빠르기 때문에 대량의 데이터를 효율적으로 처리할 수 있습니다.
    3. 키 관리가 중요: 키를 안전하게 관리해야 하며, 통신하는 모든 측이 동일한 키를 가지고 있어야 합니다.
    예를 들어, AES (Advanced Encryption Standard)는 대칭키 암호화의 대표적인 예입니다.

    * 비대칭키 암호화 (Asymmetric-key encryption):
    1. 공개 키와 개인 키를 사용: 암호화에는 공개 키를, 복호화에는 개인 키를 사용합니다.
    2. 안전성이 높음: 개인 키는 보안이 중요하며, 공개 키는 공개될 수 있습니다.
    3. 처리 속도가 느림: 대칭키 암호화보다 처리 속도가 느립니다. 따라서 대칭키 암호화로 데이터를 암호화하고, 이 대칭키를 공개 키로 암호화하여 안전하게 전송하는 방법이 일반적입니다.

    RSA (Rivest-Shamir-Adleman) 및 ECC (Elliptic Curve Cryptography)는 비대칭키 암호화의 대표적인 예입니다.

     

    3. 대칭 키 와 비대칭 키의 선택 기준

    JWT 암호화 방식에서 대칭키 방식(HMAC)과 비대칭키 방식(RSA/ECDSA)을 선택할 때의 기준은 다음과 같은 여러 가지 요소를 고려해야 합니다.
    각 방식의 장단점을 비교하고, 애플리케이션의 요구 사항에 따라 적절한 방식을 선택하는 것이 중요합니다.

    1) 대칭키 방식 (HMAC) 선택 기준

    1. 단순성:
      • 구현이 간단: HMAC 방식은 설정과 사용이 상대적으로 간단합니다. 동일한 비밀 키를 사용하여 서명하고 검증하기 때문에 추가적인 공개 키 관리가 필요 없습니다.
    2. 성능:
      • 빠른 처리 속도: HMAC 방식은 비대칭키 방식보다 계산이 단순하므로, 더 빠르게 서명 및 검증 작업을 수행할 수 있습니다. 고성능이 요구되는 환경에서 유리할 수 있습니다.
    3. 키 관리의 단순화:
      • 단일 키 사용: 한 가지 비밀 키만 관리하면 되기 때문에, 키 관리가 단순합니다. 그러나 비밀 키가 유출되면 보안에 큰 위협이 될 수 있습니다.
    4. 신뢰할 수 있는 환경:
      • 폐쇄된 시스템: 키를 안전하게 관리할 수 있는 신뢰할 수 있는 환경에서 사용하기 적합합니다. 예를 들어, 내부 네트워크에서만 사용하는 경우.

    2) 비대칭키 방식 (RSA/ECDSA) 선택 기준

    1. 보안 강화:
      • 보안성: 비대칭키 방식은 비밀 키(private key)와 공개 키(public key)를 사용합니다. 비밀 키는 안전하게 보호되고, 공개 키는 자유롭게 배포할 수 있어 키 유출에 대한 위험이 줄어듭니다.
    2. 키 분배:
      • 분산 시스템: 여러 시스템 간에 키를 공유해야 하는 경우, 비대칭키 방식이 더 적합합니다. 공개 키를 공개적으로 배포하여 누구나 검증할 수 있기 때문에 키 관리가 용이합니다.
    3. 확장성:
      • 서드파티 통합: 외부 서비스나 서드파티와 통합할 때, 공개 키를 제공하여 검증을 할 수 있도록 합니다. 이를 통해 확장성이 높아지고, 안전하게 토큰을 검증할 수 있습니다.
    4. 법적 및 규제 요구 사항:
      • 규제 준수: 특정 산업이나 법적 요구 사항에 따라, 비대칭키 방식의 강력한 보안이 요구될 수 있습니다.

    3) 암호화 방식 선택에 대한 내용 요약

    • 대칭키 방식 (HMAC)
      • 장점: 구현이 간단, 빠른 성능, 단일 키 관리
      • 단점: 비밀 키 유출 시 보안 위협, 키 교환 어려움
      • 적합한 경우: 내부 애플리케이션, 고성능 요구, 간단한 키 관리 환경
    • 비대칭키 방식 (RSA/ECDSA)
      • 장점: 높은 보안성, 키 분배 용이, 서드파티 통합 적합
      • 단점: 상대적으로 복잡한 구현, 더 많은 계산 자원 필요
      • 적합한 경우: 분산 시스템, 외부 서비스 통합, 높은 보안 요구 사항
    • 결론
      • 대칭키 방식과 비대칭키 방식을 선택할 때는 보안 요구 사항, 성능, 키 관리의 복잡성, 법적 및 규제 요구 사항 등을 종합적으로 고려해야 합니다.
        특정 상황에 따라 적절한 방식을 선택하면 애플리케이션의 보안성과 성능을 최적화할 수 있습니다.

     

     

    > 키를 관리하는 KeyStore 와 KMS 가 있는데 각 역할과 목적

    키스토어(KeyStore)와 KMS(Key Management Service)는 모두 암호화 키를 안전하게 관리하는 데 사용되지만, 그 역할과 기능, 사용 방식에 차이가 있습니다.

    다음은 키스토어와 KMS의 비교 및 각각의 역할에 대한 설명입니다.

    1. 키스토어(KeyStore)

    • 역할:
      • 암호화 키와 인증서를 로컬 시스템에서 안전하게 저장하고 관리.
      • 자바 애플리케이션 내에서 키와 인증서를 로드하고 사용할 수 있도록 지원.
    • 사용 방식:
      • 주로 파일 시스템에 저장되며, 애플리케이션이 실행되는 환경 내에서 접근 가능.
      • 대칭키, 비대칭키, 인증서 등을 저장할 수 있음.
      • 키스토어 파일을 암호화하여 보호하며, 키를 저장할 때 비밀번호를 사용.
      • 예: JKS, PKCS12 등 다양한 포맷 지원.
    • 예시:
      • 로컬 애플리케이션에서 SSL/TLS 통신을 설정하거나,
      • JWT 서명/검증에 사용되는 키를 저장.

    2. KMS(Key Management Service)

    • 역할:
      • 클라우드 환경에서 중앙 집중적으로 키를 관리하고 보호.
      • 키 생성, 저장, 회전, 폐기 등의 라이프사이클 관리 기능 제공.
      • 주요 클라우드 제공업체(AWS, Google Cloud, Azure)에서 제공하는 서비스로, 하드웨어 보안 모듈(HSM)을 사용하여 키를 보호.
    • 사용 방식:
      • 네트워크를 통해 API로 접근하여 키 관리 및 암호화/복호화 작업 수행.
      • 키를 물리적으로 클라우드 제공업체의 인프라 내에서 안전하게 관리.
      • 클라우드 리소스와의 통합이 용이하며, 정책 기반 접근 제어 가능.
      • 예: AWS KMS, Google Cloud KMS, Azure Key Vault.
    • 예시:
      • 클라우드 애플리케이션에서 데이터 암호화, 디지털 서명, 인증서 관리 등에 사용.
      • 자동 키 회전 및 모니터링 기능을 통한 강화된 보안 관리.

    3. 키스토어와 KMS 비교

    항목 키스토어(KeyStore) KMS (Key Management Service)
    위치 로컬 파일 시스템 클라우드 인프라
    관리 방식 애플리케이션 내 관리 중앙 집중적 관리
    접근 방식 애플리케이션 코드에서 직접 접근 API 호출을 통한 네트워크 접근
    보안 암호화된 파일로 저장 HSM을 통한 강력한 보안
    확장성 로컬 환경에 한정 클라우드 환경의 확장성
    기능 키 저장 및 로드, 인증서 관리 키 생성, 회전, 폐기, 정책 기반 접근 제어, 감사 로그 등

     

    4. 결론

    • 키스토어는 로컬 환경에서의 키와 인증서 관리를 위한 도구로, 주로 자바 애플리케이션에서 사용됩니다.
    • KMS는 클라우드 환경에서 중앙 집중적으로 키 관리를 제공하며, 더 강력한 보안과 확장성을 제공합니다.

    따라서, 로컬 환경에서 실행되는 애플리케이션의 키 관리가 필요하다면 키스토어를 사용하는 것이 적합하고, 클라우드 기반 애플리케이션에서 강력한 보안과 확장성을 필요로 한다면 KMS를 사용하는 것이 적합합니다.

     

    > 키스토어(KeyStore) 의 종류

    Java에서는 다양한 형식의 키스토어(KeyStore)를 지원합니다. 각각의 형식은 특정 용도에 맞춰 선택하여 사용할 수 있습니다. 주요한 키스토어 형식들은 다음과 같습니다:

    1. JKS (Java KeyStore):
      • 가장 전통적이고 널리 사용되는 Java 키스토어 형식입니다.
      • 기본적으로 비밀번호로 보호됩니다.
      • 개인 키와 인증서를 포함할 수 있습니다.
      • 확장자는 .jks입니다.
      • 예: KeyStore.getInstance("JKS")
    2. PKCS12:
      • 공개 키 암호화 표준 (PKCS #12)에 따라 개발된 형식입니다.
      • 개인 키와 해당 인증서(및 체인)를 포함할 수 있습니다.
      • 확장자는 .p12 또는 .pfx입니다.
      • 예: KeyStore.getInstance("PKCS12")
    3. PKCS11:
      • 하드웨어 보안 모듈(HSM)과 같은 하드웨어 장치에서 키를 관리하기 위한 표준입니다.
      • 자바에서는 PKCS11 프로바이더를 통해 이 형식을 지원합니다.
      • 예: KeyStore.getInstance("PKCS11")
    4. Windows-MY:
      • Windows 운영 체제의 개인 인증서 저장소에 접근하기 위한 형식입니다.
      • Windows의 기본 인증서 저장소에 접근할 때 사용됩니다.
      • 예: KeyStore.getInstance("Windows-MY")
    5. BKS (Bouncy Castle KeyStore):
      • Bouncy Castle 라이브러리에서 제공하는 키스토어 형식으로, JKS와 비슷하지만 암호화 알고리즘이 추가됩니다.
      • 확장자는 .bks입니다.
      • 예: KeyStore.getInstance("BKS")
    6. UBER (Bouncy Castle UBER KeyStore):
      • Bouncy Castle 라이브러리의 또 다른 키스토어 형식으로, 여러 다른 키스토어 유형을 단일 파일에 저장할 수 있습니다.
      • 확장자는 .ubr입니다.
      • 예: KeyStore.getInstance("UBER")

     

    > JKS 와 PKCS12 비교

    JKS (Java KeyStore)

    • 형식:
      • Java에서 기본적으로 제공하는 키스토어 형식입니다.
      • 확장자는 .jks입니다.
    • 보안:
      • JKS는 비밀번호로만 보호됩니다.
      • 기본적으로 비밀번호는 단일 암호로 키스토어 전체를 암호화합니다.
    • 지원 알고리즘:
      • 주로 RSA 알고리즘을 사용한 개인 키와 인증서를 저장할 수 있습니다.
    • 사용:
      • 널리 사용되고 있으며, Java 기반의 애플리케이션에서 기본적으로 지원됩니다.
      • 개인 키와 관련된 인증서(및 체인)를 포함하여 다양한 보안 자료를 저장할 수 있습니다.

    PKCS12 (Public-Key Cryptography Standards #12)

    • 형식:
      • 공개 키 암호화 표준(PKCS #12)에 따라 개발된 형식입니다.
      • 확장자는 .p12 또는 .pfx입니다.
    • 보안:
      • PKCS12는 비밀번호로 보호됩니다.
      • JKS와 달리 다양한 암호화 알고리즘을 지원하며, 개인 키와 인증서(및 체인)를 포함할 수 있습니다.
    • 지원 알고리즘:
      • RSA, DSA, ECDSA 등 다양한 알고리즘을 지원합니다.
      • 암호화된 개인 키와 인증서를 포함하여 보다 유연하게 사용할 수 있습니다.
    • 사용:
      • 보다 널리 사용되는 표준 형식으로, 다양한 플랫폼과 시스템에서 지원됩니다.
      • 개인 키를 포함한 다양한 보안 자료를 포함할 수 있으며, 다양한 암호화 알고리즘을 지원하여 보안 요구 사항에 유연하게 대응할 수 있습니다.

    요약

    • 선택 기준:
      • JKS: 기본적인 Java 플랫폼에서의 키스토어 사용이나 RSA 기반의 개인 키 관리에 적합합니다.
      • PKCS12: 보다 넓은 플랫폼 호환성과 다양한 암호화 알고리즘 지원이 필요할 때 사용됩니다. 또한, 비밀번호 보호 및 다양한 보안 자료를 포함할 수 있는 유연성을 제공합니다.

     

     

    > 우리는 어떤 암호화 방식과 어떻게 키를 관리해서 사용할까.

    >> 비대칭 암호화 방식으로 PKCS12 형식의 키스토어를 통해 JWT 을 서명하고 검증합니다.

     

     

    1. PKCS#12 키스토어 생성 및 JWT 생성 검증 예시

     

    1) PKCS#12 키스토어 생성

    먼저, OpenSSL을 사용하여 개인키와 인증서를 생성한 다음 이를 PKCS#12 형식으로 변환합니다.

    # 개인키 생성
    openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
    
    # 인증서 서명 요청 (CSR) 생성
    openssl req -new -key private_key.pem -out request.csr
    
    # 자체 서명된 인증서 생성
    openssl x509 -req -days 365 -in request.csr -signkey private_key.pem -out certificate.crt
    
    # 개인키와 인증서를 PKCS#12 파일로 변환
    openssl pkcs12 -export -out keystore.p12 -inkey private_key.pem -in certificate.crt -name mykey
    • 다른 서버에서 JWT 토큰의 유효성을 검증하려면, 해당 서버가 토큰을 서명할 때 사용된 공개키(Public Key)를 가지고 있어야 합니다.

     

    2) Spring Boot 설정

    > build.gradle

       ...
        implementation("org.springframework.boot:spring-boot-starter-security")
        //JWT
        implementation("io.jsonwebtoken:jjwt:0.9.1")
        implementation("com.sun.xml.bind:jaxb-impl")
        implementation("com.sun.xml.bind:jaxb-core")
        implementation("javax.xml.bind:jaxb-api:2.3.0")
        ...

     

    3) JWT 토큰 생성 및 검증

    public class JwtTokenProvider {
        ...
        // 초기에 키와 인증서를 저장할 수 있는 KeyStore 생성 및 로드한다.
        @PostConstruct
        public void init() {
            try {
                keyStore = KeyStore.getInstance("PKCS12");
                FileInputStream fis = new FileInputStream("keystore.p12");
                keyStore.load(fis, code.toCharArray());
                fis.close();
            } catch (Exception e) {
                throw new RuntimeException("Failed to load keystore", e);
            }
        }
        ...
        // 키스토어에서 개인키를 가져와서 토큰을 생성한다.
        public String generateToken(String subject) {
            try {
                PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS, KEYSTORE_PASSWORD.toCharArray());
                return Jwts.builder()
                           .setSubject(subject)
                           .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 day
                           .signWith(privateKey, SignatureAlgorithm.RS256)
                           .compact();
            } catch (Exception e) {
                throw new RuntimeException("Failed to generate token", e);
            }
        }
        ...
        // 공개키를 가져와서 토큰을 검증한다.
        public boolean validateToken(String token) {
            try {
                PublicKey publicKey = keyStore.getCertificate(KEY_ALIAS).getPublicKey();
                Jwts.parserBuilder()
                    .setSigningKey(publicKey)
                    .build().parseClaimsJws(token);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }

     

     

    > 공개키 추출

    PKCS#12 파일에서 공개키를 추출하려면, 먼저 openssl 명령을 사용하여 공개키를 PEM 형식으로 추출할 수 있습니다. 다음 명령어를 실행합니다:

    # PKCS#12 파일에서 인증서를 추출
    openssl pkcs12 -in keystore.p12 -nokeys -out certificate.pem
    
    # 인증서에서 공개키를 추출
    openssl x509 -in certificate.pem -pubkey -noout -out public_key.pem

     

    > 공개키 배포 방법

    a. 키 파일 공유

    공개키를 키 파일로 저장하고, 다른 서버에서 해당 파일을 읽어와서 검증에 사용하도록 설정할 수 있습니다. 예를 들어, 공개키를 public_key.pem 파일로 저장하고 다른 서버에서 이를 읽어오는 방법입니다.

    b. 환경 변수 또는 구성 파일 사용

    공개키를 환경 변수나 구성 파일에 저장하고, 다른 서버에서 이를 로드하여 사용합니다. 이 방법은 보안적이고 배포가 용이합니다.

    c. 키 관리 서버 (KMS) 또는 비밀 관리 서비스 사용

    AWS KMS, HashiCorp Vault 등의 키 관리 서비스를 사용하여 공개키를 안전하게 저장하고 배포합니다. 다른 서버는 해당 서비스에서 공개키를 가져와서 사용합니다.

     

     

    반응형

    '웹 개발' 카테고리의 다른 글

    WebClient 와 ChatClient 의 관계  (1) 2024.12.19
    Pageable 을 파헤치자.  (39) 2024.04.29
    자바 HashTable 과 HashMap  (0) 2024.03.28
    User-Agent 정보 가져오기  (0) 2024.03.20
    정적 코드 분석 도구 Sonarqube 도입 제안  (2) 2024.02.24
Designed by Tistory.