
# 如何使用KMS对文件进行完整性保护
#### 应用场景
当有大量文件（例如：镜像、电子保单或者重要文件等）需要在传输或者存储时确保安全性，用户可以使用KMS对文件摘要进行签名，再次使用时可以重新计算摘要进行验签。确保文件在传输或者存储过程中没有被篡改。
#### 解决方案
用户需要在KMS中创建一个用户主密钥。
用户计算文件的摘要，调用KMS的"sign"接口对摘要进行签名。用户得到摘要的签名结果。将摘要签名结果和密钥ID与文件一同传输或者存储。签名流程如[图 签名流程]所示。
图1签名流程   
![](https://support.huaweicloud.com/bestpractice-dew/zh-cn_image_0000001719655880.png "点击放大")
用户需要使用文件时，先进行完整性校验，确保文件未被篡改。
用户重新计算文件的摘要，连同签名值调用KMS的"verify"接口对摘要进行验签。用户得到验签结果。如果能正常验签，则表明文件未被篡改。验签流程如[图 验签流程]所示。
图2验签流程   
![](https://support.huaweicloud.com/bestpractice-dew/zh-cn_image_0000001767457217.png "点击放大")
#### 操作步骤
1. 获取AK/SK： 
   - ACCESS_KEY：华为账号Access Key，获取方式请参见[获取AK/SK](https://support.huaweicloud.com/iam_faq/iam_01_0618.html)。
   
   - SECRET_ACCESS_KEY：华为账号Secret Access Key，获取方式请参见[获取AK/SK](https://support.huaweicloud.com/iam_faq/iam_01_0618.html)。
   
   - PROJECT_ID：局点项目ID，获取方式请参见[获取项目ID](https://support.huaweicloud.com/api-iam/iam_17_0002.html)。
   
   - KMS_ENDPOINT：华为云KMS服务访问终端地址，获取方式请参见[终端节点](https://support.huaweicloud.com/api-dew/dew_02_0052.html#section2)。
   
   - 认证用的ak和sk直接写到代码中有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全。
   
   - 本示例以ak和sk保存在环境变量中来实现身份验证为例，运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。
   
   
   
   
2. 使用KMS对文件进行签名并验签。 
   ```
   public class FileStreamSignVerifyExample {
    
       /**
        * 基础认证信息：
        * - ACCESS_KEY: 华为云账号Access Key
        * - SECRET_ACCESS_KEY: 华为云账号Secret Access Key, 敏感信息，建议密文存储
        * - IAM_ENDPOINT: 华为云IAM服务访问终端地址,详情见终端节点
        * - KMS_REGION_ID: 华为云KMS支持的地域,详情见KMS地域KMS支持地域
        * - KMS_ENDPOINT: 华为云KMS服务访问终端地址,详情见终端地址终端地址
        */
       private static final String ACCESS_KEY = System.getenv("HUAWEICLOUD_SDK_AK");
       private static final String SECRET_ACCESS_KEY = System.getenv("HUAWEICLOUD_SDK_SK");
       private static final String IAM_ENDPOINT = "https://<IamEndpoint>";
       private static final String KMS_REGION_ID = "<RegionId>";
       private static final String KMS_ENDPOINT = "https://<KmsEndpoint>";
    
       public static void main(String[] args) {
           // 用户主密钥ID,需要选择密钥及用途包含SIGN_VERIFY的密钥
           final String keyId = args[0];
    
           signAndVerifyFile(keyId);
       }
    
       /**
        * 使用KMS对文件进行签名和验签
        *
        * @param keyId 用户主密钥ID
        */
       static void signAndVerifyFile(String keyId) {
           // 1.准备访问华为云的认证信息
           final BasicCredentials auth = new BasicCredentials()
                   .withIamEndpoint(IAM_ENDPOINT).withAk(ACCESS_KEY).withSk(SECRET_ACCESS_KEY);
    
           // 2.初始化SDK，传入认证信息及KMS访问终端地址
           final KmsClient kmsClient = KmsClient.newBuilder()
                   .withRegion(new Region(KMS_REGION_ID, KMS_ENDPOINT)).withCredential(auth).build();
    
           // 3.准备待签名的文件
           // inFile 待签名的文件
           final File inFile = new File("FirstSignFile.iso");
           final String fileSha256Sum = getFileSha256Sum(inFile);
    
           // 4.计算摘要，需要根据密钥类型，选择合适的签名算法
           final SignRequest signRequest = new SignRequest().withBody(
                   new SignRequestBody().withKeyId(keyId).withSigningAlgorithm(SignRequestBody.SigningAlgorithmEnum.RSASSA_PSS_SHA_256)
                           .withMessageType(SignRequestBody.MessageTypeEnum.DIGEST).withMessage(fileSha256Sum));
    
           final SignResponse signResponse = kmsClient.sign(signRequest);
    
           // 5.验证摘要
           final ValidateSignatureRequest validateSignatureRequest = new ValidateSignatureRequest().withBody(
                   new VerifyRequestBody().withKeyId(keyId).withMessage(fileSha256Sum).withSignature(signResponse.getSignature())
                           .withSigningAlgorithm(VerifyRequestBody.SigningAlgorithmEnum.RSASSA_PSS_SHA_256)
                           .withMessageType(VerifyRequestBody.MessageTypeEnum.DIGEST));
           final ValidateSignatureResponse validateSignatureResponse = kmsClient.validateSignature(validateSignatureRequest);
    
           // 6.比对摘要结果
           assert validateSignatureResponse.getSignatureValid().equalsIgnoreCase("true");
    
       }
    
       /**
        * 计算文件SHA256摘要
        *
        * @param file 文件
        * @return SHA256摘要, Base64格式
        */
       static String getFileSha256Sum(File file) {
           int length;
           MessageDigest sha256;
           byte[] buffer = new byte[1024];
           try {
               sha256 = MessageDigest.getInstance("SHA-256");
           } catch (NoSuchAlgorithmException e) {
               throw new RuntimeException(e.getMessage());
           }
           try (FileInputStream inputStream = new FileInputStream(file)) {
               while ((length = inputStream.read(buffer)) != -1) {
                   sha256.update(buffer, 0, length);
               }
               return Base64.getEncoder().encodeToString(sha256.digest());
           } catch (IOException e) {
               throw new RuntimeException(e.getMessage());
           }
       }
    
   }
   ```
   
   
 
