Fabric 1.0原始碼分析(1)BCCSP(區塊鏈加密服務提供者)

尹成發表於2018-05-20
# Fabric 1.0原始碼筆記 之 BCCSP(區塊鏈加密服務提供者)

## 1、BCCSP概述

BCCSP,全稱Blockchain Cryptographic Service Provider,即區塊鏈加密服務提供者,為Fabric提供加密標準和演算法的實現,包括雜湊、簽名、校驗、加解密等。
BCCSP通過MSP(即Membership Service Provider成員關係服務提供者)給核心功能和客戶端SDK提供加密演算法相關服務。
另外BCCSP支援可插拔,提供多種CSP,支援自定義CSP。目前支援sw和pkcs11兩種實現。

程式碼在bccsp目錄,bccsp主要目錄結構如下:

* bccsp.go,主要是介面宣告,定義了BCCSP和Key介面,以及眾多Opts介面,如KeyGenOpts、KeyDerivOpts、KeyImportOpts、HashOpts、SignerOpts、EncrypterOpts和DecrypterOpts。
* keystore.go,定義了KeyStore介面,即Key的管理和儲存介面。如果Key不是暫時的,則儲存在實現了該介面的物件中,否則不儲存。
* *opts.go,bccsp所使用到的各種技術選項的實現。
* [factory]目錄,即bccsp工廠包,通過bccsp工廠返回bccsp例項,比如sw或pkcs11,如果自定義bccsp實現,也需加新增到factory中。
* [sw]目錄,為the software-based implementation of the BCCSP,即基於軟體的BCCSP實現,通過呼叫go原生支援的密碼演算法實現,並提供keystore來儲存金鑰。
* [pkcs11]目錄,為bccsp的pkcs11實現,通過呼叫pkcs11介面實現相關加密操作,僅支援ecdsa、rsa以及aes演算法,密碼儲存在pkcs11通過pin口令保護的資料庫或者硬體裝置中。
* [utils]目錄,為工具函式包。
* [signer]目錄,實現go crypto標準庫的Signer介面。
補充:bccsp_test.go和mocks目錄,可忽略。

## 2、介面定義

### 2.1、BCCSP介面定義

BCCSP介面(區塊鏈加密服務提供者)定義如下:

```go
type BCCSP interface {
    KeyGen(opts KeyGenOpts) (k Key, err error) //生成Key
    KeyDeriv(k Key, opts KeyDerivOpts) (dk Key, err error) //派生Key
    KeyImport(raw interface{}, opts KeyImportOpts) (k Key, err error) //匯入Key
    GetKey(ski []byte) (k Key, err error) //獲取Key
    Hash(msg []byte, opts HashOpts) (hash []byte, err error) //雜湊msg
    GetHash(opts HashOpts) (h hash.Hash, err error) //獲取雜湊例項
    Sign(k Key, digest []byte, opts SignerOpts) (signature []byte, err error) //簽名
    Verify(k Key, signature, digest []byte, opts SignerOpts) (valid bool, err error) //校驗簽名
    Encrypt(k Key, plaintext []byte, opts EncrypterOpts) (ciphertext []byte, err error) //加密
    Decrypt(k Key, ciphertext []byte, opts DecrypterOpts) (plaintext []byte, err error) //解密
}
//程式碼在bccsp/bccsp.go
```

Key介面(金鑰)定義如下:

```go
type Key interface {
    Bytes() ([]byte, error) //Key轉換成位元組形式
    SKI() []byte //SKI,全稱Subject Key Identifier,主題金鑰識別符號
    Symmetric() bool //是否對稱金鑰,是為true,否則為false
    Private() bool //是否為私鑰,是為true,否則為false
    PublicKey() (Key, error) //返回非對稱金鑰中的公鑰,如果為對稱金鑰則返回錯誤
}
//程式碼在bccsp/bccsp.go
```

KeyStore介面(金鑰儲存)定義如下:

```go
type KeyStore interface {
    ReadOnly() bool //金鑰庫是否只讀,只讀時StoreKey將失敗
    GetKey(ski []byte) (k Key, err error) //如果SKI通過,返回Key
  StoreKey(k Key) (err error) //將Key儲存到金鑰庫中
}
//程式碼在bccsp/keystore.go
```

### 2.2、Opts介面定義

KeyGenOpts介面(金鑰生成選項)定義如下:

```go
//KeyGen(opts KeyGenOpts) (k Key, err error)
type KeyGenOpts interface {
    Algorithm() string //獲取金鑰生成演算法的識別符號
    Ephemeral() bool //要生成的金鑰是否為暫時的,如果為長期金鑰,需要通過SKI來完成儲存和索引
}
//程式碼在bccsp/bccsp.go
```

