검색

레이블이 Java인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Java인 게시물을 표시합니다. 모든 게시물 표시

2024년 10월 26일

코딩 - 자바스크립트 CryptoJS 와 JAVA 간의 하이브리드 암호화 (RSA+AES)

클라이언트와 서버간의 안전한 통신을 위하여 간단하게 암호화를 만들어보았다. 작업 환경은 아래와 같다. 

◼︎ 환경

  • HW : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 15.0.1 (24A348)
  • TOOLS : Visual Studio Code, Java 11 
  • Programming Language : Java, HTML, JavaScript 


개인적으로 데이터 암호화는 AES (Advanced Encryption Standard) 알고리즘을 사용한다. AES는 대칭키 알고리즘 중 하나로, 데이터 암호화와 복호화에 동일한 키를 사용한다. 
  • AES 키: 대칭키 암호화에서 데이터를 암호화하고 복호화하는데 사용되는 비밀 키. AES 키는 128, 192, 또는 256비트로 설정될 수 있다.
  • IV (Initialization Vector): AES-CBC 모드에서 사용되는 추가 보안 요소. IV는 암호화할 때마다 무작위로 생성되고, 암호화된 데이터와 함께 전송됩다.
  • Base64 인코딩: AES 키와 IV는 바이너리 데이터이므로, 이를 텍스트 형식으로 저장하고 전송하기 위해 Base64로 인코딩한다.
서버/클라이언트 환경을 고려하면 대칭키 방식의 암호화/복호화는 서버와 클라이언트 모두가 AES 키 와 IV 값을 미리 알고 있어야 한다.  키 관리의 위험성을 고려하면 암호화 키를 서버와 클라이언트가 공유 하는 대칭키 방식 보다는 비대칭키를 사용하는 것이 더 키 관리에 안전하다고 할 수 있다. 

대칭키와 비대칭키 암호화의 장점과 단점은 아래와 같다.

대칭키 암호화 (Symmetric Encryption)
① 특징:
  • 암호화와 복호화에 같은 키를 사용.
  • AES(Advanced Encryption Standard)와 같은 알고리즘이 대칭키 암호화에 해당.
② 장점:
  • 빠른 속도: 대칭키 암호화는 비대칭키보다 훨씬 빠름. 특히 대량의 데이터를 암호화할 때 성능이 우수함.
  • 간단한 구현: 동일한 키로 암호화와 복호화를 수행하기 때문에 상대적으로 간단하게 구현할 수 있음.
③ 단점:
  • 키 관리 문제: 대칭키를 안전하게 공유하는 것이 문제. 만약 네트워크를 통해 키를 전송해야 한다면, 키가 중간에서 가로채기당할 위험이 있음.
  • 보안성 문제: 키가 노출되면 데이터를 보호할 방법이 없음. 키 교환이 안전하지 않다면, 대칭키 암호화는 취약할 수 있음.

비대칭키 암호화 (Asymmetric Encryption)
① 특징:
  • 암호화에는 공개키를, 복호화에는 개인키를 사용.
  • RSA(Rivest-Shamir-Adleman)와 같은 알고리즘이 비대칭키 암호화에 해당.
② 장점:
  • 보안성: 비대칭키 암호화는 공개키를 사용해 암호화하므로 키 교환이 안전. 공개키는 누구나 사용할 수 있지만, 개인키는 소유자만이 알고 있기 때문에 보안성이 뛰어남.
  • 키 관리 용이: 공개키는 네트워크를 통해 쉽게 공유할 수 있고, 개인키는 비밀로 유지되므로 키 교환이 안전하게 구현됨.
③ 단점:
  • 속도 문제: 비대칭키 암호화는 대칭키 암호화에 비해 훨씬 느림. 특히 대량의 데이터를 암호화하는 데 비효율적임.
  • 데이터 크기 제한: RSA와 같은 비대칭키 알고리즘은 암호화할 수 있는 데이터의 크기가 키 길이에 의해 제한됨.
이들 암호화 방식의 특성을 고려하면 서버/클라이언트 환경에서는  TLS/SSL 프로토콜(HTTPS) 등에서 널리 사용되는 비대칭키와 대칭키를 결합한 하이브리드 암호화 방식이 가장 안전하고 효율적이라고 할 수 있다. 

