低代码到底是不是行业毒瘤?一线大厂怎么做的?戳此了解>>> 了解详情
写点什么

当 DR 灾备遇到 KMS(一)

2019 年 12 月 20 日

当 DR 灾备遇到 KMS(一)

中国区的 KMS 服务已经发布,为加密的需求提供了便利的解决方案。但是注意到为了满足安全的要求,KMS 的 CMK 是不允许出区域的,在这种情况下,已经加密的资料,如何能够在另外的区域使用?采用了 KMS 加密的方案,如何实施灾备呢?


一般来说,各种应用场景可以归类为服务器端加密和客户端加密两种实现方式,针对着两种不同的实现方式,本文都提供了相应的解决方案建议。


【应用场景和对应方案】

1. 场景一、服务器端加密的 DR 方案

服务器端加密主要是指 KMS 与托管服务集成的应用场景。这样的场景下的 DR 方案,代码无感,无需研发人员修改代码。

1.1 信封加密的基本原理

为了实现对 DR 的支持,需要借助信封加密的方法。先回顾信封加密的原理。



在信封加密中,首先需要创建在 KMS 中创建一个 CMK(Customer Master Key);其次,在创建数据库或者 EBS 卷的时候,选择使用刚才创建的 CMK 加密。实际上,系统服务在后台创建了一个 data encryption key(DEK)真正用于加密的数据。这个 DEK 则需要使用之前指定的 CMK 进行加密保护。加密后的 DEK 则会跟相应的业务(RDS/EBS)保存在一起。


信封加密之加密过程

服务器根据客户选择的 CMK,生成一个 DEK,利用 DEK 加密数据,然后用 CMK 加密 DEK,将加密的 DEK 和数据密文放在一个信封里。



信封加密之解密过程

从信封中取出加密的 DEK,通过 CMK 解密后得到明文的 DEK,使用 DEK 对数据密文进行解密,得到明文数据。



信封加密在 DR 方案中应用示意


而在 DR 场景中,数据密文和 DEK 明文在两个不同的区域间是保存不变的,这样就保证了复制到两个区域的数据是被同一个 DEK 加密的,就能使用同一个 DEK 进行解密。关键在于在主区域中的 DEK,是用主区域的 CMK 进行加密保护;而在数据跨区域复制过程中,DEK 会用主区域的 CMK 解密后,将明文的 DEK 使用备份区域的 CMK 进行加密保护。这样,借助信封加密的方法,避免了对海量的源数据进行解密和重新加密的过程,高效实现加密数据的跨区域备份。


1.2 KMS 加密的 EBS 卷,跨区域复制方法

在数据解密的过程中,以 EBS 解密为例。当 EC2 需要读取加密的 EBS 卷,保存在 EBS 上的加密的 DEK,会通过接口,发送到 KMS 服务,通过相应的 CMK,对 DEK 进行解密,在这个过程中,传输的是 DEK,CMK 始终保存在 KMS 服务中,降低泄漏风险。通过接口返回明文的 DEK(当然,传输通道是加密的),EC2 就可以使用该明文的 DEK,对 EBS 进行解密,读取相应的数据。该过程对 EC2 上的应用程序是透明无感的,应用的代码不需要做任何修改。


关于如何将已有的 RDS 数据库转换为加密的数据库,请参见


https://amazonaws-china.com/cn/blogs/aws/amazon-rds-update-share-encrypted-snapshots-encrypt-existing-instances/


那么在 DR 场景下,如何完成对加密的 EBS 卷的夸区域复制呢?


在信封加密中,用户数据实际上是被 DEK 加密的,而加密的 DEK 会跟 EBS 卷保存在一起。因此,当我们需要将加密的 EBS 卷做垮区域的复制,以便支持 DR 的场景,需要解决的问题就是如何在新的区域能够将 DEK 解密,得到明文 DEK,就可以对数据解密了。


命令行的方式如下


以下代码示例将数据库快照从北京区域复制到宁夏区域并重新加密。在宁夏区域中运行命令。注意 kms-key-id 这个是目标区域宁夏的


Java


aws rds copy-db-snapshot \--source-db-snapshot-identifier arn:aws-cn:rds:cn-north-1:3264xxxxxx:snapshot:rds:test-2019-06-29-12-19 \--target-db-snapshot-identifier ningxia-encry-dbsnapshot \--source-region cn-north-1 \--kms-key-id xxxxx
复制代码


1.3 KMS 加密的 RDS 数据库,创建跨区域只读副本

对于使用了 KMS 加密的 RDS 数据库,可以按照以下步骤创建垮区域的只读副本


首先源区域创建使用 KMS 加密的数据库。


然后在控制台选定该数据库实例,针对该数据库创建只读副本操作;


