文档首页/ 数据加密服务 DEW/ 最佳实践/ 密钥管理/ 如何使用KMS对文件进行完整性保护
更新时间:2024-05-09 GMT+08:00
分享

如何使用KMS对文件进行完整性保护

应用场景

当有大量文件(例如:镜像、电子保单或者重要文件等)需要在传输或者存储时确保安全性,用户可以使用KMS对文件摘要进行签名,再次使用时可以重新计算摘要进行验签。确保文件在传输或者存储过程中没有被篡改。

解决方案

用户需要在KMS中创建一个用户主密钥。

用户计算文件的摘要,调用KMS的“sign”接口对摘要进行签名。用户得到摘要的签名结果。将摘要签名结果和密钥ID与文件一同传输或者存储。签名流程如图 签名流程所示。

图1 签名流程

用户需要使用文件时,先进行完整性校验,确保文件未被篡改。

用户重新计算文件的摘要,连同签名值调用KMS的“verify”接口对摘要进行验签。用户得到验签结果。如果能正常验签,则表明文件未被篡改。验签流程如图 验签流程所示。

图2 验签流程

操作步骤

  1. 获取AK/SK:

    • ACCESS_KEY: 华为账号Access Key,获取方式请参见获取AK/SK
    • SECRET_ACCESS_KEY: 华为账号Secret Access Key,获取方式请参见获取AK/SK
    • 认证用的ak和sk直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全。
    • 本示例以ak和sk保存在环境变量中来实现身份验证为例,运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。

  2. 获取region相关信息:

  3. 使用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());
            }
        }
     
    }

相关文档