참고로 아래는 TLS/SSL 프로토콜(HTTPS) 을 쉽게 설명하는 이미지 이다. (출처https://has3ong.github.io/computer%20science/ssl-tls/ )


클라이언트/서버간의 안전한 데이터 암호화를 위한 하이브리드 방식은 아래와 같은 절차를 따른다.
  1. 서버는 클라이언트(웹) 에게 RSA 공개키를 배포
  2. 클라이언트는 랜덤 생성된 AES 키를 사용하여 데이터를 암호화
  3. RSA 공개키로 AES 키를 암호화 하고 암호화된 데이터와 IV 값을 함께 서버에 전달
  4. 서버는 RSA 개인키로 암호화된 AES 키를 복호화하고 함께 전달된 IV 값을 사용하여 데이터를 복호화



하이브리드 방식의 암호화 방식을 적용하면 적은 노력으로 손쉽게 클라이언트와 서버간의 암호화 통신을 구현할 수 있다.  

하이브리드 방식 암호화 구현에 있어 클라이언트에서 ❶ AES 암호화는 CryptoJS (JavaScript library of crypto standards) 을 사용하였고 ❷ RSA 암호화는 웹 암호화 API 의 일부로 제공되는 crypto.subtle 을 사용하였다. 주의 할 점은 crypto.subtle API는모든 브라우저에서 지원하지 않으며, 특히 일부 구형 브라우저나 특정 모바일 브라우저에서는 지원되지 않을 수 있다. 서버의 경우 기본 자바의 암호화 기능을 사용하였다.

Can I Use - Web Cryptography API 사이트를 통하여 호환성을 확인해 볼 수 있다. 이 사이트에 따르면 대부분의 웹 브라우저에서 지원된다. 

하이브리드 방식 암호화 구현하기

서버는 AES 암호화를 위한 대칭키를 암호화기 위하여 먼저 RSA 비대칭키  생성한다


비밀키는 안전하게 보관하고 공개키는 클라이언트에 전달한다. 클라이언트는 AES 키를 생성하여 데이터를 암호화하고 AES 키는 서버가 공개한 RAS 공개키로 암호화를 한다.

Encrypt Data 버튼을 클릭하여 데이터를 암호화 한다.

이제 공개키로 암호화된 AES 키 값, AES 로 암호화된 데이터와 IV 값을 서버에 전달한다.
  • 공개키로 암호화된 AES 키 : BASE64로 인코딩 
  • IV : BASE64로 인코딩 
  • AES 로 암호화된 데이터 : BASE64로 인코딩

서버는 비밀키로 암호화된 대칭키 값을 복호화하고 키를 사용하여 암호화된 데이터를 복호화 한다.

참고자료 

  • ChatGPT 4o : 코드는 ChatGPT 4o 을 사용하여 작성.

2024년 10월 2일

코딩 - Spring Boot 환경에서 설정 정보 암호화

"Config Server 을 구성할 필요가 없는 경우 주요한 설정을 어떻게 암호화를 할 것인가"

Spring Boot 에서 Config Server를 사용하지 않고도 암호화를 적용하는 방법은 여러 가지가 있다. 특히, 민감한 데이터를 보호하기 위해 Jasypt 와 같은 라이브러리를 사용하여 암호화를 적용, 애플리케이션의 설정 파일이나 환경 변수에 저장된 비밀번호, API 키 등의 민감한 정보를 안전하게 암호화할 수 있다. 

◼︎ 환경

  • Model : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 13.2.4 (22F66)
  • TOOLS : Visual Studio Code, Java 11, Gradle, Docker
  • Version Control : GitHub
  • Programming Language : Java
  • Back-End Framework : Spring Boot 2.7.12, Spring Security 5.7.7 
  • DBMS : MySql 8.0.33
  • Cloud : OCI (free tier account

Jasypt ?

Jasypt (Java Simplified Encryption) 는 자바 애플리케이션 속성 파일, 데이터베이스 비밀번호, 또는 기타 중요한 데이터를 쉽게 암호화하고 복호화할 수 있도록 도와주는 것이다. Jasypt는 복잡한 암호화 알고리즘을 이해하거나 구현할 필요 없이 간단한 API를 통해 보안을 향상시킬 수 있도록 설계되었다.

주요 특징

  1. 암호화 및 복호화 기능: 비밀번호, 속성 값 등의 중요한 데이터를 쉽게 암호화하고, 필요한 시점에 복호화
  2. 구성 파일 보호: 애플리케이션의 application.properties 또는 application.yml과 같은 설정 파일에서 중요한 정보(예: 데이터베이스 연결 정보)를 암호화
  3. 알고리즘 지원: AES, DES, PBE 등 다양한 암호화 알고리즘을 지원
  4. 설정이 용이: 환경 변수나 명령줄 인자를 통해 암호화 키를 전달하고, 자동으로 암호화된 데이터를 복호화할 수 있는 기능을 제공
  5. 스프링 통합: 스프링 프레임워크와 통합되어 스프링 부트 (Spring Boot) 애플리케이션에서 손쉽게 사용

Jasypt는 특히 애플리케이션 보안 향상을 위해 중요한 정보를 암호화하는 데 유용하며, 자바 기반 프로젝트에서 많이 사용된다. 


Jasypt를 사용한 암호화 적용 방법

재십트(jasypt) 는 스프링 부트 (Spring Boot) 와의 통합을 돕기 위해 jasypt-spring-boot-starter 라이브러리를 제공한다. 이를 사용하면 Spring Boot 의 환경 설정 파일(application.properties, application.yml)에서 암호화된 값을 쉽게 처리할 수 있다.  아래는 gradle 기반 스프링 부트 (Spring Boot) 프로젝트에 재십트(jasypt) 암호화를 적용하는 것을 설명한다.

❶ 의존성 추가: build.gradle 에 jasypt-spring-boot-starter 라이브러리 의존성을 추가한다.


implementation "com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4"

❷ 암호화된 값 사용: 애플리케이션의 application.properties 또는 application.yml 파일에 민감한 데이터를 암호화된 상태로 저장할 수 있다. 암호화된 값은 ENC(...) 형식으로 표기한다.

application.yml


datasource:
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:postgresql://localhost:5432/studio_db
username: ENC(amX5KdNShNl7jMYsHfpWccYeGhDWHD9bXsxXQSZGuUI=)
password: ENC(4EyexAjWphLdRG6NaDaPb2S2c6gU8PZr/QgLM7e4nY4=)

❸ Jasypt Encryptor 설정: 재십트(jasypt) 가 암호화된 값을 복호화할 수 있도록 JasyptStringEncryptor 를 설정해야 한다.  스프링 부트 (Spring Boot) 애플리케이션에서는 암호화를 위한 비밀번호를 설정하는 것으로 간단하게 구현할 수 있다.

비밀번호를 전달하는 방법은 여러가지 방법이 있다.

  1. 환경변수로 전달
  2. CI/CD 도구에서 지원하는 시크릿 관리 기능을 사용하여 전달
  3. AWS Secrets Manager, HashiCorp Vault, Azure Key Vault 등의 비밀 관리 서비스 사용하여 전달
  4. 사용자 정의 JasyptStringEncryptor 을 구현하여 원하는 곳에서 비밀번호를 로드하여 직접 사용
아래는 환경변수를 사용하여 비밀번호를 전달하는 방법이다.

application.yml

jasypt:
encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD} # 환경 변수로 암호화 비밀번호 설정


JASYPT_ENCRYPTOR_PASSWORD 환경변수에 비밀번호를 세팅.

Linux/macOS:

export JASYPT_ENCRYPTOR_PASSWORD=your-encryption-password
./gradlew bootRun

Windows:

set JASYPT_ENCRYPTOR_PASSWORD=your-encryption-password
gradlew bootRun

❹ 암호화 : Jasypt CLI(Command Line Interface)를 사용하여 값을 암호화하거나 복호화할 수 있다. 예를 들어, 다음과 같이 명령어를 사용하여 값을 암호화할 수 있다.

encrypt input="yourSecretValue" password="your-encryption-password"

jasypt 1.9.3 (binaries and javadocs)

암호화된 값을 ❷ 암호화된 값 사용 과 같이 application.yml 설정에 기술하면 된다.

재십트(jasypt) 암호화

재십트(jasypt) 에서 디폴트로 적용되는 암호화 알고리즘과 관련된 주요 값들은 다음과 같다. 이 값들은 재십트(jasypt)를 기본 설정으로 사용할 때 적용되는 값 들이다.


① 디폴트 암호화 알고리즘 
  • PBEWithMD5AndDES: 기본적으로 Jasypt는 PBE (Password-Based Encryption) 알고리즘 중 하나인 PBEWithMD5AndDES를 사용한다. PBEWithMD5AndDES는 MD5 해시 알고리즘과 DES(Data Encryption Standard)를 사용한 암호화 알고리즘이다.
  • 이 알고리즘은 기본적인 보안성을 제공하지만, 현대적 암호화 표준에 비해 상대적으로 약한 것으로 간주되기 때문에 더 강력한 알고리즘을 사용할 수 있도록 설정을 변경하는 것이 좋다.

② 디폴트 해시 알고리즘 
  • MD5재십트(jasypt)는 기본적으로 MD5 해시 알고리즘을 사용하여 암호화 키를 처리한다. MD5는 널리 사용되지만, 충돌 공격에 취약하다는 보안 문제로 인해 요즘은 SHA-256 또는 SHA-512와 같은 더 강력한 해시 알고리즘이 선호된다.

③ 키 생성 반복 횟수
  • 1000번 반복: Jasypt는 기본적으로 암호화 키를 생성할 때 1000번의 반복을 수행한다. 이 값은 비밀번호 기반 암호화의 보안성을 높이기 위한 중요한 요소로 이 값을 더 높여 보안을 강화할 수 있다.

④ 디폴트 Salt Generation (솔트 생성)
  • 랜덤 솔트: 재십트(jasypt)는 암호화 시 자동으로 랜덤 솔트(Salt) 값을 생성한다. 솔트는 암호화된 값의 보안성을 높이기 위해 추가되는 값으로, 암호화된 텍스트의 패턴화를 방지하는 역할을 한다.

⑤ 디폴트 출력 포맷 
  • Base64: 재십트(jasypt)는 암호화된 텍스트를 기본적으로 Base64 로 인코딩하여 출력한다. Base64는 바이너리 데이터를 텍스트로 인코딩하는 방식으로, 암호화된 값을 쉽게 문자열로 표현할 수 있게 한다.

⑥ IV(Initialization Vector, 초기화 벡터) 
  • 기본적으로 Jasypt는 PBEWithMD5AndDES 알고리즘을 사용할 때 초기화 벡터(IV)를 생성하지 않는데 알고리즘 자체가 IV를 사용하지 않기 때문이다. 그러나 다른 알고리즘, 예를 들어 AES와 같은 알고리즘을 사용할 때는 IV가 자동으로 처리된다. 

⑦ 디폴트 암호화기(PBE String Encryptor) 
  • 재십트(jasypt)는 기본적으로 **StandardPBEStringEncryptor**를 사용하여 문자열을 암호화 및 복호화한다.

이와 같은 기본 설정들은 애플리케이션의 보안 요구에 맞게 조정할 수 있으며, 기본값을 사용하더라도 어느 정도의 보안을 제공하지만, 더 강력한 알고리즘 변경 권장된다. 

아래는 보안 수준에 따른 알고리즘 및 설정 값을 정리한 것이다. 운영 환경이라면 최소 중간 수준이상의 알고리즘 적용이 필요하다.

  • 낮은 보안 수준
    • 알고리즘 : PBEWithMD5AndDES
    • 키 획득 반복 횟수 : 1000
    • 키 길이 : 56
    • 솔트 사용 : ✓
    • IV 사용 : ✗
    • 설명 : 기본 Jasypt 설정. MD5와 DES를 사용하는 알고리즘으로 비교적 낮은 보안 수준. 과거에 널리 사용되었으나 현대의 공격에 취약.
    • 권장 사용 환경 : 테스트 환경 또는 민감하지 않은 데이터를 암호화할 때.
  • 보통 보안 수준
    • 알고리즘 : PBEWithSHA1AndDESede
    • 키 획득 반복 횟수 : 1000
    • 키 길이 : 168
    • 솔트 사용 : ✓
    • IV 사용 : ✗
    • 설명 : SHA-1 해시 알고리즘과 3DES 암호화 알고리즘 사용. DES보다 더 안전하지만, SHA-1이 약해져 더 강력한 알고리즘이 권장됨.
    • 권장 사용 환경 : 테스트 환경 또는 민감하지 않은 데이터를 암호화할 때.
  • 중간 보안 수준
    • 알고리즘 : PBEWithSHA256And128BitAES-CBC-BC
    • 키 획득 반복 횟수 : 1000
    • 키 길이 : 168
    • 솔트 사용 : ✓
    • IV 사용 : ✓
    • 설명 : SHA-256 해시 알고리즘과 AES-128 비트 암호화를 사용. AES는 고성능과 높은 보안성을 제공. IV를 사용하여 보안성을 강화.
    • 권장 사용 환경 : 내부 시스템의 암호화 작업이나 중요하지만 최고 수준의 보안을 요구하지 않는 환경.
  • 높은 보안 수준
    • 알고리즘 : PBEWithHMACSHA512AndAES_256
    • 키 획득 반복 횟수 : 1000
    • 키 길이 : 256
    • 솔트 사용 : ✓
    • IV 사용 : ✓
    • 설명 : HMAC-SHA512 해시 알고리즘과 AES-256 비트 암호화를 사용. AES-256은 강력한 보안을 제공하며, HMAC을 사용해 무결성 보장.
    • 권장 사용 환경 : 금융 서비스, 헬스케어 시스템 등 민감한 데이터를 보호해야 하는 환경.
  • 매우높은  보안 수준
    • 알고리즘 : PBEWithHMACSHA512AndAES_256
    • 키 획득 반복 횟수 : 5000~10000
    • 키 길이 : 256
    • 솔트 사용 : ✓
    • IV 사용 : ✓
    • 설명 : 암호화와 해시에서 최고 수준의 보안. 반복 횟수를 늘리고 AES-256을 사용하여 비밀번호 기반 암호화의 보안을 최대한 강화.
    • 권장 사용 환경 : 금융 서비스, 헬스케어 시스템 등 민감한 데이터를 보호해야 하는 환경.
  • 최고  보안 수준
    • 알고리즘 : PBKDF2WithHmacSHA512 + AES_256
    • 키 획득 반복 횟수 : 10000~50000
    • 키 길이 : 256
    • 솔트 사용 : ✓
    • IV 사용 : ✓
    • 설명 : PBKDF2는 비밀번호 기반 키 도출 기능으로 반복 횟수와 HMAC-SHA512를 사용해 높은 보안성을 제공. AES-256과 함께 사용하면 최상의 보안을 제공.
    • 권장 사용 환경 : 정부, 군사 시스템, 민감한 데이터를 보호하는 최고 수준의 보안이 요구되는 환경

중간 보안 요구 이상에서는 AES-256과 함께 PBEWithHMACSHA512AndAES_256 또는 PBKDF2WithHmacSHA512를 사용하는 것이 권장. AES-256은 현재 가장 안전한 암호화 표준 중 하나이며, HMAC-SHA512는 암호화 데이터의 무결성을 보장. 반복 횟수(Iterations)를 5000번 이상으로 설정하여 암호화 키 도출 과정의 보안을 더욱 강화하는 것을 권장.

특히 개인정보와 같은 민감한 데이터 보호가 필요한 환경에서는 AES-256HMAC-SHA512를 사용한 설정을 적극 추천.

중간 수준이상의 암호화 적용

테스트 환경에서는 디폴트 암호화 알고리즘 이외의 암호화를 사용하려는 경우 오류가 발생하여 진행이 불가 하였다. 이런 이유로 환경 이슈에 독립적으로 동작하도록 보니캐슬(Bouncy Castle) 암호화 프로바이터를 사용하였다. (맥 환경이 문제는 아닌가 추정만 하였다.)

❶ 의존성 추가: build.gradle 에 보니캐슬(Bouncy Castle) 라이브러리 의존성을 추가한다.

❷ 재십트(jasypt) 에 보니캐슬(Bouncy Castle) 설정 

Jasypt 설정 예시 (AES 256 사용):

application.yml

encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD} # 환경 변수로 암호화 비밀번호 설정
algorithm: "PBEWithSHA256And256BitAES-CBC-BC" # Bouncy Castle 알고리즘 사용
providerName: "BC" # Bouncy Castle 프로바이더 지정
key-obtention-iterations: 1000
pool-size: 1
salt-generator-classname: "org.jasypt.salt.RandomSaltGenerator"
string-output-type: "base64"


