# 隐私保护

数据的隐私保护是联盟链的关键技术之一，传统的隐私保护方案一般都是使用非对称加密算法对数据加密上链，获取数据时再解密。这些方案仅仅解决数据单方面的隐私保存，而无法做到隐私数据的动态分享。chain33联盟链使用代理重加密方案，结合存证合约，实现隐私数据的加密存储和动态分享。

## 代理重加密

### 算法简介

`代理重加密`(Proxy Re-Encryption, PRE)是一种密钥转换算法，可以将数据所有人`owner`公钥加密的密文转换为另一种密文，转换的密文可以由被授权人`recipient`的私钥进行解密。密文转换过程由一个半可信的代理服务器`proxy`执行，在执行该过程前，代理节点需要持有一个由授权人到被授权人的转换密钥，一般由授权人提前生成并发送给代理节点。通过转换密钥无法直接解析密文，最终还需要被授权人的私钥才能解密，所以代理节点没办法获取到明文信息。

### 功能组件

chain33代理重加密功能组件包括：

- [chain33-pre](https://github.com/33cn/chain33-pre)代理重加密节点
- [chain33-sdk](https://github.com/33cn/chain33-sdk-java/blob/master/联盟链接口说明.md#代理重加密接口)代理重加密接口
- [chain33](https://github.com/33cn/plugin)存证合约

### 使用步骤

![img](../../assets/u2.png)

- 数据所有人`owner`将隐私数据加密，通过存证合约保存在区块链上
- 数据所有人`owner`对被授权人`recipient`生成重加密转换秘钥
- 数据所有人`owner`将转换秘钥分发到代理重加密节点`proxy`
- 被授权人`recipient`在区块链上查询隐私数据
- 被授权人`recipient`向重加密节点`proxy`申请重加密
- 被授权人`recipient`解密数据

## 使用方法

### 环境搭建

#### chain33联盟链

搭建方法参考[联盟链开发环境搭建](./../installDeploy/consortiumChain.md)

#### 代理重加密节点

##### 软件包下载

```
wget https://bty33.oss-cn-shanghai.aliyuncs.com/chain33-Pre.tar.gztar xzvf chain33-Pre.tar.gzcd chain33-Pre
```

##### 配置

根据实际情况配置服务端口和节点私钥

```
# 对外重加密服务端口bindAddr=":11801"# 节点操作私钥preServerKey="0x123456789abcdef0fedcba987654321"
```

##### 启动

```
./chain33-pre -f chain33.pre.toml
```

### sdk调用

数据所有人`owner`

```
// 生成账户
AccountInfo alice = new AccountInfo();
alice.setPrivateKey(OwnerPrivateKey);
alice.setPublicKey(TransactionUtil.getHexPubKeyFromPrivKey(OwnerPrivateKey));
// 生成对称秘钥
EncryptKey encryptKey = PreUtils.GenerateEncryptKey(HexUtil.fromHexString(alice.getPublicKey()));
// 生成重加密密钥分片
KeyFrag[] kFrags = new KeyFrag[numSplit];
try {
    kFrags = PreUtils.GenerateKeyFragments(HexUtil.fromHexString(alice.getPrivateKey()),
            HexUtil.fromHexString(RecipientPubKey), numSplit, threshold);
} catch (Exception e) {
    e.printStackTrace();
}
// 密钥分片发送到代理节点
String dhProof = PreUtils.ECDH(ServerPub, alice.getPrivateKey());
for(int i = 0; i < preClient.length; i++) {
    boolean result = preClient[i].sendKeyFragment(alice.getPublicKey(), RecipientPubKey, encryptKey.getPubProofR(),
            encryptKey.getPubProofU(), 100, dhProof, kFrags[i]);
    if (!result) {
        System.out.println("sendKeyFragment failed");
        return;
    }
}
// 数据加密
byte[] iv = AesUtil.generateIv();
byte[] cipher = AesUtil.encrypt(content, encryptKey.getShareKey(), iv);
System.out.println(cipher);
// 通过存证合约保存加密数据
byte[] contentHash;
try {
    contentHash = TransactionUtil.Sha256(content.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    return;
}
String txEncode = StorageUtil.createEncryptNotaryStorage(cipher, contentHash, iv,
        "chain33-storage-key-pre-"+kFrags[0].getPrecurPub(), "", "storage", OwnerPrivateKey);
String submitTransaction = chain33Client.submitTransaction(txEncode);
```

数据被授权人`recipient`

```
// 生成账户
AccountInfo bob = new AccountInfo();
bob.setPrivateKey(RecipientPrivateKey);
bob.setPublicKey(TransactionUtil.getHexPubKeyFromPrivKey(RecipientPrivateKey));
// 申请重加密，需要两边的公钥
ReKeyFrag[] reKeyFrags = new ReKeyFrag[threshold];
for(int i = 0; i < threshold; i++) {
    reKeyFrags[i] = preClient[i].reencrypt(OwnerPubKey, bob.getPublicKey());
}
// 解密对称密钥，需要被授权人私钥
byte[] shareKeyBob;
try {
    shareKeyBob = PreUtils.AssembleReencryptFragment(HexUtil.fromHexString(bob.getPrivateKey()), reKeyFrags);
} catch (Exception e) {
    e.printStackTrace();
    return;
}
// 从链上获取密文
JSONObject resultJson = chain33Client.queryStorage("chain33-storage-key-pre-"+reKeyFrags[0].getPrecurPub());
JSONObject resultArray = resultJson.getJSONObject("encryptStorage");
String content = resultArray.getString("encryptContent");
byte[] fromHexString = HexUtil.fromHexString(content);
// 解密
String text = AesUtil.decrypt(fromHexString, HexUtil.toHexString(shareKeyBob));
```

详细示例，请参考[PreOwner](https://github.com/33cn/chain33-sdk-java/blob/master/src/test/java/cn/chain33/javasdk/model/PreOwner.java)和[PreRecipient](https://github.com/33cn/chain33-sdk-java/blob/master/src/test/java/cn/chain33/javasdk/model/PreRecipient.java)