Java密码学 - 10.数字信封

  1. 定义
    1. 概念
      1. DEK
      2. KEK
    2. 流程
    3. 流程探讨
  2. 代码片段
    1. 信封的生成
    2. 信封内容的获取

定义

数字信封Digital Envelope,是中国大陆地区的讲法,外国则使用信封加密Envelope encryption一词。数字信封/信封加密是一种加密方法,它使用对称密钥对DEK)数据进行加密,然后使用非对称密钥DEK)对对称密钥对进行加密。

好处:[aws]

  • 一个非对称密钥可以加密若干个对称密钥对,意味着对称密钥对可以更新,增强安全性
  • 对称密钥对加密速度快,加密对象可以是大型文件

概念

DEK

**用于加密数据的**密钥data encryption key(DEK) ,DEK一般使用对称密钥,对称密钥加密速度快。

KEK

**用于加密密钥的**密钥key encryption key (KEK),用于加密DEK密钥,保证DEK密钥的安全性。KEK一般使用非对称密钥。

KEK的生成方式途径有:

  • 通过客户端生成该密钥对,如:云密钥管理服务Key Management Service(KMS)
  • 通过本地生成,如:OpenSSL

流程

加密流程如下:

  1. 可通过OpenSSL的方式等,本地生成一个DEK密钥
  2. 生成KEK密钥,并获得其中一个密钥(私钥Private Key),通过该密钥加密DEK密钥,获得加密的DEK密钥
  3. 通过DEK密钥加密数据

解密流程:

  1. 获得KEK密钥,该密钥为公钥Public Key
  2. 获得加密的DEK密钥,通过KEK密钥进行解密,获得DEK密钥
  3. 获得加密数据Encrypted Data
  4. 通过DEK密钥解密加密数据Encrypted Data,得到数据

流程探讨

流程存在若干个变量,导致有若干种方案:

  • KEK密钥是否在本地生成?如果在本地生成的话,本地存储私钥Private Key,并将公钥Public Key交予出去,本地生成可以确保私钥的唯一性,即第三方机构无法不可能获得私钥并擅自解密密文。
  • DEK密钥KEK密钥公钥存储方式。如果是client-server方式则两把钥匙将会存储在本地,否则则可能会存储到密钥管理服务Key Management Service(KMS)
  • 如何进行通讯?通讯方式可以是client-server方式直接交换密文,也可以将密文放到第三方存储空间处。

代码片段

该示例中使用到的证书,获取方式有以下:

  • pem格式的证书
  • keystore中获取证书

信封的生成

需导入包:

源码参考地址

1
2
3
4
5
6
7
8
9
10
11
//CMS是消息加密语法
//数据处理
CMSTypedData msg = new CMSProcessableByteArray(数据);
//生成信封
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert)//证书(含公钥)
		.setProvider("BC"));
		CMSEnvelopedData ed = edGen.generate(
				msg,
				new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC) //对称加密的算法
						.setProvider("BC").build());

源码中以下部分:

1
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC"));

是用于证书里的内容提取出来,再将内容转换成Bouncy Castle的数据结构 (原来处于的数据结构是java.security包下的数据结构)。

而该证书里包含的内容有:

  • 公钥 (进行加密)

  • Issuer 发行者 SerialNumber 发行者序列 (识别)

信封内容的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public byte[] openEnvelope(PrivateKey prikey) throws Exception {
	Security.addProvider(new BouncyCastleProvider());
	PEMParser pr = new PEMParser(new FileReader(envfile));
	ContentInfo o = (ContentInfo) pr.readObject();//读取PEM格式的数字信封

	//获取密文
	CMSEnvelopedData ed = new CMSEnvelopedData(o.getEncoded());
	RecipientInformationStore recipients = ed.getRecipientInfos();

	//解密
	ArrayList list = (ArrayList) recipients.getRecipients();//接收人们的信息
	RecipientInformation recipient = (RecipientInformation) list.get(0);//获得第一个接收人
	
	return recipient.getContent(new JceKeyTransEnvelopedRecipient(prikey).setProvider("BC"));
	
}

PS:代码必须设置BC作为提供者

1
Security.addProvider(new BouncyCastleProvider());