PBEWithSHA256And256BitAES-CBC-BC: 보니캐슬(Bouncy Castle)이 제공하는 AES-256 알고리즘이다.

❸ Bouncy Castle 프로바이더 등록
Java 환경에서 보니캐슬(Bouncy Castle)을 암호화 프로바이더로 등록해야 하며, 이 작업은 JVM이 시작될 때 수행할 수 있다. 스프링 부트 (Spring Boot) 에서는 Application.java 에 추가하여 프로바이터를 등록할 수 있다.


import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
SpringApplication.run(MyApplication.class, args);
}
}



좀더 자동화된 방법을 고민하다 재십트(jasypt) 라이브러리가 추가되어 있고 보니캐슬(Bouncy Castle) 암호화 라이브러리가 있는경우 보니캐슬(Bouncy Castle) 프로파이더를 추가 하고  디폴트 암호화(StandardPBEStringEncryptor) 모듈을 생성하도록 자동화된 설정을 구현하여 사용하였다.

다만 이경우 Jasypt CLI(Command Line Interface)를 사용하여 값을 암호화하거나 복호화 하는 것이 어려워저 추가로 암호화를 위한 RESTful API 을 추가하여 사용할 수 있도록 하였다.

특정 값을 암호화하려면 아래와 같이 curl 을 사용하여 암호화하고 yml 파일에 ENC() 함수를 사용하여 암호화된 값으로 설정을 추가하면 된다.

