

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Hive에서 Parquet 모듈식 암호화
<a name="hive-parquet-modular-encryption"></a>

Parquet 모듈식 암호화는 열 수준 액세스 제어 및 암호화를 제공하여 Parquet 파일 형식으로 저장된 데이터의 개인 정보 보호 및 데이터 무결성을 개선합니다. 이 기능은 릴리스 6.6.0부터 Amazon EMR Hive에서 사용할 수 있습니다.

파일 암호화 또는 스토리지 계층 암호화를 포함하여 이전에 지원되는 보안 및 무결성 솔루션은 Amazon EMR 관리 안내서의 [암호화 옵션](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-data-encryption-options.html)에 설명합니다. 이러한 솔루션을 Parquet 파일에 사용할 수 있지만, 통합된 Parquet 암호화 메커니즘의 새로운 기능을 활용하면 열 수준에 대한 세분화된 액세스가 가능하고 성능 및 보안이 개선됩니다. Apache github 페이지 [Parquet Modular Encryption](https://github.com/apache/parquet-format/blob/master/Encryption.md)에서 이 기능에 대해 자세히 알아보세요.

사용자는 Hadoop 구성을 사용하여 Parquet 리더와 라이터에 구성을 전달합니다. 사용자가 암호화를 활성화하도록 리더와 라이터를 구성하고 고급 기능을 전환하는 방법에 대한 자세한 구성은 [PARQUET-1854: Properties-driven Interface to Parquet Encryption Management](https://docs.google.com/document/d/1boH6HPkG0ZhgxcaRkGk3QpZ8X_J91uXZwVGwYN45St4/edit)에 나와 있습니다.

## 사용 예제
<a name="usage-examples"></a>

다음 예제에서는 암호화 키를 관리하는 데 AWS KMS 를 사용하여 Hive 테이블을 생성하고 여기에 데이터를 작성하는 방법을 설명합니다.

1. [PARQUET-1373: Encryption Key Management Tools](https://docs.google.com/document/d/1bEu903840yb95k9q2X-BlsYKuXoygE4VnMDl9xz_zhk/edit) 문서에 설명된 대로 AWS KMS 서비스에 대한 KmsClient를 구현합니다. 다음 샘플은 구현 스니펫을 보여줍니다.

   ```
   package org.apache.parquet.crypto.keytools;
   
   import com.amazonaws.AmazonClientException;
   import com.amazonaws.AmazonServiceException;
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.kms.AWSKMS;
   import com.amazonaws.services.kms.AWSKMSClientBuilder;
   import com.amazonaws.services.kms.model.DecryptRequest;
   import com.amazonaws.services.kms.model.EncryptRequest;
   import com.amazonaws.util.Base64;
   import org.apache.hadoop.conf.Configuration;
   import org.apache.parquet.crypto.KeyAccessDeniedException;
   import org.apache.parquet.crypto.ParquetCryptoRuntimeException;
   import org.apache.parquet.crypto.keytools.KmsClient;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   
   import java.nio.ByteBuffer;
   import java.nio.charset.Charset;
   import java.nio.charset.StandardCharsets;
   
   public class AwsKmsClient implements KmsClient {
   
       private static final AWSKMS AWSKMS_CLIENT = AWSKMSClientBuilder
               .standard()
               .withRegion(Regions.US_WEST_2)
               .build();
       public static final Logger LOG = LoggerFactory.getLogger(AwsKmsClient.class);
   
       private String kmsToken;
       private Configuration hadoopConfiguration;
   
       @Override
       public void initialize(Configuration configuration, String kmsInstanceID, String kmsInstanceURL, String accessToken) throws KeyAccessDeniedException {
           hadoopConfiguration = configuration;
           kmsToken = accessToken;
   
       }
   
       @Override
       public String wrapKey(byte[] keyBytes, String masterKeyIdentifier) throws KeyAccessDeniedException {
           String value = null;
           try {
               ByteBuffer plaintext = ByteBuffer.wrap(keyBytes);
   
               EncryptRequest req = new EncryptRequest().withKeyId(masterKeyIdentifier).withPlaintext(plaintext);
               ByteBuffer ciphertext = AWSKMS_CLIENT.encrypt(req).getCiphertextBlob();
   
               byte[] base64EncodedValue = Base64.encode(ciphertext.array());
               value = new String(base64EncodedValue, Charset.forName("UTF-8"));
           } catch (AmazonClientException ae) {
               throw new KeyAccessDeniedException(ae.getMessage());
           }
           return value;
       }
   
       @Override
       public byte[] unwrapKey(String wrappedKey, String masterKeyIdentifier) throws KeyAccessDeniedException {
           byte[] arr = null;
           try {
               ByteBuffer ciphertext  = ByteBuffer.wrap(Base64.decode(wrappedKey.getBytes(StandardCharsets.UTF_8)));
               DecryptRequest request = new DecryptRequest().withKeyId(masterKeyIdentifier).withCiphertextBlob(ciphertext);
               ByteBuffer decipheredtext = AWSKMS_CLIENT.decrypt(request).getPlaintext();
               arr = new byte[decipheredtext.remaining()];
               decipheredtext.get(arr);
           } catch (AmazonClientException ae) {
               throw new KeyAccessDeniedException(ae.getMessage());
           }
           return arr;
       }
   }
   ```

1. *AWS Key Management Service 개발자 안내서*의 키 [생성](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html)에 설명된 대로 바닥글에 대한 AWS KMS 암호화 키와 IAM 역할이 액세스할 수 있는 열을 생성합니다. 기본 IAM 역할은 EMR\_ECS\_default입니다.

1. Amazon EMR 클러스터의 Hive 애플리케이션에서 [Apache Hive 리소스 설명서](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Cli#LanguageManualCli-HiveResources)에 설명된 대로 `ADD JAR` 문을 사용하여 위의 클라이언트를 추가합니다. 다음은 명령문 예제입니다.

   ```
   ADD JAR 's3://location-to-custom-jar';
   ```

   다른 방법은 부트스트랩 작업을 사용하여 JAR을 Hive의 `auxlib`에 추가하는 것입니다. 다음은 부트스트랩 작업에 추가할 예제 줄입니다.

   ```
   aws s3 cp 's3://location-to-custom-jar' /usr/lib/hive/auxlib 
   ```

1. 다음과 같은 구성을 설정합니다.

   ```
   set parquet.crypto.factory.class=org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory;
   set parquet.encryption.kms.client.class=org.apache.parquet.crypto.keytools.AwsKmsClient;
   ```

1. Parquet 형식의 Hive 테이블을 생성하고 SERDEPROPERTIES에서 AWS KMS 키를 지정하고 여기에 일부 데이터를 삽입합니다.

   ```
   CREATE TABLE my_table(name STRING, credit_card STRING)
   ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe’
   WITH SERDEPROPERTIES (
     'parquet.encryption.column.key’=<aws-kms-key-id-for-column-1>: credit_card’,
     'parquet.encryption.footer.key’='<aws-kms-key-id-for-footer>’)
   STORED AS parquet
   LOCATION “s3://<bucket/<warehouse-location>/my_table”;
   
   INSERT INTO my_table SELECT 
   java_method ('org.apache.commons.lang.RandomStringUtils','randomAlphabetic',5) as name,
   java_method ('org.apache.commons.lang.RandomStringUtils','randomAlphabetic',10) as credit_card
   from (select 1) x lateral view posexplode(split(space(100),' ')) pe as i,x;
   
   select * from my_table;
   ```

1.  AWS KMS 키에 대한 액세스 권한이 없는 동일한 위치에 외부 테이블을 생성할 때(예: IAM 역할 액세스가 거부됨) 데이터를 읽을 수 없는지 확인합니다.

   ```
   CREATE EXTERNAL TABLE ext_table (name STRING, credit_card STRING)
   ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe’
   STORED AS parquet
   LOCATION “s3://<bucket>/<warehouse-location>/my_table”;
   
   SELECT * FROM ext_table;
   ```

1. 마지막 명령문에서 다음 예외가 발생합니다.

   ```
   Failed with exception java.io.IOException:org.apache.parquet.crypto.KeyAccessDeniedException: Footer key: access denied
   ```