对于目标区域 Destination region,选择要复制的目的区域,比如 ZHY;


切换到目标区域的 KMS 页面,记录目标区域使用的自建的 CMK 或者 AWS managed keys 的 ARN



在 Encryption 选项下面,篮框部分粘贴目标区域的 CMK 的 ARN



这两个步骤是最关键的,通过这样的方式,创建出来的目标区域的数据库只读副本,对 DEK 加密的 CMK 就会被替换为目标区域的 CMK。在这个过程中数据库的数据没有收到任何改变,加密的数据不变,使用的 DEK 也没有发生变化。


2. 场景二、客户端加密:使用 KMS 的 encryption SDK 的应用场景,涉及代码修改

有一些场景下,客户需要使用客户端方式对数据进行加密,这样的方式下,如何实现 DR 方案呢?


在 AWS 中,提供了 ENCRYPTION SDK,可以支持多个 CMK,每个 Provider 对应了一个或多个 CMK。如下图所示,假设我们需要在 BJS 北京区域和 ZHY 宁夏区域都能够使用同一个 DEK 对数据加解密。


我们可以通过 SDK 的 KmsMasterKeyProvider 方法,通过传入的 CMK 的 ARN,可以构造一个 KmsMasterKeyProvider。我们可以分别通过 BJS 区域的 CMK-bjs 以及 ZHY 区域的 CMK-zhy 各自构造出一个 KmsMasterKeyProvider。将这两个 KmsMasterKeyProvider 作为参数调用 MultipleProviderFactory.buildMultiProvider 方法,就可以构建出一个包含多个 CMK 的 Provider。


接下来,我们就可以使用这个 Provider 对同一个 DEK 进行加密,就可以得到两个加密的 DEK,分别是 Encryted Data Keybjs 和 Encrypted Date Keyzhy. 这两个被加密的 datakey,在分别使用北京区域和宁夏区域对应的 CMK 解密之后,得到的明文的 datakey 是一样的。这就是加密场景在跨区域 DR 的应用的基础了。在这个过程中,DEK 是不可见的,实际上,调用 encryptData(provider, plaintext, context),在方法内部会使用 DEK 对 plaintext 做加密。


备__注:context:所有 AWS KMS 加密操作都接受加密上下文,它是一__组__包含有关数据的__额__外上下文信息的 可__选键值对。以加密方式__绑__定到密文,需要 相同的加密上下文 来解密(或解密和重新加密)数据。



在北京区域解密过程。被解密的数据,通过调用 AwsCrypto().decryptData 的方法,只需要传入 provider 和密文就可以了。在 decyptData 方法内部,如下图所示,自动判断应该使用 BJS 的 CMK,对 DEK 解密后,使用 DEK 的明文,就可以用于对密文进行解密了。



在宁夏区域解密过程也是类似的。SDK 自动判断应该使用 ZHY 的 CMK,对 DEK 解密后,使用 DEK 的明文,用于对密文进行解密。



col 1


----- |


  | 
复制代码


参考代码如下。此外,AWS 提供了一个完整的 LAB,建议通过这个 LAB 来熟悉 SDK 不同的使用场景。


http://busy-engineers-guide.reinvent-workshop.com/index.html


以 java 为例,需要指定依赖的 encryption sdk 的版本:


Java


<dependency>    <groupId>com.amazonaws</groupId>    <artifactId>aws-encryption-sdk-java</artifactId>    <version>1.6.0</version></dependency>
复制代码


需要构造包含多个 CMK 的 provider,例如


Java


kms = AWSKMSClient.builder().build();this.masterKeyEast = new KmsMasterKeyProvider(keyIdEast)     .getMasterKey(keyIdEast);this.masterKeyWest = new KmsMasterKeyProvider(keyIdWest)     .getMasterKey(keyIdWest);this.provider = getKeyProvider(masterKeyEast, masterKeyWest)
复制代码


其中的 getKeyProvider 的方法如下: ****


Java


private static MasterKeyProvider<?> getKeyProvider(KmsMasterKey masterKeyEast, KmsMasterKey masterKeyWest) {         return MultipleProviderFactory.buildMultiProvider(masterKeyWest, masterKeyEast);    }}
复制代码


执行数据加密的时候,就可以调用这个 provider,这个 provider 会使用之前初始化的多个 CMK 对数据的 DEK 进行加密。


Java


public String encrypt(JsonNode data) throws IOException {     FormData formValues = MAPPER.treeToValue(data, FormData.class);
LOGGER.info("Got form submission for order " + formValues.orderid);
byte[] plaintext = MAPPER.writeValueAsBytes(formValues);
HashMap<String, String> context = new HashMap<>(); context.put(K_MESSAGE_TYPE, TYPE_ORDER_INQUIRY);
byte[] ciphertext = new AwsCrypto().encryptData(provider, plaintext, context).getResult();
return Base64.getEncoder().encodeToString(ciphertext);}
复制代码