KeyDerivOpts介面(金鑰派生選項)定義如下:

```go
//KeyDeriv(k Key, opts KeyDerivOpts) (dk Key, err error)
type KeyDerivOpts interface {
    Algorithm() string //獲取金鑰派生演算法識別符號
    Ephemeral() bool //要派生的金鑰是否為暫時的
}
//程式碼在bccsp/bccsp.go
```

KeyImportOpts介面(匯入選項)定義如下:

```go
//KeyImport(raw interface{}, opts KeyImportOpts) (k Key, err error)
type KeyImportOpts interface {
    Algorithm() string //獲取金鑰匯入演算法識別符號
    Ephemeral() bool //要生成的金鑰是否為暫時的
}
//程式碼在bccsp/bccsp.go
```

HashOpts介面(雜湊選項)定義如下:

```go
//Hash(msg []byte, opts HashOpts) (hash []byte, err error)
type HashOpts interface {
    Algorithm() string //獲取雜湊演算法識別符號
}
//程式碼在bccsp/bccsp.go
```

SignerOpts介面(簽名選項)定義如下:

```go
//Sign(k Key, digest []byte, opts SignerOpts) (signature []byte, err error)
//即go標準庫crypto.SignerOpts介面
type SignerOpts interface {
    crypto.SignerOpts
}
//程式碼在bccsp/bccsp.go
```

另外EncrypterOpts介面(加密選項)和DecrypterOpts介面(解密選項)均為空介面。

```go
type EncrypterOpts interface{}
type DecrypterOpts interface{}
//程式碼在bccsp/bccsp.go
```

## 3、SW實現方式

### 3.1、sw目錄結構

SW實現方式是預設實現方式,程式碼在bccsp/sw。主要目錄結構如下:

* impl.go,bccsp的SW實現。
* internals.go,簽名者、校驗者、加密者、解密者等介面定義,包括:KeyGenerator、KeyDeriver、KeyImporter、Hasher、Signer、Verifier、Encryptor和Decryptor。
* conf.go,bccsp的sw實現的配置定義。
------
* aes.go,AES型別的加密者(aescbcpkcs7Encryptor)和解密者(aescbcpkcs7Decryptor)介面實現。AES為一種對稱加密演算法。
* ecdsa.go,ECDSA型別的簽名者(ecdsaSigner)和校驗者(ecdsaPrivateKeyVerifier和ecdsaPublicKeyKeyVerifier)介面實現。ECDSA即橢圓曲線演算法。
* rsa.go,RSA型別的簽名者(rsaSigner)和校驗者(rsaPrivateKeyVerifier和rsaPublicKeyKeyVerifier)介面實現。RSA為另一種非對稱加密演算法。
------
* aeskey.go,AES型別的Key介面實現。
* ecdsakey.go,ECDSA型別的Key介面實現,包括ecdsaPrivateKey和ecdsaPublicKey。
* rsakey.go,RSA型別的Key介面實現,包括rsaPrivateKey和rsaPublicKey。
------
* dummyks.go,dummy型別的KeyStore介面實現,即dummyKeyStore,用於暫時性的Key,儲存在記憶體中,系統關閉即消失。
* fileks.go,file型別的KeyStore介面實現,即fileBasedKeyStore,用於長期的Key,儲存在檔案中。
------
* keygen.go,KeyGenerator介面實現,包括aesKeyGenerator、ecdsaKeyGenerator和rsaKeyGenerator。
* keyderiv.go,KeyDeriver介面實現,包括aesPrivateKeyKeyDeriver、ecdsaPrivateKeyKeyDeriver和ecdsaPublicKeyKeyDeriver。
* keyimport.go,KeyImporter介面實現,包括aes256ImportKeyOptsKeyImporter、ecdsaPKIXPublicKeyImportOptsKeyImporter、ecdsaPrivateKeyImportOptsKeyImporter、
  ecdsaGoPublicKeyImportOptsKeyImporter、rsaGoPublicKeyImportOptsKeyImporter、hmacImportKeyOptsKeyImporter和x509PublicKeyImportOptsKeyImporter。
* hash.go,Hasher介面實現,即hasher。

### 3.2、SW bccsp配置

即程式碼bccsp/sw/conf.go,config資料結構定義:
elliptic.Curve為橢圓曲線介面,使用了crypto/elliptic包。有關橢圓曲線,參考http://8btc.com/thread-1240-1-1.html。
SHA,全稱Secure Hash Algorithm,即安全雜湊演算法,參考https://www.cnblogs.com/kabi/p/5871421.html。

```go
type config struct {
    ellipticCurve elliptic.Curve //指定橢圓曲線,elliptic.P256()和elliptic.P384()分別為P-256曲線和P-384曲線
    hashFunction func() hash.Hash //指定雜湊函式,如SHA-2(SHA-256、SHA-384、SHA-512等)和SHA-3
    aesBitLength int //指定AES金鑰長度
    rsaBitLength int //指定RSA金鑰長度
}
//程式碼在bccsp/sw/conf.go
```

func (conf *config) setSecurityLevel(securityLevel int, hashFamily string) (err error)為設定安全級別和雜湊系列(包括SHA2和SHA3)。
如果hashFamily為"SHA2"或"SHA3",將分別調取conf.setSecurityLevelSHA2(securityLevel)或conf.setSecurityLevelSHA3(securityLevel)。

func (conf *config) setSecurityLevelSHA2(level int) (err error)程式碼如下:

```go
switch level {
case 256:
    conf.ellipticCurve = elliptic.P256() //P-256曲線
    conf.hashFunction = sha256.New //SHA-256
    conf.rsaBitLength = 2048 //指定AES金鑰長度2048
    conf.aesBitLength = 32 //指定RSA金鑰長度32
case 384:
    conf.ellipticCurve = elliptic.P384() //P-384曲線
    conf.hashFunction = sha512.New384 //SHA-384
    conf.rsaBitLength = 3072 //指定AES金鑰長度3072
    conf.aesBitLength = 32 //指定RSA金鑰長度32
//...
}
//程式碼在bccsp/sw/conf.go
```

func (conf *config) setSecurityLevelSHA3(level int) (err error)程式碼如下:

```go
switch level {
case 256:
    conf.ellipticCurve = elliptic.P256() //P-256曲線
    conf.hashFunction = sha3.New256 //SHA3-256
    conf.rsaBitLength = 2048 //指定AES金鑰長度2048
    conf.aesBitLength = 32 //指定RSA金鑰長度32
case 384:
    conf.ellipticCurve = elliptic.P384() //P-384曲線
    conf.hashFunction = sha3.New384 //SHA3-384
    conf.rsaBitLength = 3072 //指定AES金鑰長度3072
    conf.aesBitLength = 32 //指定RSA金鑰長度32
//...
}
//程式碼在bccsp/sw/conf.go
```

### 3.3、SW bccsp例項結構體定義

```go
type impl struct {
    conf *config //bccsp例項的配置
    ks bccsp.KeyStore //KeyStore物件,用於儲存和獲取Key

    keyGenerators map[reflect.Type]KeyGenerator //KeyGenerator對映
    keyDerivers map[reflect.Type]KeyDeriver //KeyDeriver對映
    keyImporters map[reflect.Type]KeyImporter //KeyImporter對映
    encryptors map[reflect.Type]Encryptor //加密者對映
    decryptors map[reflect.Type]Decryptor //解密者對映
    signers map[reflect.Type]Signer //簽名者對映
    verifiers map[reflect.Type]Verifier //校驗者對映
    hashers map[reflect.Type]Hasher //Hasher對映
}
//程式碼在bccsp/sw/impl.go
```

涉及如下方法:

```go
func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) //生成sw例項
func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) //生成Key
func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) //派生Key
func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) //匯入Key
func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) //獲取Key
func (csp *impl) Hash(msg []byte, opts bccsp.HashOpts) (digest []byte, err error) //雜湊msg
func (csp *impl) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) //獲取雜湊例項
func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) //簽名
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) //校驗簽名
func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error) //加密
func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) //解密
//程式碼在bccsp/sw/impl.go
```

func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.BCCSP, error)作用為:
設定securityLevel和hashFamily,設定keyStore、encryptors、decryptors、signers、verifiers和hashers,之後設定keyGenerators、keyDerivers和keyImporters。

func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error)作用為:
按opts查詢keyGenerator是否在csp.keyGenerators[]中,如果在則調取keyGenerator.KeyGen(opts)生成Key。如果opts.Ephemeral()不是暫時的,調取csp.ks.StoreKey儲存Key。

func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error)作用為:
按k的型別查詢keyDeriver是否在csp.keyDerivers[]中,如果在則調取keyDeriver.KeyDeriv(k, opts)派生Key。如果opts.Ephemeral()不是暫時的,調取csp.ks.StoreKey儲存Key。