curl localhost:8080/data/encrypt -s -d 암호화할 데이터 


2024년 3월 7일

코딩 - Cloud Ready : Spring Boot & Oracle Object Storage

◼︎ 환경

  • Model : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 13.2.4 (22F66)
  • TOOLS : Visual Studio Code, Java 11, Gradle, Docker
  • Version Control : GitHub
  • Programming Language : Java
  • Framework : Spring Boot 2.7.12, Spring Security 5.7.7
  • Cache : ehcahce 2.10.9.2, Guava 31.1-jre
  • DBMS : MySql 8.0.33
  • Cloud : OCI (free tier account)

OCI 오브젝트 스토이지 버킷 생성

OCI 오브젝트 스토리지 버킷 생성은 다음 절차로 진행한다. ( 계정은 free tier 을 이용하였다. )

※ 스토이지 버킷(Storage Bucket)는 대부분의 클라우드 기반 객체 스토리지 서비스에서 사용되는 컨테이너 개념으로 Amazon S3, Google Cloud Storage, Microsoft Azure Blob Storage, Oracle Cloud Object Storage 등에서 사용된다. 파일이나 데이터를 버킷에 저장하면 클라우드 제공 업체가 제공하는 다양한 기능(데이터의 보관, 버전 관리, 접근 제어, 보안, 데이터 복제)을 활용할 수 있다.
  1. OCI 콘솔 왼쪽 상단의 햄버거 아이콘을 클릭
  2. 스토이지 를 클릭합
  3. 오브젝트 스토리지 및 아카이브 스토리지 아래에 있는 "버킷"을 클릭
  4. 버킷 생성 버튼을 클릭. 팝업 창이 열리면 "생성"을 클릭(기본 구성 유지)





