Skip to content

Secure Key Manager #227

@hyeonjaez

Description

@hyeonjaez

Secure Key Manager


  • 기밀 데이터(데이터베이스 접속 정보, 앱키, 비밀번호 등), 대칭키, 비대칭키와 같이 애플리케이션 서버에 저장할 경우 보안 위험에 노출될 수 있는 데이터를 중앙 집중적으로 안전하게 관리하고, 인증을 통과한 클라이언트만 접근할 수 있게 제어하는 서비스입니다.

  • 정보보호관리체계(ISMS) 인증 심사의 요구사항인 개인정보 암호화 키를 물리적으로 분리 보관하여야 한다는 요건을 만족시키기 위한 고객사/개발사를 위한 서비스이다.

  • 공개 키 암호 표준 PKCS(Public-Key Cyptography Standard) RSA 시큐리티에서 정한, 공개 키 암호에 대한 사용 방식에 대한 표준 프로토콜을 따르는 서비스

  • Personal Information Exchange Syntax Standard 개인 정보 교환 표준 사용자의 개인키, 인증 등의 저장과 교환을 위한 포맷을 설명하는 파일이다.

  • 확장자는 .p12, .pfx 를 가진다.

구조


  • 루트키와 시스템 키 라는 두개의 암호키를 내부적으로 사용한다.
  • 루트키는 시스템키를 보호하기 위해 사용
  • 시스템키는 사용자 데이터를 보호하기 위해 사용

Image

기능

  • 데이터 관리

    • 기밀 데이터 등록, 관리, 조회
    • 대칭키 생성, 관리, 회전, 데이터 암호화, 복호화, 조회
    • 비대칭키 생성, 관리, 회전, 데이터 서명, 데이터 검증, 조회
  • 데이터 접근 제어

    • 클라이언트 IPv4 주소를 사용한 데이터 접근 제어
    • 클라이언트 MAC 주소를 사용한 데이터 접근 제어
    • 클라이언트 인증서를 사용한 데이터 접근 제어
  • 승인기능

    • 데이터, 데이터 접근 제어의 변경을 승인자, 요청자로 나누어 관리

사용

1.1 인증서 관리

Image

1.2 인증서 등록

Image

1.3 인증서 파일

인증서는 .p12 파일로 다운로드 된다.
이 파일은 PKCS #12 형식을 사용하는 인증서 입니다.
이 인증서는 프로젝트의 resource 폴더에 위치 시키면 됩니다.

Image

1.4 키 관리

Image

1.5 키 추가

Image

1.6 키 상세 정보

Image

2. 프로젝트 로직 설정


2.1 DTO 클래스

@Getter
public class KeyResponseDto {
    private Header header;
    private Body body;

    @Getter
    @NoArgsConstructor
    public static class Body {
        private String secret;
    }
 
    @Getter
    @NoArgsConstructor
    public static class Header {
        private Integer resultCode;
        private String resultMessage;
        private boolean isSuccessful;
    }
}

###2.2 API

기밀 데이터 조회 api

Image

조회 코드

@Configuration
public class KeyConfig {

    private final KeyProperties keyProperties;

    @Autowired
    public KeyConfig(KeyProperties keyProperties) {
        this.keyProperties = keyProperties;
    }

    public String keyStore(String keyId) {
        try {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            InputStream result = new ClassPathResource("my-books.p12").getInputStream();
            clientStore.load(result, keyProperties.getPassword().toCharArray());

            SSLContext sslContext = SSLContextBuilder.create()
                    .setProtocol("TLS")
                    .loadKeyMaterial(clientStore, keyProperties.getPassword().toCharArray())
                    .loadTrustMaterial(new TrustSelfSignedStrategy())
                    .build();

            SSLConnectionSocketFactory sslConnectionSocketFactory =
                    new SSLConnectionSocketFactory(sslContext);
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(sslConnectionSocketFactory)
                    .build();

            HttpComponentsClientHttpRequestFactory requestFactory =
                    new HttpComponentsClientHttpRequestFactory(httpClient);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.setAccept(List.of(MediaType.APPLICATION_JSON));

            RestTemplate restTemplate = new RestTemplate(requestFactory);

            URI uri = UriComponentsBuilder
                    .fromUriString(keyProperties.getUrl())
                    .path(keyProperties.getPath())
                    .encode()
                    .build()
                    .expand(keyProperties.getAppKey(), keyId)
                    .toUri();
            return Objects.requireNonNull(restTemplate.exchange(uri,
                                    HttpMethod.GET,
                                    new HttpEntity<>(headers),
                                    KeyResponseDto.class)
                            .getBody())
                    .getBody()
                    .getSecret();
        } catch (KeyStoreException | IOException | CertificateException
                 | NoSuchAlgorithmException
                 | UnrecoverableKeyException
                 | KeyManagementException e) {
            throw new KeyMangerException(e.getMessage());
        }
    }
}

2.3 키 복호화 하여 사용

키값을 properties파일에 저장한다

Image

Properties에 있는 키값을 가지고 있는 클래스를 빈으로 등록

Image

keyConfig.keyStore(databaseProperties.getUrl())를 호출하여 실제 값을 복호화 하여 가져온다

@Configuration
public class DatabaseConfig {
    private final KeyConfig keyConfig;
    private final DatabaseProperties databaseProperties;

    @Autowired
    public DatabaseConfig(KeyConfig keyConfig, DatabaseProperties databaseProperties) {
        this.keyConfig = keyConfig;
        this.databaseProperties = databaseProperties;
    }

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();

        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl(keyConfig.keyStore(databaseProperties.getUrl()));
        dataSource.setUsername(keyConfig.keyStore(databaseProperties.getUserName()));
        dataSource.setPassword(keyConfig.keyStore(databaseProperties.getPassword()));

        dataSource.setInitialSize(databaseProperties.getInitialSize());
        dataSource.setMaxTotal(databaseProperties.getMaxTotal());
        dataSource.setMinIdle(databaseProperties.getMinIdle());
        dataSource.setMaxIdle(databaseProperties.getMaxIdle());

        dataSource.setMaxWaitMillis(databaseProperties.getMaxWait());

        dataSource.setTestOnBorrow(true);
        dataSource.setTestOnReturn(true);
        dataSource.setTestWhileIdle(true);

        return dataSource;
    }

Reference

https://docs.nhncloud.com/ko/Security/Secure%20Key%20Manager/ko/overview/

Metadata

Metadata

Assignees

Labels

technical기술 문서 작성

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions