์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ TLS 1.3์„ ์‚ฌ์šฉํ•˜์—ฌ ์•ˆ์ „ํ•œ HTTPS ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Apache HttpComponents ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ RestTemplate์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค

1. TLS๋ž€?

TLS(Transport Layer Security)๋Š” ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์•”ํ˜ธํ™”ํ•˜์—ฌ ๋ณด์•ˆ์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. TLS๋Š” ๋ฐ์ดํ„ฐ์˜ ๊ธฐ๋ฐ€์„ฑ๊ณผ ๋ฌด๊ฒฐ์„ฑ์„ ๋ณด์žฅํ•˜๋ฉฐ, ์•ˆ์ „ํ•œ ํ†ต์‹ ์„ ์œ„ํ•ด HTTPS์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

2. ์™œ TLS 1.3์„ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

TLS 1.3์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ๋ณด์•ˆ ๊ฐ•ํ™”: TLS 1.3์€ ์ด์ „ ๋ฒ„์ „๋ณด๋‹ค ํ–ฅ์ƒ๋œ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.
  2. ์„ฑ๋Šฅ ํ–ฅ์ƒ: ํ•ธ๋“œ์…ฐ์ดํฌ ๊ณผ์ •์ด ๋‹จ์ˆœํ™”๋˜์–ด ์—ฐ๊ฒฐ ์„ค์ • ์‹œ๊ฐ„์ด ๋‹จ์ถ•๋˜๊ณ  ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.
  3. ํ˜ธํ™˜์„ฑ: ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์™€ ํด๋ผ์ด์–ธํŠธ๋Š” TLS 1.3์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ํ˜ธํ™˜์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

3. Gradle ์˜์กด์„ฑ ์ถ”๊ฐ€

Spring Boot์—์„œ TLS 1.3์„ ์ง€์›ํ•˜๋Š” RestTemplate์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ Apache HttpComponents ์˜์กด์„ฑ์„ build.gradle ํŒŒ์ผ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

์ €๋Š” ๊ฐ€์žฅ ์ตœ์‹  ๋ฒ„์ „์ธ 5.4-alpha2 5.3-alpha2 ๋ฅผ ์ฐพ์•„์„œ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

mvnRepository ์ฐธ๊ณ 

dependencies {
    // ๊ธฐํƒ€ ์ข…์†์„ฑ

   // Apache HttpComponents dependencies
    implementation group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.4-alpha2'
    implementation group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.3-alpha2'
}

4. RestTemplateConfig ํด๋ž˜์Šค ์ž‘์„ฑ

RestTemplate์„ TLS 1.3์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด RestTemplateConfig ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋ฒ„์ „์˜ Apache HttpComponents ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SSL ์ปจํ…์ŠคํŠธ์™€ ์—ฐ๊ฒฐ ํ’€์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

RestTemplateConfig.java

package enha.eodilo.config;

import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.TLS;
import org.apache.hc.core5.util.Timeout;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
        SSLContext sslContext = SSLContextBuilder.create()
                .loadTrustMaterial(null, (chain, authType) -> true) // ๋ชจ๋“  ์ธ์ฆ์„œ๋ฅผ ์‹ ๋ขฐํ•˜๋„๋ก ์„ค์ •
                .build();

        DefaultClientTlsStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext);

        PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
                .setTlsStrategy(tlsStrategy)
                .setDefaultTlsConfig(tlsConfig -> tlsConfig
                        .setHandshakeTimeout(Timeout.ofSeconds(30)) // ํ•ธ๋“œ์…ฐ์ดํฌ 30์ดˆ ์ œํ•œ
                        .setSupportedProtocols(TLS.V_1_3)) // TLS 1.3 ์‚ฌ์šฉ
                .build();

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .build();

        HttpComponentsClientHttpRequestFactory factory =
                new HttpComponentsClientHttpRequestFactory(httpClient);

        return new RestTemplate(factory);
    }
}