API 연계를 위한 개인키 구성 파일 생성

프로그램에서 OCI 오브젝트 스토리지와 연결하려면 개인 키와 구성 파일을 생성해야 한다. 생성은 아래 절차로 진행한다.
  1. OCI 콘솔에서 프로필 세부 정보 페이지로 이동 
  2. API 키 탭으로 이동
  3. API 키 추가 버튼을 클릭 



  1. 프라이빗 키 다운로드 버튼을 클릭 파일을 다운로드 
  2. 다운로드한 비밀키 파일을 스프링 부팅 애플리케이션 루트에 복사
  3. 추가 버튼을 를 클릭 API 키를 추가 
  4. 구성 파일 미리보기 복사 링크를 클릭하여 구성파일 내용을 복사
  5. 스프링 부팅 애플리케이션 프로젝트 루트 폴더에 파일 확장자 없이 "config"라는 이름의 파일을 만들고 복사한 콘텐츠를 추가.  config 파일에서 다운로드한 개인 키가 저장된 경로를 업데이트 (<path to your private keyfile>)




스프링 부트 의존성 추가 

스프링 부트 애플리케이션 프로젝트에 OCI Java SDK 사용을 위한 의존성을 build.gradle 파일에 추가한다. 추가되는 의존성은 아래와 같다.

  • implementation 'com.oracle.oci.sdk:oci-java-sdk:3.36.0'
  • implementation 'com.oracle.oci.sdk:oci-java-sdk-objectstorage:3.36.0'
  • implementation 'com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey:3.36.0'