```go
func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error)
func (csp *impl) Hash(msg []byte, opts bccsp.HashOpts) (digest []byte, err error)
func (csp *impl) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error)
func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error)
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error)
func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error)
func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error)
//與上述方法實現方式相似。
```

func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error)作用為:按ski調取csp.ks.GetKey(ski)獲取Key。

### 3.4、AES演算法相關程式碼實現

參考:https://studygolang.com/articles/7302。
AES,Advanced Encryption Standard,即高階加密標準,是一種對稱加密演算法。
AES屬於塊密碼工作模式。塊密碼工作模式,允許使用同一個密碼塊對於多於一塊的資料進行加密。
塊密碼只能加密長度等於密碼塊長度的單塊資料,若要加密變長資料,則資料必須先劃分為一些單獨的資料塊。
通常而言最後一塊資料,也需要使用合適的填充方式將資料擴充套件到符合密碼塊大小的長度。

Fabric中使用的填充方式為:pkcs7Padding,即填充字串由一個位元組序列組成,每個位元組填充該位元組序列的長度。 程式碼如下:
另外pkcs7UnPadding為其反操作。

```go
func pkcs7Padding(src []byte) []byte {
    padding := aes.BlockSize - len(src)%aes.BlockSize //計算填充長度
    padtext := bytes.Repeat([]byte{byte(padding)}, padding) //bytes.Repeat構建長度為padding的位元組序列,內容為padding
    return append(src, padtext...)
}
//程式碼在bccsp/sw/aes.go
```

AES常見模式有ECB、CBC等。其中ECB,對於相同的資料塊都會加密為相同的密文塊,這種模式不能提供嚴格的資料保密性。
而CBC模式,每個資料塊都會和前一個密文塊異或後再加密,這種模式中每個密文塊都會依賴前一個資料塊。同時為了保證每條訊息的唯一性,在第一塊中需要使用初始化向量。
Fabric使用了CBC模式,程式碼如下:

```go
//AES加密
func aesCBCEncrypt(key, s []byte) ([]byte, error) {
    block, err := aes.NewCipher(key) //生成加密塊

    //隨機一個塊大小作為初始化向量
    ciphertext := make([]byte, aes.BlockSize+len(s))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }

    mode := cipher.NewCBCEncrypter(block, iv) //建立CBC模式加密器
    mode.CryptBlocks(ciphertext[aes.BlockSize:], s) //執行加密操作

    return ciphertext, nil
}
//程式碼在bccsp/sw/aes.go
```

```go
//AES解密
func aesCBCDecrypt(key, src []byte) ([]byte, error) {
    block, err := aes.NewCipher(key) //生成加密塊

    iv := src[:aes.BlockSize] //初始化向量
    src = src[aes.BlockSize:] //實際資料

    mode := cipher.NewCBCDecrypter(block, iv) //建立CBC模式解密器
    mode.CryptBlocks(src, src) //執行解密操作

    return src, nil
}
//程式碼在bccsp/sw/aes.go
```

pkcs7Padding和aesCBCEncrypt整合後程式碼如下:

```go
//AES加密
func AESCBCPKCS7Encrypt(key, src []byte) ([]byte, error) {
    tmp := pkcs7Padding(src)
    return aesCBCEncrypt(key, tmp)
}
//AES解密
func AESCBCPKCS7Decrypt(key, src []byte) ([]byte, error) {
    pt, err := aesCBCDecrypt(key, src)
    return pkcs7UnPadding(pt)
}
//程式碼在bccsp/sw/aes.go
```

### 3.5、RSA演算法相關程式碼實現

簽名相關程式碼如下:

```go
type rsaSigner struct{}
func (s *rsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
    //...
    return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts) //簽名
}
//程式碼在bccsp/sw/rsa.go
```

校驗簽名相關程式碼如下:

```go
type rsaPrivateKeyVerifier struct{}
func (v *rsaPrivateKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
    /...
    rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.PublicKey), (opts.(*rsa.PSSOptions)).Hash, digest, signature, opts.(*rsa.PSSOptions)) //驗籤
    /...    
}
```

```go
type rsaPublicKeyKeyVerifier struct{}
func (v *rsaPublicKeyKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
    /...
    err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey, (opts.(*rsa.PSSOptions)).Hash, digest, signature, opts.(*rsa.PSSOptions)) //驗籤
    /...
}
//程式碼在bccsp/sw/rsa.go
```

另附rsaPrivateKey和rsaPublicKey定義如下:

```go
type rsaPrivateKey struct {
    privKey *rsa.PrivateKey
}
type rsaPublicKey struct {
    pubKey *rsa.PublicKey
}
//程式碼在bccsp/sw/rsakey.go
```