执行数据解密的时候,调用这个 Provider 即可,注:解密的时候,可以用多个 CMK,也可以用当前区域的单个 CMK 初始化 Provider。后者代码执行效率更高。


Java


public JsonNode decrypt(String ciphertext) throws IOException {     byte[] ciphertextBytes = Base64.getDecoder().decode(ciphertext);
CryptoResult<byte[], ?> result = new AwsCrypto().decryptData(provider, ciphertextBytes);
// Check that we have the correct type if (!Objects.equals(result.getEncryptionContext().get(K_MESSAGE_TYPE), TYPE_ORDER_INQUIRY)) { throw new IllegalArgumentException("Bad message type in decrypted message"); }
return MAPPER.readTree(result.getResult());}
复制代码


本文转载自 AWS 技术博客。


原文链接:https://amazonaws-china.com/cn/blogs/china/when-the-dr-disaster-prepared-encounter-kms/


2019 年 12 月 20 日 15:26163

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

发布
暂无评论
发现更多内容

MySQL选错索引导致的线上慢查询事故

Zhendong

Java MySQL

2021年全球公有云终端用户支出将增长18% ;EMNLP 2020最佳论文:无声语音的数字发声

京东科技开发者

程序人生

第十周作业

Geek_4c1353

极客大学架构师训练营

五年Java开发经验,裸辞准备半月面试阿里,阿里巴巴却“不讲武德”,居然面了我7轮,历经千辛万苦终于斩获P7及Offer

Java架构之路

Java 程序员 架构 面试 编程语言

训练营第五周作业

爱码士

训练营

探秘RocketMQ源码【1】——Producer视角看事务消息

阿里云金融线TAM SRE专家服务团队

开源 RocketMQ 中间件 开源代码 消息中间件

奉劝各位Java工程师都要学习这份阿里内部绝密《百亿级并发系统设计》实战教程,大厂面试官可“不讲武德”!

Java架构之路

Java 程序员 架构 面试 编程语言

Java踩坑记系列之线程池

Java老k

Java 线程池

《华为数据之道》读书笔记:第1章 数据驱动的企业数字化转型

方志

数据中台 数据湖 数据治理

区块链赋能医疗行业,区块链医疗应用场景开发

13530558032

上周我面了个三年 Javaer,这几个问题都没答出来

yes

面试 RPC HTTP

架构设计:高并发读取,高并发写入,并发设计规划落地方案思考

互联网应用架构

高并发读,高并发写

架构师训练营第 1 期第 10 周作业

业哥

前端如何实现一键截图功能?

徐小夕

Java 前端 React 前端训练 前端进阶

一位Java程序员在上家公司CRUD了3年,金九银十想要跳槽面试却屡屡碰壁,感觉很迷茫!网友:这是你安逸太久技术能力跟不上了!

Java架构之路

Java 程序员 架构 面试 编程语言

乘上这艘“智能体”之舟,即刻前往智慧未来

脑极体

新图灵测试背后,智能交互点燃了哪些产业可能性?

脑极体

OAuth 2.0授权框架详解

程序那些事

OAuth 2.0 程序那些事 Oauth 授权框架 安全框架

训练营第5周学习总结

爱码士

训练营

DocView 现在支持自定义 Markdown 模版了!

程序员小航

markdown IDEA idea插件 文档生成

《码出高效:Java开发手册》,每一位想要成为优秀开发工程师的程序员必须要看的一本小册!

Java架构之路

Java 程序员 架构 面试 编程语言

贼好用,冰河开源了这款精准定时任务和延时队列框架!!

冰河

redis 中间件 消息队列 延时队列 Zset

怎么做好一场分享或者培训

fq

从资源管理角度认识K8S

LorraineLiu

Kubernetes 云原生 k8s k8s入门

Python进阶——如何正确使用魔法方法?(下)

Kaito

Python

《华为数据之道》读书笔记:序言

方志

数据中台 数字化转型 数据治理

JVM Metaspace内存溢出排查与总结

Java老k

Java OOM 内存溢出 metaspace

贞炸了!上线之后,消息收不到了!

楼下小黑哥

Java RocketMQ MQ

区块链商品溯源系统开发,数据上链应用落地方案

WX13823153201

高速二维码报警定位系统开发,智能报警系统

13530558032

甲方日常 55

句子

工作 随笔杂谈 日常

2021 ThoughtWorks 技术雷达峰会

2021 ThoughtWorks 技术雷达峰会

当 DR 灾备遇到 KMS(一)-InfoQ