OCI 오브젝트 스토리지 클라이언트 클래스 구현

다음으로 앞서 생성한 OCI 오브젝트 스토리지 접속을 위한 클라이언트 클래스를 구현한다.


SDK 에서 제공하는 API 는 앞서 저장한 OCI config file 과 비밀키를 읽어드리기 위하여 파일위치를 지정하도록 되어 있다. 배포의 용의성을 위해서 프로젝트 리소스에서 읽어드릴 수 있도록 SDK 소스를 일부를 아래와 같이 수정하여 사용하였다. (src/main/resources/oci 경로에 config 와 privite_key.pem 파일 복사, config 파일 key_file 값을 oci/private_key.pem 수정 )
  1. ConfigFileAuthenticationDetailsProvider -> ClasspathConfigFileAuthenticationDetailsProvider.java
  2. SimplePrivateKeySupplier.java -> ClasspathPrivateKeySupplier.java


OCI 오브젝트 스토리지 테스트 클래스 구현

OCI 오브젝트 스토이지에 파일을 추가하려면 먼저 버킷이 필요하다. 생성된 버킷 목록을 조회하려면 compartmentId 와 namespaceName 값이 요구된다. 

⑴ namespaceName 값은 우측 상단 사용자 아이콘을 클릭하고, 테넌시 이름을 클릭 테넌시 상세 페이지로 이동한다. 테넌시 정보 구획에서 오브젝트 스토이지 네임스페이스 값이 namespaceName 이다.  




⑵ compartmentId 는 OCI 콘솔 > ID& 보안 > 구획 메뉴를 통하여 확인 할 수 있다. 목록에서 이름을 클릭, "구획정보"섹션에서 "OCID"를 값을 확인할 수 있는데 이 값이 해당 구획(compartment) 의 compartmentId 값이다.


OCI 오브젝트 스토이지에 파일을 추가하는 것은 PutObjectRequest 을 생성하여 쉽게 구현할 수 있다. 주의할 점은 object name 은 버킷 내에서 고유 해야 한다는 점이다. 



이제 OCI 오브젝트 스토이지에 존재하는 파일 목록을 조회하는 테스트 코드를 만들어 본다. 오브젝트 스토이지는 위에서 생성한 것을 사용하였다.


bucket name 과 namespace 정보는 OCI 콘솔 > 스토이지 > 오브젝트 스토리지 및 아카이브 스토리지 버킷 목록 화면에서 생성된 버킷 이름을 클릭 상세 화면에서 확인할 수 있다.




파일 목록을 조회하고 객체에 대한 사전 인증된 요청 URL 을 생성하는 테스트 코드를 만들어 본다.

생성된 URL 은 1시간 동안 인증이 유효하며 사전 인증 요청들은 OCI 콘솔 > 스토이지 > 오브젝트 스토리지 및 아카이브 스토리지 > 버킷 > 버킷 세부정보의 좌측 리소스 목록중 사전인증된요청 을 클릭하면 내역을 확인할 수 있다. 

 

참고자료


2024년 2월 23일

코딩 - Cloud Ready : Spring Config Server

Spring Boot는 마이크로서비스 아키텍처(Microservices Architecture) 를 구현하는 데 많은 도구와 기능을 제공하고 있다. 이를 통해 애플리케이션을 작은 독립적인 서비스로 분할하고, 이러한 서비스를 개발, 배포 및 관리하는 데 도움을 준다.



이러한 기능중에서 유연성, 안정성, 보안성 향상을 위하여 설정의 집중화를 구현하는 Spring Cloud Config는 애플리케이션의 설정을 외부화하고 중앙에서 관리할 수 있게 한다. 이를 통해 설정 변경을 용이하게하고 여러 환경 간에 설정을 공유할 수 있다.