### 3.6、橢圓曲線演算法相關程式碼實現

程式碼在bccsp/sw/ecdsa.go
橢圓曲線演算法,相關內容參考:[Fabric 1.0原始碼筆記 之 附錄-ECDSA(橢圓曲線數字簽名演算法)](../../annex/ecdsa.md)

### 3.7、檔案型別KeyStore介面實現

虛擬型別KeyStore介面實現dummyKeyStore,無任何實際操作,忽略。
檔案型別KeyStore介面實現fileBasedKeyStore,資料結構定義如下:

```go
type fileBasedKeyStore struct {
    path string //路徑
    readOnly bool //是否只讀
    isOpen bool //是否開啟
    pwd []byte //密碼
    m sync.Mutex //鎖
}
//程式碼在bccsp/sw/fileks.go
```
fileBasedKeyStore是一個基於資料夾的金鑰庫,每個Key都儲存在分散的檔案中,檔名包含金鑰的SKI。
金鑰庫可以用密碼初始化,這個密碼可以用於加密和解密儲存金鑰的檔案。為了避免覆蓋,金鑰庫可以設定為只讀。


涉及方法如下:

```go
func NewFileBasedKeyStore(pwd []byte, path string, readOnly bool) (bccsp.KeyStore, error) //建立fileBasedKeyStore,並呼叫Init完成初始化
func (ks *fileBasedKeyStore) Init(pwd []byte, path string, readOnly bool) error //初始化路徑、密碼、是否只讀,以及建立並開啟KeyStore
func (ks *fileBasedKeyStore) ReadOnly() bool //金鑰庫是否只讀,只讀時StoreKey將失敗
func (ks *fileBasedKeyStore) GetKey(ski []byte) (k bccsp.Key, err error) //如果SKI通過,返回Key。通過ski可以獲取檔案字尾,key、sk、pk分別為普通key、私鑰、公鑰
func (ks *fileBasedKeyStore) StoreKey(k bccsp.Key) (err error) //將Key儲存到金鑰庫中
//程式碼在bccsp/sw/fileks.go
```

func (ks *fileBasedKeyStore) StoreKey(k bccsp.Key) (err error)程式碼如下:

```go
switch k.(type) {
case *ecdsaPrivateKey:
    kk := k.(*ecdsaPrivateKey)
    err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.privKey) //ECDSA私鑰
case *ecdsaPublicKey:
    kk := k.(*ecdsaPublicKey)
    err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.pubKey) //ECDSA公鑰
case *rsaPrivateKey:
    kk := k.(*rsaPrivateKey)
    err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.privKey) //RSA私鑰
case *rsaPublicKey:
    kk := k.(*rsaPublicKey)
    err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.pubKey) //RSA公鑰
case *aesPrivateKey:
    kk := k.(*aesPrivateKey)
    err = ks.storeKey(hex.EncodeToString(k.SKI()), kk.privKey) //AES私鑰
//...
//程式碼在bccsp/sw/fileks.go
```

## 4、pkcs11實現方式

pkcs11包,即HSM基礎的bccsp(the hsm-based BCCSP implementation),HSM是Hardware Security Modules,即硬體安全模組。
pckcs11是硬體基礎的加密服務實現,sw是軟體基礎的加密服務實現。這個硬體基礎的實現以 https://github.com/miekg/pkcs11 這個庫為基礎。

PKCS#11稱為Cyptoki,定義了一套獨立於技術的程式設計介面,USBKey安全應用需要實現的介面。
在密碼系統中,PKCS#11是公鑰加密標準(PKCS, Public-Key Cryptography Standards)中的一份子,由RSA實驗室(RSA Laboratories)釋出,它為加密令牌定義了一組平臺無關的API ,如硬體安全模組和智慧卡。
pkcs11包主要內容是PKCS11標準的實現及橢圓曲線演算法中以low-S演算法為主導的go實現。同時也通過利用RSA的一些特性和演算法,豐富了PKCS11加密體系。

### 4.1、pkcs11目錄結構

* impl.go,bccsp的pkcs11實現。
* conf.go,bccsp的pkcs11實現的配置定義,實現程式碼與sw的配置定義接近,即實現設定安全級別和雜湊系列。
* pkcs11.go,以miekg/pkcs11包為基礎,包裝了各種pkcs11功能。
* ecdsa.go,ECDSA演算法的簽名和驗籤的實現。
* ecdsakey.go,ECDSA型別的Key介面實現,包括ecdsaPrivateKey和ecdsaPublicKey。