์„ค๋ช…

  1. SSLContext ์„ค์ •: SSLContextBuilder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ TLS 1.3์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  1. SSLContext sslContext = SSLContextBuilder.create() .loadTrustMaterial(null, (chain, authType) -> true) // ๋ชจ๋“  ์ธ์ฆ์„œ๋ฅผ ์‹ ๋ขฐํ•˜๋„๋ก ์„ค์ • .build();

  2. PoolingHttpClientConnectionManager ๊ตฌ์„ฑ: PoolingHttpClientConnectionManagerBuilder์™€ DefaultClientTlsStrategy๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SSL ์†Œ์ผ“ ํŒฉํ† ๋ฆฌ๋ฅผ ์„ค์ •ํ•˜๊ณ  ์—ฐ๊ฒฐ ํ’€์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.`
  1. DefaultClientTlsStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext); PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() .setTlsStrategy(tlsStrategy) .setDefaultTlsConfig(tlsConfig -> tlsConfig .setHandshakeTimeout(Timeout.ofSeconds(30)) // ํ•ธ๋“œ์…ฐ์ดํฌ 30์ดˆ ์ œํ•œ .setSupportedProtocols(TLS.V_1_3)) // TLS 1.3 ์‚ฌ์šฉ .build();

  2. CloseableHttpClient ์ƒ์„ฑ: ๊ตฌ์„ฑ๋œ PoolingHttpClientConnectionManager๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CloseableHttpClient๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  1. CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .build();

  2. HttpComponentsClientHttpRequestFactory ์‚ฌ์šฉ: CloseableHttpClient๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HttpComponentsClientHttpRequestFactory๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ RestTemplate์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  3. HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); return new RestTemplate(factory);

5. ํ…Œ์ŠคํŠธ ๋ฐ ์‚ฌ์šฉ

์ด์ œ ์„ค์ •์ด ์™„๋ฃŒ๋œ RestTemplate ๋นˆ์„ ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. RestTemplate์„ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋น„์Šค ํด๋ž˜์Šค์—์„œ ์ž๋™ ์ฃผ์ž…ํ•˜์—ฌ HTTPS ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MyService.java

@Service
public class MyService {

    @Autowired
    private RestTemplate restTemplate;

    public String getSecureData() {
        ResponseEntity<String> response = restTemplate.getForEntity("https://secure.example.com/api/data", String.class);
        return response.getBody();
    }
}

6. ์ฐธ๊ณ  ์ž๋ฃŒ

์ด๋ฒˆ ์„ค์ • ๋ณ€๊ฒฝ ์ „์— setSSLSocketFactory ๋ฐ SSLConnectionSocketFactoryBuilder์˜ deprecation ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด ์ตœ์‹  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” ๋” ์ด์ƒ ๊ถŒ์žฅ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ DefaultClientTlsStrategy ์™€ PoolingHttpClientConnectionManagerBuilder ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ TLS ์„ค์ •์„ ๊ตฌ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

'setSSLSocketFactory(org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory)' is deprecated  
'org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder' is deprecated  

Apache HttpComponents Migration Guide๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์ด์ „ ํฌ์ŠคํŒ…

 

mac springboot ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ SSL ์ธ์ฆ์„œ ์ƒ์„ฑํ•˜๊ธฐ (mkcert ์‚ฌ์šฉ)

๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ HTTPS๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ์™€ ์‹ค์ œ ๋ฐฐํฌ ํ™˜๊ฒฝ๊ณผ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” mkcert๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋กœ์ปฌ SSL ์ธ์ฆ์„œ๋ฅผ ์ƒ

jakezo.tistory.com

 

๊ณผ ๋งŽ์ด ์—ฐ๊ณ„๊ฐ€ ๋˜๋Š” ๋‚ด์šฉ์ด๋‹ค. ์‹ค์ œ๋กœ ๋‚˜๋Š” ์ด์ „ ์„ค์ •(SSL)์„ ํ•œ ํ›„์— ์ด ํฌ์ŠคํŒ… ๋‚ด์šฉ์„ ์ง„ํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

TLS 1.3 HttpComponents SSL ๋“ฑ ๋ง๋กœ๋งŒ ๋“ค์—ˆ์ง€ ์ •๋ฆฌ๊ฐ€ ์•ˆ๋œ ๋‚ด์šฉ๋“ค์ด ๋งŽ์•˜๋Š”๋ฐ ์ด๋ฒˆ ํฌ์ŠคํŒ…์„ ํ•˜๋ฉด์„œ ๋‹ค์‹œ ์ •๋ฆฌํ•ด๋ณด๋Š” ๊ธฐํšŒ๋ฅผ ๊ฐ€์ ธ์„œ ์ข‹์•˜๋‹ค.

 

์›น๊ฐœ๋ฐœ์„ ํ•˜๋‹ค๋ณด๋ฉด ๊ผญ ๊ตฌ์„ฑํ•ด์•ผํ•˜๋Š” ๋‚ด์šฉ์ด๊ธฐ์—(SSL ์„ธํŒ…์„ ํ•˜์ง€์•Š์œผ๋ฉด ํฌ๋กฌ๋“ฑ ์—ฌ๋Ÿฌ๊ณณ์—์„œ ๋ถˆ์ด์ต์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.) ์ •๋ฆฌ๊ฐ€ ๊ผญ ํ•„์š”ํ–ˆ๋‹ค.

๋˜ํ•œ setSSLSocketFactory ๋ฐ SSLConnectionSocketFactoryBuilder ๊ฐ€ deprecated ๋˜์—ˆ๋‹ค๋Š” ๋‚ด์šฉ๋„ ์•„๋ฌด๋ฆฌ stackoverflow ๋กœ ๊ฒ€์ƒ‰์„ ํ•ด๋„ ๋‚˜์˜ค์ง€ ์•Š์•˜๋Š”๋ฐ,
์—ญ์‹œ ์ด๋Ÿด๋•Œ๋Š” ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋Š”๊ฒŒ ๋งž๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.