Config Server 구성하기


◼︎ 환경
  • Model : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 13.2.4 (22F66)
  • TOOLS : Visual Studio Code, Java 11, Gradle, Docker
  • Version Control : GitHub
  • Programming Language : Java
  • Framework : Spring Boot 2.7.12, Spring Security 5.7.7
  • DBMS : MySql 8.0.33
  • Cloud : Oracle Cloud Free Tier

Spring Cloud Config 서버 프로젝트는  ① gradle 명령을 사용하여 새로운 자바 응용프로그램 프로젝트를 생성하고 여기에 관련 의존성과 @SpringBootApplication 를 구현하는 클래스를 생성하는 방법과 ② Spring Initializer 을 이용하여 프로젝트를 다운로드하는 방식이 있다. 

① Gradle

brew 을 이용하여 gradle 을 설치한다. 


# brew update
# brew install gradle
# gradle init --type java-application

 Select build script DSL:
  1: Kotlin
  2: Groovy
 Enter selection (default: Kotlin) [1..2] 2

 Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
 Enter selection (default: JUnit Jupiter) [1..4] 4

 Project name (default: studio-config): 
 Enter target version of Java (min. 7) (default: 21): 11
 Generate build using new APIs and behavior (some features may change in the next minor release)?   (default: no) [yes, no] yes

 > Task :init
 To learn more about Gradle by exploring our Samples at   https://docs.gradle.org/8.6/samples/sample_building_java_applications.html

 BUILD SUCCESSFUL in 2m 17s
 1 actionable task: 1 executed

다음으로 build.gradle 파일에 org.springframework.cloud:spring-cloud-config-server 의존성을 추가한다.

build.gradle

dependencies {
implementation 'org.springframework.cloud:spring-cloud-config-server'
// lombok
implementation 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}

springCloudVersion 속성은 gradle.properties 에 정의했다.

springBootVersion=2.7.18
springDependencyManagementVersion=1.1.4
springCloudVersion=2021.0.8
sourceCompatibility=11


가장 어려웠던 부분이 spring cloud version 을 확인하는것 과정이었다. 

마지막으로 @EnableConfigServer 을 구현하는 서버 소스를 추가한다. (소스는 ② 생성되는 코드들을 참고했다)

package architecture.studio.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServer {

public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}

}

② Spring Initializer

브라우저를 열고 https://start.spring.io/ 에 접속한다.


config server 의존성을 선택하여 새로운 config server 프로젝트를 생성한다. (2.x 버전은 지원하지 않고 있다.)

Config Server 프로젝트 생성

이미 프로젝트가 존재했고 해당 프로젝트에서 config 을 분리하는 것으로 목표로 했기 때문에 ① 방식을 참고하여 서브 프로젝트를 추가하고 생성된 프로젝트에 설정을 추가하는 방식으로 구현했다.

존재하는 spring boot 프로젝트에 config-server 를 추가하는 경우는 "Supported Versions" 참고하여 호환되는 Spring Cloud 버전을 확인 하려 하였으나 https://spring.io/projects/spring-cloud 문서가 더 도움이 되었다.  문서에 따르면 2.6.x, 2.7.x 버전은 2021.0.x aka Jubilee 버전이 지원하고 있다.


2021.0 정보는 https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2021.0-Release-Notes 확인 가능한데  2024년 2월 기준으로 '2021.0.8' 이 가장 최신이다. 이제 해당 버전으로 의존성를 추가한다.

설정정보 저장소

대부분의 자료들이 github 을 저장소로 사용하여 config server 을 구성하고 있는데 로컬 파일을 사용하는 방식으로 구성했다. 로컬 파일들은 jar 파일에 포함되어 배포 될수 있도록 src/main/resources/config 경로에 [application]-[profile].yml 규칙으로 생성하였다. 주의 할것은 이 파일들은 "--" 문자를 포함하면 오류가 발생한다. 


config server 에 대한 설정은 application.yml 에 아래와 같이 설정하였다.

application.yml
---
logging:
level:
root: debug
---
server:
port: 8888
spring:
application:
name: studio-config
profiles:
active: native
cloud:
config:
server:
native:
searchLocations: classpath:/config, classpath:/config/{application}, classpath:/config/{application}/{profile}


웹 브라우져에서 http://localhost:8888/[application]/[profile] 형식으로 호출하여 프로파일에 따른 설정을 확인할 수 있다.

암호화 

비밀번호와 같은 중요 정보들을 암호화하여 설정 파일을 구성하기 위한 목적으로  Config Server 가 제공하는 암호화 기능을 사용하였다 

Config Server 는 암호화 구현을 위하여 대칭(공유) 키 또는 비대칭 키(RSA 키 쌍)를 사용하여 할 수 있다. 비대칭 키가 보안 측면에서 더 우수하지만, 대칭 키 방식은 application.yml(application.properties) 파일에  에 대칭 키 문자 하나만 설정하면 되기 떄문에 더 편리하다고 할 수 있다.