### 4.2、pkcs11例項結構體定義和實現

```go
type impl struct {
    bccsp.BCCSP //結構體中內嵌介面,參考https://studygolang.com/articles/6934

    conf *config //pkcs11例項的配置
    ks bccsp.KeyStore //KeyStore物件,用於儲存和獲取Key

    ctx *pkcs11.Ctx //pkcs11上下文
    sessions chan pkcs11.SessionHandle //即type SessionHandle uint,會話識別符號通道,預設數量10
    slot uint   //安全硬體外設連線插槽標識號

    lib string //pkcs11庫檔案所在路徑
    noPrivImport bool   //是否禁止匯入私鑰
    softVerify bool   //是否使用軟體方式校驗簽名
}
//程式碼在bccsp/pkcs11/impl.go
```

涉及方法如下:

```go
func New(opts PKCS11Opts, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) //生成pkcs11例項
func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) //生成Key
func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) //派生Key
func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) //匯入Key
func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) //獲取Key
func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) //簽名
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) //校驗簽名
func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error) //加密
func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) //解密
func FindPKCS11Lib() (lib, pin, label string) //從環境變數PKCS11_LIB、PKCS11_PIN、PKCS11_LABEL中獲取lib、pin、label,否則取預設使用libsofthsm2.so、98765432、ForFabric
//程式碼在bccsp/pkcs11/impl.go
```

func New(opts PKCS11Opts, keyStore bccsp.KeyStore) (bccsp.BCCSP, error)核心程式碼如下:

```go
conf := &config{}
err := conf.setSecurityLevel(opts.SecLevel, opts.HashFamily) //初始化conf
swCSP, err := sw.New(opts.SecLevel, opts.HashFamily, keyStore) //建立sw例項

lib := opts.Library
pin := opts.Pin
label := opts.Label
ctx, slot, session, err := loadLib(lib, pin, label) //載入動態庫,尋找slot,開啟會話並登陸會話

sessions := make(chan pkcs11.SessionHandle, sessionCacheSize)
csp := &impl{swCSP, conf, keyStore, ctx, sessions, slot, lib, opts.Sensitive, opts.SoftVerify}
csp.returnSession(*session)
return csp, nil
//程式碼在bccsp/pkcs11/impl.go
```

loadLib(lib, pin, label)程式碼如下:
* pkcs11.New(lib)根據lib路徑載入動態庫(如openCryptoki的動態庫),並建立pkcs11例項ctx。ctx相當於fabric與安全硬體模組通訊的橋樑:bccsp<–>ctx<–>驅動lib<–>安全硬體模組,只要驅動lib是按照pkcs11標準開發。
* ctx.Initialize()進行初始化PKCS#11庫。
* 從ctx.GetSlotList(true)返回的列表中獲取由label指定的插槽標識slot。注:這裡的槽可以簡單的理解為電腦主機上供安全硬體模組插入的槽,如USB插口,可能不止一個,每一個在系統核心中都有名字和標識號。
* 嘗試10次呼叫ctx.OpenSession開啟一個會話session。會話就是通過通訊路徑與安全硬體模組建立連線,可以簡單的理解為pkcs11的chan。
* 登陸會話ctx.Login。
* 返回ctx,slot,會話物件session,用於賦值給impl例項成員ctx,slot,把session傳送到sessions裡。
pkcs11庫的使用參考:Cryptoki庫概述https://docs.oracle.com/cd/E19253-01/819-7056/6n91eac56/index.html

```go
var slot uint = 0
ctx := pkcs11.New(lib) //根據lib路徑載入動態庫(如openCryptoki的動態庫),並建立pkcs11例項ctx
ctx.Initialize() //初始化 PKCS #11 庫
slots, err := ctx.GetSlotList(true) //可用插槽的列表

found := false
for _, s := range slots {
    info, err := ctx.GetTokenInfo(s) //獲取有關特定令牌的資訊
    if label == info.Label {
        found = true
        slot = s
        break
    }
}

var session pkcs11.SessionHandle
for i := 0; i < 10; i++ { //嘗試10次呼叫ctx.OpenSession開啟一個會話session
    session, err = ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
    if err != nil {
        //...
    } else {
        break
    }
}
err = ctx.Login(session, pkcs11.CKU_USER, pin) //登陸會話ctx.Login
return ctx, slot, &session, nil //返回ctx,slot,會話物件session
//程式碼在bccsp/pkcs11/pkcs11.go
```

補充type PKCS11Opts struct定義如下:

