fabric 在隐私保护问题上提供不同层面的解决方案
1.粗粒度的通道隔离,通道是一个比较重的资源,动态性比较差,不适宜频繁使用
2.私有数据(private data collections)是fabric 1.3之后提供的一种隐私保护方案,但是使用不是那么灵活,修改涉及到合约的部署,应用场景不多。
3.比较常见的隐私保护方案是数据加密以及授权,接下来详细讲的内容
用场景说话
场景一:A需要将敏感数据上链,需要加密。同时,需要授权B查看,同一通道内的C和D无法查看数据内容。
解决方案:第一步,A的数据上链时,对明文数据使用对称加密(例如AES256),生成密文,将密文上链。
第二步,将上一步骤加密使用的密钥和随机向量 使用B的公钥做非对称加密(例如ECC),B的公钥获取可以从应用层获取。将加密后的密钥和向量发给B。(可以通过应用层发给B,也可以写到区块链上给B,具体看实现)
第三步,B通过自己的私钥解密出加密数据的密钥和向量,使用这些密钥和向量对A的密文数据进行解密。
talk is cheap ,直接上代码。go 语言版智能合约
核心部分
func (t *Chaincode) submit(stub shim.ChaincodeStubInterface, args []string, encKey, IV, publicKey []byte) pb.Response { var err error key := args[0] value := args[1] ent, err := entities.NewAES256EncrypterEntity("ID", t.bccspInst, encKey, IV) if err != nil { return shim.Error(fmt.Sprintf("entities.NewAES256EncrypterEntity failed, err %s", err)) } //对称加密部分,使用了AES256加密算法 err = encryptAndPutState(stub, ent, key, []byte(value)) //使用aesKey和Iv加密 if err != nil { fmt.Println("encryptAndPutState err " + supplierKey) return shim.Error(err.Error()) } aesenckey := &AESENCKEY{string(encKey), string(IV)} aesenckeyJSONasBytes, err := json.Marshal(aesenckey) endata, err := ECCEncryptWithPublickKey(randReader, aesenckeyJSONasBytes, string(publicKey)) var authorizationKey string authorizationKey = "" //自定义授权键值 err = stub.PutState(authorizationKey, endata) if err != nil { return shim.Error("PutState err:" + authorizationKey) } return shim.Success(nil) } //aes加密 func encryptAndPutState(stub shim.ChaincodeStubInterface, ent entities.Encrypter, key string, value []byte) error { ciphertext, err := ent.Encrypt(value) if err != nil { return err } return stub.PutState(key, ciphertext) }
//aes加密解密 func getStateAndDecrypt(stub shim.ChaincodeStubInterface, ent entities.Encrypter, key string) ([]byte, error) { ciphertext, err := stub.GetState(key) if err != nil { return nil, err } if len(ciphertext) == 0 { return nil, errors.New("no ciphertext to decrypt") } return ent.Decrypt(ciphertext) }
//ECC 加密 ,需要引入"github.com/ethereum/go-ethereum/crypto/ecies"包 func ECCEncryptWithPublickKey(rand string, data []byte, publickKey string) ([]byte, error) { var err error block, _ := pem.Decode([]byte(publickKey)) if block == nil { fmt.Println("公钥解析错误:") return nil, err } // 解析公钥 pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { fmt.Println("ParsePKIXPublicKey公钥解析错误:") } pub := pubInterface.(*ecdsa.PublicKey) var pb = ecies.ImportECDSAPublic(pub) endata, err := ECCEncrypt(rand, []byte(data), *pb) if err != nil { panic(err) } fmt.Println("ecc公钥加密后:", hex.EncodeToString(endata)) return endata, err }
func ECCEncrypt(rand string, pt []byte, puk ecies.PublicKey) ([]byte, error) { r := strings.NewReader(rand) ct, err := ecies.Encrypt(r, &puk, pt, nil, nil) return ct, err }
//ECC 解密部分 func ECCDecryptWithPrivateKey(data []byte, privateKey string) ([]byte, error) { var err error block, _ := pem.Decode([]byte(privateKey)) if block == nil { fmt.Println("私钥解析错误:") return nil, err } // 解析私钥 priInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes) pri := priInterface.(*ecdsa.PrivateKey) if err != nil { } var pi = ecies.ImportECDSA(pri) dedata, err := ECCDecrypt(data, *pi) if err != nil { panic(err) } fmt.Println("私钥解密:", string(dedata)) return dedata, err }
完整代码有空的时候会放到github上