대칭 키를 설정은 encrypt.key 에 개인 키 암호를 설정해야 한다.  (또는 ENCRYPT_KEY 환경 변수를 사용하여 값을 지정하는 방법도 있다.)

비대칭 키 방식은  keystore (JDK 에 포함된 keytool 유틸리티로 생성) 을 사용하는 이유에서  아래 설정들이 필요하다.
  • encrypt.keyStore.location : keyStore 파일 위치
  • encrypt.keyStore.password : KeyStore 파일에 접근을 위한 비빌번호
  • encrypt.keyStore.alias : KeyStore 내의 엔트리를 식별하는 고유한 이름. KeyStore는 공개 키, 개인 키 쌍 및 이에 대한 인증서를 저장하는 데 사용되는데, 이러한 엔트리는 각각 고유한 이름인 alias로 식별됨
※KeyStore에 저장되는 개별 항목을 "엔트리(entry)"라고 한다. 이 엔트리는 주로 공개 키와 개인 키 쌍과 그에 대응하는 인증서를 포함한다.

KeyStore 를 생성하고 비대칭키를 설정하는 것은  "Spring Cloud Config - Creating a Key Store for Testing" 참고했다.

참고로 긴 기간동안 키가 유효하도록 아래와 같이 10년동안 유효하게 키 스토어를 생성했다. 

keytool -genkeypair -alias testalias -keyalg RSA -keysize 2048 \
  -dname "CN=ConfigServer, OU=none, O=none, L=none, ST=none, C=KR" \
  -keystore server.jks -keypass testkeypass  \
  -storepass teststorepass -validity 3650

생성된 키는 src/main/resources/key 경로에 복사하고 아래와 같이 application.yml 파일에 설정을 추가했다.

application.yml
keyStore:
location: classpath:/key/server.jks
password: *****
alias: studiokey
secret: *****

암호화된 문자는 접두사 {cipher} 사용하여 설정하고 Config Server 은 요청시 설정정보를 복호화하여 전송한다.

application.yml
spring:
datasource:
url: "{cipher}AQCnTtoaV9juWvUutYNWNVyx9Bd+E9zUf+Mf1"
username: "{cipher}AQCHbvX1Sa9VyjTfaFLP2uSesfTCe0Wn"
password: "{cipher}AQBUIaBt+nzhl+mbyOlSb5MStLWkvZ1wjW2iwByefg34aLs="


암호화

암호화를 위한 키 설정을 하고 나면 ConfigServer 가 제공하는 암호화와 복호화를 RESTful API 을 호출하여 데이터를 암호화 하고 값을 {cipher} 을 접두사로 하여 application.yml 파일에 값을 수정한다.  

Config Server 는 /encrypt 및 /decrypt 엔드포인트를 공개하고 있다(이들 엔드포인트는 안전하고 인증된 클라이언트만 접속 가능한 것으로 가정). 만약 설정 파일을 값을 암호화 하고자 하는 경우 아래와 같이 /encrypt 엔드포인트를 POST 로 호출하여 암호화 할 수 있다. 

curl -X POST \

  -H "Content-Type: application/json" \

  -d 'jdbc:mysql://xxx.xxx.xxx:3306/xxxdb?serverTimezone=Asia/Seoul' \          

  http://localhost:8888/encrypt


복호화 역시 동일한 방법으로 /decrypt 를 호출하면 된다.

Config Server 에서 설정 불러오기

Spring boot 프로그램에서 앞에서 생성한 Config Server 에서 설정을 읽어드리도록 하려면 ❶ Config Server 의 경우와 같이 org.springframework.cloud:spring-cloud-config-server 의존성을 추가하고 ❷
Config Server 에서 설정을 읽어드리도록 application.yml 설정을 추가한다. 

/application.yml

logging:
level:
root: INFO
spring:
application:
name: studio
config:
import: optional:configserver:http://localhost:8888


추가로 dev , prod 프로파일을 application.yml 이 아닌 테스트를 위하여 프로그램을 실행 할 떄 prod 인자를 사용하여 개발 과 운영 모드로 동작할 수 있도록 아래와 같은 설정을 build.gradle 에 추가하였다. 이를 통하여 개발과 운영을 동일한 코드로 구현하고 테스트를 위하여 프로그램을 실행할 떄 프로파일을 인자로 넘겨서 처리하도록 할 수 있었다.  

build.gradle

tasks.named("bootRun") {
if (project.hasProperty('profile')) {
jvmArgs = ["-Dspring.profiles.active=${project.getProperty('profile')}"]
}
}



./gradlew bootRun -Pprofile=dev

locathost:8888/studio/dev 에 해당하는 /config/studio-dev.yml 을 읽어드림.


./gradlew bootRun -Pprofile=prod 

locathost:8888/studio/prod 에 해당하는 /config/studio-prod.yml 을 읽어드림.


참고 설정 및 소스