目录
一、加密方式
AES256分组对称加密是指将明文数据分解为多个16字节的明文块,利用密钥分别对每个明文块进行加密,得到相同个数的16字节密文块,如下图所示:
如果分解后有明文块不足16字节,就需要涉及填充和链加密模式。
二、填充方式
由于对明文数据进行了分块,那么就有可能存在分解后的明文块不足128位的情况,这就需要对明文块进行填充。
AES加密支持多种填充方式:NoPadding,PKCS5Padding,ZerosPadding,PKCS7Padding。
其中NoPadding表示不进行填充,这就需要自行检测和控制数据的长度;ZerosPadding表示用0填充缺少的位数;
PKCS5Padding和PKCS7Padding的填充方式在实际效果中是相同的,用缺少的位数进行填充,如块长度为11个字节,缺少5个字节,那么就填充5个字节的内容,每个字节的内容为十进制的5,如下图所示:
→ |
注意:如果块长度刚好为16字节,则需要在块后补16个字节的数据,每个字节数据为十进制的16,如下所示:
ENCChaincode使用了PKCS7Padding填充方式。
三、加密模式
ECB模式:
CBC模式(密码分组连接模式):
CFB模式(密码反馈模式):
OFB模式(输出反馈模式):
ENCChaincode使用了CBC(密码分解连接)模式。
四、源码解读
ENCKEY和DECKEY:用于分组对称加密和解密
命令 openssl rand -base64 32
ENCKEY=DECKEY=’hJM2KYj33vBq/+3GGybwyFB3chOkNo4lv1swAEMxC3E=’
IV:加密过程中使用的向量,ENCCC例子使用了CBC模式,IV需要提供给链码,若不提供则会随机生成,但如果背书策略指定需要由多个节点进行背书,那么就必须提供唯一的IV,否则节点在背书时会出现读写冲突。
命令 opensslrand -base64 16
IV=’ST56TUR9CYY+NZ41sorYVg==’
SIGKEY和VERKEY:用于签名和验证
命令 openssl ecparam -nameprime256v1 -genkey | tail -n5 | base64 -w0
SIGKEY=VERKEY='LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUtKRzhzMnlqNzJEcGo3L0o1OHFHQzdHa1R5cXQ1REkwZWJod01GamkxMVZvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFMWlEOUlyU1Ixdi9LOVN6TzVCSjVaUUpIZVdTblIxbE00b21iRTBwZ0NpUml6ZExtZkkyVgp6VE84ZE5PTHNhYlhCZGZyNHJUWDNhSkxzeHE5azBZUm1nPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo='
链码源文件:fabric/example/chaincode/go/enccc_example/enccc_example.go
(一)Encrypter
作用:把通过AES256位密钥进行加密的value写入账本。
源码分析:
1. 创建加密实体,参数为:ID,bccp实例,加密密钥encKey,IV(可选参数,若不提供则随机创建)
ent, err:= entities.NewAES256EncrypterEntity("ID", t.bccspInst, encKey, IV)
2. 加密和写入数据
err= encryptAndPutState(stub, ent, key, cleartextValue)
2.1 使用加密器实体对value进行加密
ciphertext, err := ent.Encrypt(cleartextValue)
2.2 将加密后的value写入账本
stub.PutState(key, ciphertext)
(二)Decrypter
作用:将账本value读出并通过AES256位密钥进行解密。
源码分析:
1. 创建加密实体,参数为:ID,bccp实例,加密密钥encKey,IV(可选参数,此处示例readme文档中描述:若不提供则随机创建,但与IV的数据结构描述不符)
ent, err:= entities.NewAES256EncrypterEntity("ID", t.bccspInst, encKey, IV)
2. 读取和解密
err= getStateAndDecrypt (stub, ent, key, cleartextValue)
2.1 读出key对应的value
ciphertext, err := stub.GetState(key)
2.2 对value进行解密
ent.Decrypt(ciphertext)
(三)EncrypterSigner
作用:对value使用AES256位密钥进行加密和prime256v1标准进行签名,并将value写入账本。
源码分析:
1. 创建加密签名实体,参数为:ID,bccp实例,加密密钥encKey,签名密钥sigKey
ent, err:= entities.NewAES256EncrypterECDSASignerEntity("ID", t.bccspInst,encKey, sigKey)
2. 对签名数据加密,写入账本
err= signEncryptAndPutState(stub, ent, key, cleartextValue)
2.1 创建一个签名消息,value作为Payload,加密签名实体的ID作为ID,签名消息数据结构如下图所示:
msg := &entities.SignedMessage{Payload: value,ID: []byte(ent.ID())}
2.2 利用加密签名实体对SignedMessage进行签名,签名后的信息作为签名消息的Sig字段
err := msg.Sign(ent)
2.3 序列化SignedMessage
b, err := msg.ToBytes()
2.4 对序列化的签名信息进行加密并写入账本
encryptAndPutState(stub, ent, key, b)
(四)DecrypterVerify
作用:从账本中读取数据,使用AES256位密钥进行解密和prime256v1标准验证签名。
源码分析:
1. 创建加密签名实体,参数为:ID,bccp实例,加密密钥encKey,签名密钥sigKey
ent, err:= entities.NewAES256EncrypterECDSASignerEntity("ID", t.bccspInst,encKey, sigKey)
2. 读取数据,解密并验证签名
cleartextValue, err :=getStateDecryptAndVerify(stub, ent, key)
2.1 读取账本数据并解密
val, err := getStateAndDecrypt(stub, ent, key)
2.2 创建SignedMessage对象msg
msg := &entities.SignedMessage{}
将序列化的val解码至msg中
err = msg.FromBytes(val)
2.3 验证签名
ok, err := msg.Verify(ent)
五、链码测试
使用e2e_cli示例网络结构:一个orderer节点,分属两个Org的四个peer节点。
修改了script.sh脚本,在所有peer节点安装、实例化ENCCC(ENC Chaincode)
测试过程中的ENCKEY、DECKEY、DECKEY1、IV、SIGKEY、VERKEY、VERKEY1已经设置为cli容器的环境变量
Test1:验证加密解密
使用ENCKEY将value写入账本:
peerchaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n enccc -c'{"Args":["ENCRYPT","a","100"]}'--transient"{\"ENCKEY\":\"$ENCKEY\",\"IV\":\"$IV\"}
结果:数据可正确加密写入
使用DECKEY读取value:
peer chaincode query -C $CHANNEL_NAME -n enccc -c '{"Args":["DECRYPT","a"]}' --transient "{\"DECKEY\":\"$DECKEY\"}"
结果:数据正确读出并解密
使用DECKEY1读取value:
peer chaincode query -C $CHANNEL_NAME -n enccc -c '{"Args":["DECRYPT","a"]}' --transient "{\"DECKEY\":\"$DECKEY1\"}"
结果:由于DECKEY1 not equal ENCKEY,导致解密失败
Test2:验证签名验证
使用ENCKEY和SIGKEY将value写入账本:
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n enccc -c '{"Args":["ENCRYPTSIGN","b","200"]}' --transient "{\"ENCKEY\":\"$ENCKEY\",\"SIGKEY\":\"$SIGKEY\"}"
结果:数据正确签名加密,写入账本
使用ENCKEY和VERKEY读取value:
peer chaincode query -C $CHANNEL_NAME -n enccc -c '{"Args":["DECRYPTVERIFY","b"]}' --transient "{\"DECKEY\":\"$DECKEY\",\"VERKEY\":\"$VERKEY\"}"
结果:数据正确读出并解密,验证签名通过
使用ENCKEY和VERKEY1读取value:
peer chaincode query -C $CHANNEL_NAME -n enccc -c '{"Args":["DECRYPTVERIFY","b"]}' --transient "{\"DECKEY\":\"$DECKEY\",\"VERKEY\":\"$VERKEY1\"}"
结果:数据正确读出并解密,签名验证不通过
Test3:验证多节点背书情况下IV的使用
测试组织结构如下:
Org1:peer0,peer1
Org2:peer0,peer1
背书策略指定需要Org1和Org2各一个peer进行背书,在不提供IV的情况下,可正常运行。
示例程序enccc_example的readme文档中描述IV是AES256分组对称加密的初始化向量,在不提供时会随机创建,导致最终的加密结果不同,多节点背书验证时会无法通过验证,但源码中对IV的描述为:只有不为空时才使用IV。
六、总结
enccc_example作为一个示例链码,为我们提供了一种对账本数据进行加密的解决方案,若要通过加密对数据进行隔离,则需要针对业务进行一定的修改,目前还在根据业务探索如何使用加密进行数据隔离,大家有好的想法阔以私信在下。
签名和验证部分采用了prime256v1标准,但通过测试发现签名和验证需要使用相同的key,具体的签名和验证处理需要进一步研读源码,目前在业务中还未找到此种方式的具体应用场景。
参考资料:对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
注:文中图片来源于网络,如有侵犯请联系在下删除