```go
type PKCS11Opts struct {
    //...
    //Keystore選項
    Ephemeral bool //是否暫存的
    FileKeystore *FileKeystoreOpts //FileKeystore
    DummyKeystore *DummyKeystoreOpts //DummyKeystore

    // PKCS11 options
    Library string //庫檔案路徑
    Label string //插槽標識
    Pin string //登入密碼
    Sensitive bool                     
    SoftVerify bool                     
}
//程式碼在bccsp/pkcs11/conf.go
```

如下方法優先判斷Opts或Key型別,如果為pkcs11支援的ecdsa型別,將調取pkcs11包的實現,否則調取sw包作為預設實現。

```go
func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) //生成Key
func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) //派生Key
func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) //匯入Key
func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) //獲取Key
func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) //簽名
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) //校驗簽名
//程式碼在bccsp/pkcs11/impl.go
```

如下加解密方法,將直接調取sw包的預設實現。

```go
func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error) //加密
func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) //解密
//程式碼在bccsp/pkcs11/impl.go
```

### 4.3、pkcs11對第三方包github.com/miekg/pkcs11的封裝

```go
func (csp *impl) getSession() (session pkcs11.SessionHandle) //在cache為空或者完全為使用狀態的時候,通過OpenSession來獲取session
func (csp *impl) returnSession(session pkcs11.SessionHandle) //關閉Session
func (csp *impl) getECKey(ski []byte) (pubKey *ecdsa.PublicKey, isPriv bool, err error) //通過SKI, 查詢EC(橢圓曲線) key
func (csp *impl) generateECKey(curve asn1.ObjectIdentifier, ephemeral bool) (ski []byte, pubKey *ecdsa.PublicKey, err error) //生成EC key
func (csp *impl) signP11ECDSA(ski []byte, msg []byte) (R, S *big.Int, err error) //簽名
func (csp *impl) verifyP11ECDSA(ski []byte, msg []byte, R, S *big.Int, byteSize int) (valid bool, err error) //校驗簽名
func (csp *impl) importECKey(curve asn1.ObjectIdentifier, privKey, ecPt []byte, ephemeral bool, keyType bool) (ski []byte, err error) //匯入EC key
//loadLib 載入lib檔案,初始化資料,通過GetSlotList來解析slot資料,通過GetTokenInfo獲取token資訊,通過pkcs11.SessionHandle方法來獲取session
func loadLib(lib, pin, label string) (*pkcs11.Ctx, uint, *pkcs11.SessionHandle, error)
//findKeyPairFromSKI 通過Ctx、session及ski來獲取對應的公私鑰
func findKeyPairFromSKI(mod *pkcs11.Ctx, session pkcs11.SessionHandle, ski []byte, keyType bool) (*pkcs11.ObjectHandle, error)
//程式碼在bccsp/pkcs11/pkcs11.go
```

## 5、BCCSP工廠

通過factory可以獲得兩類BCCSP例項:sw和pkcs11。
BCCSP例項是通過工廠來提供的,sw包對應的工廠在swFactory.go中實現,pkcs11包對應的工廠在pkcs11Factory.go中實現,它們都共同實現了BCCSPFactory介面。

### 5.1、factory目錄結構

* factory.go,定義BCCSPFactory介面,宣告全域性變數bccspMap來儲存例項化的bccsp,宣告bootBCCSP來儲存預設的例項,以及定義factory初始化函式和Get函式。
* nopkcs11.go/pkcs11.go,定義了兩個版本的工廠選項FactoryOpts、初始化和Get函式。區別在於編譯時是否指定nopkcs11或!nopkcs11,預設是nopkcs11。
* opts.go,定義了預設的FactoryOpts,即SW。
* pkcs11factory.go,pkcs11型別的bccsp工廠實現PKCS11Factory。
* swfactory.go, sw型別的bccsp工廠實現SWFactory。

### 5.2、BCCSPFactory介面定義

```go
type BCCSPFactory interface {
    Name() string //獲取工廠名稱
    Get(opts *FactoryOpts) (bccsp.BCCSP, error) //使用FactoryOpts獲取BCCSP例項
}
//程式碼在bccsp/factory/factory.go
```

nopkcs11的FactoryOpts:

```go
type FactoryOpts struct {
    ProviderName string
    SwOpts *SwOpts
}
//程式碼在bccsp/factory/nopkcs11.go
```

pkcs11的FactoryOpts:

```go
type FactoryOpts struct {
    ProviderName string
    SwOpts *SwOpts
    Pkcs11Opts *pkcs11.PKCS11Opts
}
//程式碼在bccsp/factory/pkcs11.go
```

