BCCSP接口
fabric的BCCSP全称为blockchain cryptographic service provider,区块链密码学服务提供方,是fabric用于提供密钥生成、加解密以及签名验证的服务接口,接口的定义如下。
github.com/hyperledger/fabric/bccsp/bccsp.go
除了上述接口以外,为了保存密钥,还定义了一个密钥存储(keystore)的接口,如下
github.com/hyperledger/fabric/bccsp/keystore.go
官方实现使用方法
创建与初始化
官方实现使用工厂进行创建工作,工厂类在bccsp/factory
包下。factory包提供了一些创建函数。
其中,FactoryOpts
是定义工厂的参数,默认实现放在pkcs11.go
和nopkcs11.go
下面,如
github.com/hyperledger/fabric/bccsp/factory/pkcs11.go
该实现有三种工厂,对应三个Opts,SWOpts
、PluginOpts
、PKCS11Opts
。ProviderName
指定GetDefault()
方法返回的默认工厂的类型。具体这些Opts就不再细说。
使用的方法就是
- 创建
FactoryOpts
对象,设置参数
- 使用
InitFactories()
函数初始化工厂,或者直接使用GetBCCSPFromOpts()
获取BCCSP,就直接结束创建
- 初始化完毕后,就可以使用名称,通过
GetBCCSP()
函数获取,亦或者使用GetDefault()
函数获取默认的工厂。
使用BCCSP接口
BCCSP的函数已经介绍过了。官方SW实现提供了RSA、AES、ECDSA三种算法,那么有许多的Opts提供了,分别在bccsp/aesopts.go
、bccsp/ecdsaopts.go
、bccsp/rsaopts.go
文件里面。根据所使用的算法,创建对应的Opts,再传入BCCSP的各个函数即可。
那些方法会根据传入的key类型,判断所使用的算法。需要确保opts和key两个是属于同一个密钥算法的。
源码简析
包结构
SW实现简析
SW的基本结构是这个impl
可以看到,包含了一个配置结构,以及一个存储库,另外还有许多map
,这些map
的键是Key
的类型,而值则是对应的函数实现类,如encryptors
里面,保存了ECDSA、RSA、AES的加密接口实现。
密码学函数接口
可以看到impl
文件里面有许多的er
接口,定义全部在internals.go
文件里面,如下
github.com/hyperledger/fabric/bccsp/sw/internals.go
这些接口的实现分散在了ecdsa.go
、rsa.go
、aes.go
、hash.go
等的文件里面,具体实现不再细说。
初始化
使用impl.go
文件的New()
等函数初始化本实现,函数签名如下
我们不需要直接调用该函数,该函数由工厂方法调用。我们关注的是里面创建了许多的encryptor
等,初始化了impl
的各个map
。
只是,新版的impl
,为了实现能够定制,大改了New()
函数的逻辑,由原本在New函数里面创建各个函数接口的实现,到在new.go
文件内创建,改到了NewWithParameters()
函数中。Fabric-CA用的是老的实现。
主要函数逻辑
查看impl
的BCCSP
接口的函数实现可以发现,逻辑都是差不多的,首先判断传入的Key的类型,然后获取相应的函数接口实现,然后再调用该函数,同时会传入Opts
对象,比如下面的签名函数
github.com/hyperledger/fabric/bccsp/sw/impl.go
工厂
factory.go
是工厂的通用接口的定义,包括GetDefaults()
等函数,也是通用的。具体的工厂,是由pkcs11.go
以及nopkcs11.go
提供的。这两个文件定义了初始化所有工厂的函数,以及直接创建BCCSP实例的函数,而初始化工厂函数InitFactories()
会给factory.go
文件内的defaultBCCSP
、bccspMap
等赋值,从而我们能够使用factory.go
的函数。
下面会以pkcs11.go
的工厂实现为例讲解。
工厂接口BCCSPFactory
工厂有一个通用的接口,BCCSPFactory
,用于创建某个类型的BCCSP的。如下
github.com/hyperledger/fabric/bccsp/factory/factory.go
默认三个工厂的实现则放置在了swfactory.go
、pluginfactory.go
以及pkcs11factory.go
文件中。
这些具体的实现,使用的是FactoryOpts
中属于自己的设置的那个结构,如SWFactory
使用的是SWOpts
,而不是整个FactoryOpts
。
工厂初始化函数InitFactories()
该函数调用setFactories
函数,首先检查FactoryOpts
,然后根据FactoryOpts
创建工厂
其中会使用factory.go
的initBCCSP()
函数,该函数使用BCCSPFactory
接口的Get()
方法创建BCCSP
,并存入bccspMap
中,键为工厂的名称,值为BCCSP
实现。
github.com/hyperledger/fabric/bccsp/factory/factory.go
然后,设置factory.go
中定义的defaultBCCSP
,以实现GetDefault()
函数的功能。
github.com/hyperledger/fabric/bccsp/factory/pkcs11.go
初始化完成后,就能够通过GetDefault()
获取默认实现,通过GetBCCSP()
根据工厂名获取实现,等等了。
SW工厂实现
swfactory.go
文件中存放了SW的工厂实现,定义了SWOpts
结构。New()
函数则构造Keystore
、解析SWOpts
等,在此不再细讲。
文件KeyStore
这里不细讲,讲下大概思路,下面一段翻译自该实现的注释。
fileBasedKeyStore
是一个基于文件夹的密钥存储库。每一个密钥存在一个文件当中,文件的命名使用密钥的SKI以及对应密钥类型的字符串。所有的密钥保存在初始化时指定的文件夹中。存储库支持使用密码创建,密码将会用于密钥的加密和解密。密钥存储库可设置为只读的,以防止错误的覆盖了密钥。
获取密钥
GetKey()
函数使用SKI来获取密钥,在searchKeystoreForSKI()
函数中,通过遍历文件名的方式,找到该SKI的文件,然后就能根据Key的类型获取了。
保存密钥
根据密钥的SKI以及类型,使用getPathForAlias(alias string)
函数来获取文件名,alias就是ski转成的16进制字符串,然后使用storePrivateKey()
、storePublicKey()
以及storeKey()
函数存储。
兼容默认实现的BCCSP实现的一点思路
工厂
首先需要写一个我们自己实现BCCSPFactory
的实现,定义Opts
结构,以及实现两个方法,其中Get()
方法调用BCCSP
实现的构造函数。
然后我们需要创建一个自己的FactoryOpts
,可以使用内嵌结构,像下面,包含本来的Opts
改了FactoryOpts
,也需要重新定义BCCSPFactory
,只是因为Get()
函数要用到FactoryOpts
。
然后按照工厂库四大导出函数,写我们的函数。初始化时,初始化本来的工厂,然后初始化我们的。
GetDefault()
则返回本来的Default就行。
其他函数也挺好改的,照葫芦画瓢就可。根据传入的参数,判断是用我们的实现,还是用本来的实现。
BCCSP接口实现
为了兼容本来的实现,判断Key
类型是我们的Key的时候,则调用我们的实现函数,否则调用本来的。
KeyStore实现
我们需要自己实现Keystore
,因为默认实现是根据Key类型来存储Key的,不是一个较为通用的实现,无法用于我们自己的Key。也是照着本来的Keystore
实现一个类似的即可。