### 5.3、swfactory實現

type SWFactory struct{}

涉及方法:

```go
func (f *SWFactory) Name() string //此處返回SoftwareBasedFactoryName,即"SW"
func (f *SWFactory) Get(config *FactoryOpts) (bccsp.BCCSP, error) //使用FactoryOpts獲取BCCSP例項
//程式碼在bccsp/factory/swfactory.go
```

func (f *SWFactory) Get(config *FactoryOpts) (bccsp.BCCSP, error)程式碼如下:

```go
swOpts := config.SwOpts
var ks bccsp.KeyStore
if swOpts.Ephemeral == true { //金鑰是暫時的
    ks = sw.NewDummyKeyStore()
} else if swOpts.FileKeystore != nil { //金鑰是永久的並且定義了FileKeystore
    fks, err := sw.NewFileBasedKeyStore(nil, swOpts.FileKeystore.KeyStorePath, false)
    ks = fks
} else {
    ks = sw.NewDummyKeyStore() //預設是暫時的
}
return sw.New(swOpts.SecLevel, swOpts.HashFamily, ks) //建立sw例項
//程式碼在bccsp/factory/swfactory.go
```

### 5.4、pkcs11factory實現

type PKCS11Factory struct{}

涉及方法:

```go
func (f *PKCS11Factory) Name() string //此處返回PKCS11BasedFactoryName,即"PKCS11"
func (f *PKCS11Factory) Get(config *FactoryOpts) (bccsp.BCCSP, error) //使用FactoryOpts獲取BCCSP例項
//程式碼在bccsp/factory/pkcs11factory.go
```

func (f *PKCS11Factory) Get(config *FactoryOpts) (bccsp.BCCSP, error)程式碼如下:
程式碼結構基本與swfactory相同,但此處有一個TODO提示。即:
PKCS11是不需要金鑰庫(keystore)的,但目前還沒有從PKCS11 BCCSP中拆分出去,所以這裡留著待後續進行改進,因此程式碼實現中依然保留了一部分keystore的實現。

```go
p11Opts := config.Pkcs11Opts
//TODO: PKCS11 does not need a keystore, but we have not migrated all of PKCS11 BCCSP to PKCS11 yet
var ks bccsp.KeyStore
if p11Opts.Ephemeral == true {
    ks = sw.NewDummyKeyStore()
} else if p11Opts.FileKeystore != nil {
    fks, err := sw.NewFileBasedKeyStore(nil, p11Opts.FileKeystore.KeyStorePath, false)
    ks = fks
} else {
    ks = sw.NewDummyKeyStore()
}
return pkcs11.New(*p11Opts, ks)
//程式碼在bccsp/factory/pkcs11factory.go
```

### 5.5、Factories初始化

nopkcs11版本Factories初始化:

```go
factoriesInitOnce.Do(func() { //僅執行一次
    bccspMap = make(map[string]bccsp.BCCSP) //初始化全域性bccsp.BCCSP map:bccspMap

    // Software-Based BCCSP
    if config.SwOpts != nil {
        f := &SWFactory{} //建立SWFactory
        err := initBCCSP(f, config) //建立BCCSP例項,即呼叫f.Get,並加入bccspMap中
    }

    var ok bool
    defaultBCCSP, ok = bccspMap[config.ProviderName] //將其作為預設defaultBCCSP
})
//程式碼在bccsp/factory/nopkcs11.go
```

pkcs11版本Factories初始化:

```go
func InitFactories(config *FactoryOpts) error {
    factoriesInitOnce.Do(func() {
        setFactories(config)
    })
}

func setFactories(config *FactoryOpts) error {
    bccspMap = make(map[string]bccsp.BCCSP)

    // Software-Based BCCSP,如果是SW
    if config.SwOpts != nil {
        f := &SWFactory{}
        err := initBCCSP(f, config)
    }

    // PKCS11-Based BCCSP,如果是PKCS11
    if config.Pkcs11Opts != nil {
        f := &PKCS11Factory{}
        err := initBCCSP(f, config)
    }

    var ok bool
    defaultBCCSP, ok = bccspMap[config.ProviderName]
}
//程式碼在bccsp/factory/pkcs11.go
```

func initBCCSP(f BCCSPFactory, config *FactoryOpts) error程式碼如下:

```go
csp, err := f.Get(config) //調取f.Get生成BCCSP例項
bccspMap[f.Name()] = csp //新生成的例項,加入bccspMap中
//程式碼在bccsp/factory/factory.go
```





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN

相關文章