Fabric-CA的使用教程

作者: melanc 分类: 区块链 发布时间: 2018-11-12 14:41

原文:https://www.lijiaocn.com/%E9%A1%B9%E7%9B%AE/2018/05/04/fabric-ca-example.html

  1. 説明

    本文將演示如何使用fabric-ca為每個組件和用户生成證書。

    這裏將創建一個由兩個組織 org1.example.com 和 org2.example.com 組成的的聯盟。

    另外還有一個組織 example.com 用來部署orderer。

    example.com部署了一個 solo 模式的orderer。(多個orderer的部署方式,以後探討)

    org1.example.com部署了兩個peer:

    org2.example.com部署了一個peer:

    每個組織都要有一個Admin用户,每個組件(peer/orderer)也需要一個賬號,因此需要通過fabric-ca創建7個用户:

    這裏只創建了Admin用户,普通用户的創建方式相同,只是普通用户的證書不需要添加到目標組件的admincerts目錄中。

    啟動fabric-ca-server

    fabirc-ca的編譯安裝方法見: hyperledger的fabricCA的安裝使用 。

    這裏將fabric-ca部署在 /opt/app/fabric-ca/server 目錄中:

    mkdir /opt/app/fabric-ca/server
    cp -rf $GOPATH/src/github.com/hyperledger/fabric-ca/bin/*  /opt/app/fabric-ca/server
    ln -s /opt/app/fabric-ca/server/fabric-ca-client  /usr/bin/fabric-ca-client

    直接啟動ca,fabric-ca admin的名稱為admin,密碼為123123。(這裏只是演示,生產中使用,你需要根據實際的情況配置)

    啟動ca server(–cfg参数表示可以刪除聯盟和用户):

    cd /opt/app/fabric-ca/server
    fabric-ca-server init -b admin:123123
    fabric-ca-server start -b admin:123123 --cfg.affiliations.allowremove  --cfg.identities.allowremove &

    fabric-ca admin的憑證

    創建的ca-files目錄,進行後續操作。

    mkdir ca-files
    cd ca-files

    生成fabric-ca admin的憑證:

    export FABRIC_CA_CLIENT_HOME=`pwd`
    mkdir -p `pwd`/admin
    
    fabric-ca-client enroll -u http://admin:123123@localhost:7054 -H `pwd`/admin

    環境變量 FABRIC_CA_CLIENT_HOME 指定了client的工作目錄,生成的用户憑證將存放在這個目錄中。

    創建聯盟

    執行下面命令創建聯盟:

    fabric-ca-client affiliation add com 
    fabric-ca-client affiliation add com.example
    fabric-ca-client affiliation add com.example.org1
    fabric-ca-client affiliation add com.example.org2

    創建聯盟如下:

    $ fabric-ca-client affiliation list
    2018/04/28 15:19:34 [INFO] 127.0.0.1:38160 GET /affiliations 201 0 "OK"
    affiliation: com
       affiliation: com.example
          affiliation: com.example.org1
          affiliation: com.example.org2

    你可能會看到fabric-ca啟動時自動創建的org1和org2聯盟,可以用下面的命令將其刪除:

    fabric-ca-client affiliation remove --force  org1
    fabric-ca-client affiliation remove --force  org2

     註冊example.com的管理員Admin@example.com

    可以將命令行參數寫在fabric-ca admin的配置文檔 admin/fabric-ca-client-config.yaml 中。

    $ ls admin/
    fabric-ca-client-config.yaml  msp

    執行下面的命令,即可完成用户 Admin@example.com 註冊:

    fabric-ca-client register --id.name Admin@example.com --id.type client --id.affiliation "com.example" \
    --id.attrs '"hf.Registrar.Roles=client,orderer,peer,user","hf.Registrar.DelegateRoles=client,orderer,peer,user",hf.Registrar.Attributes=*,hf.GenCRL=true,hf.Revoker=true,hf.AffiliationMgr=true,hf.IntermediateCA=true,role=admin:ecert' \
    --id.secret=123123 --csr.cn=example.com --csr.hosts=['example.com']

    如果不用 --id.secret 指定密碼,會自動生成密碼。

    其它配置的含義是用户名為 Admin@example.com ,類型是 client ,它能夠管理 com.example.* 下的用户,如下:

    --id.name  Admin@example.com                           //用户名
    --id.type client                                       //類型為client
    --id.affiliation "com.example"                         //權利訪問
    hf.Registrar.Roles=client,orderer,peer,user            //能夠管理的用户類型
    hf.Registrar.DelegateRoles=client,orderer,peer,user    //可以授權給子用户管理的用户類型
    hf.Registrar.Attributes=*                              //可以為子用户設置所有屬性
    hf.GenCRL=true                                         //可以生成撤銷證書列表
    hf.Revoker=true                                        //可以撤銷用户
    hf.AffiliationMgr=true                                 //能夠管理聯盟
    hf.IntermediateCA=true                                 //可以作為中間CA
    role=admin:ecert                                       //自定義屬性

    生成Admin@example.com的msp和tls憑證:

    mkdir -p example.com/users/Admin@example.com
    
    fabric-ca-client enroll -u http://Admin@example.com:123123@localhost:7054 -M `pwd`/example.com/users/Admin@example.com/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://Admin@example.com:123123@localhost:7054 -M `pwd`/example.com/users/Admin@example.com/tls
    
    cp ./example.com/users/Admin@example.com/tls/tlscacerts/tls-localhost-7054.pem ./example.com/users/Admin@example.com/tls/ca.crt
    cp ./example.com/users/Admin@example.com/tls/signcerts/cert.pem ./example.com/users/Admin@example.com/tls/server.crt
    cp ./example.com/users/Admin@example.com/tls/keystore/*_sk ./example.com/users/Admin@example.com/tls/server.key
    ls ./example.com/users/Admin@example.com

    這時候可以用Admin@example.com的身份查看聯盟:

    fabric-ca-client affiliation list -u http://admin:123123@localhost:7054

    最後需要將Admin@example.com的證書複製到example.com/msp/admincerts/

    mkdir -p example.com/msp/admincerts/
    cp example.com/users/Admin@example.com/msp/signcerts/cert.pem  example.com/msp/admincerts/

    只有這樣,才能具備管理員權限。

    註冊org1.example.com的管理員Admin@org1.example.com

    org1.example.com的管理員Admin@org1.example.com準備一個目錄:

    mkdir -p ./org1.example.com/users/Admin@org1.example.com

    註冊:

    fabric-ca-client register --id.name Admin@org1.example.com --id.type client --id.affiliation "com.example.org1" \
    --id.attrs '"hf.Registrar.Roles=client,orderer,peer,user","hf.Registrar.DelegateRoles=client,orderer,peer,user",hf.Registrar.Attributes=*,hf.GenCRL=true,hf.Revoker=true,hf.AffiliationMgr=true,hf.IntermediateCA=true,role=admin:ecert' \
    --id.secret=123123 --csr.cn=org1.example.com --csr.hosts=['org1.example.com']

    生成憑證:

    fabric-ca-client enroll -u http://Admin@org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/users/Admin@org1.example.com/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://Admin@org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/users/Admin@org1.example.com/tls
    
    cp ./org1.example.com/users/Admin@org1.example.com/tls/tlscacerts/tls-localhost-7054.pem ./org1.example.com/users/Admin@org1.example.com/tls/ca.crt
    cp ./org1.example.com/users/Admin@org1.example.com/tls/signcerts/cert.pem ./org1.example.com/users/Admin@org1.example.com/tls/server.crt
    cp ./org1.example.com/users/Admin@org1.example.com/tls/keystore/*_sk ./org1.example.com/users/Admin@org1.example.com/tls/server.key
    ls ./org1.example.com/users/Admin@org1.example.com

    查看聯盟:

    fabric-ca-client affiliation list -u http://admin:123123@localhost:7054

    注意與 Admin@example.com 的區別,這裏智能看到組織com.example.org1

    將Admin@org1.example.com的證書複製到org1.example.com的msp/admincerts中:

    mkdir -p org1.example.com/msp/admincerts/
    cp org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem  org1.example.com/msp/admincerts/

    在Admin@org1.example.com中也需要創建msp/admincerts目錄,通過peer命令操作fabric的時候會要求admincerts存在:

    mkdir -p org1.example.com/users/Admin@org1.example.com/msp/admincerts/
    cp org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem  org1.example.com/users/Admin@org1.example.com/msp/admincerts/

    另外,這裏沒有中間CA,將intermediatecerts中的空文檔刪除,否則peer會提示Warning:

    rm org1.example.com/admin/msp/intermediatecerts/*

    註冊org2.example.com的管理員Admin@org2.example.com

    org2.example.com的管理員Admin@org2.example.com準備一個目錄:

    mkdir -p ./org2.example.com/users/Admin@org2.example.com

    註冊:

    fabric-ca-client register --id.name Admin@org2.example.com --id.type client --id.affiliation "com.example.org2" \
    --id.attrs '"hf.Registrar.Roles=client,orderer,peer,user","hf.Registrar.DelegateRoles=client,orderer,peer,user",hf.Registrar.Attributes=*,hf.GenCRL=true,hf.Revoker=true,hf.AffiliationMgr=true,hf.IntermediateCA=true,role=admin:ecert' \
    --id.secret=123123 --csr.cn=org2.example.com --csr.hosts=['org2.example.com']

    生成憑證:

    fabric-ca-client enroll -u http://Admin@org2.example.com:123123@localhost:7054 -M `pwd`/org2.example.com/users/Admin@org2.example.com/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://Admin@org2.example.com:123123@localhost:7054 -M `pwd`/org2.example.com/users/Admin@org2.example.com/tls
    
    cp ./org2.example.com/users/Admin@org2.example.com/tls/tlscacerts/tls-localhost-7054.pem ./org2.example.com/users/Admin@org2.example.com/tls/ca.crt
    cp ./org2.example.com/users/Admin@org2.example.com/tls/signcerts/cert.pem ./org2.example.com/users/Admin@org2.example.com/tls/server.crt
    cp ./org2.example.com/users/Admin@org2.example.com/tls/keystore/*_sk ./org2.example.com/users/Admin@org2.example.com/tls/server.key
    ls ./org2.example.com/users/Admin@org2.example.com

    查看聯盟:

    fabric-ca-client affiliation list -u http://admin:123123@localhost:7054

    Admin@org2.example.com只能看到組織 com.example.org2 。

    將Admin@org2.example.com的證書複製到org2.example.com的msp/admincerts中:

    mkdir -p org2.example.com/msp/admincerts/
    cp org2.example.com/users/Admin@org2.example.com/msp/signcerts/cert.pem  org2.example.com/msp/admincerts/

    在Admin@org2.example.com中也需要創建msp/admincerts目錄,通過peer命令操作fabric的時候會要求admincerts存在:

    mkdir -p org2.example.com/users/Admin@org2.example.com/msp/admincerts/
    cp org2.example.com/users/Admin@org2.example.com/msp/signcerts/cert.pem  org2.example.com/users/Admin@org2.example.com/msp/admincerts/

    另外,這裏沒有中間CA,將intermediatecerts中的空文檔刪除,否則peer會提示Warning:

    rm org2.example.com/admin/msp/intermediatecerts/*

    各個組織分別使用自己的Admin賬户創建其它賬號

    example.comorg1.example.comorg2.example.com三個組織這時候可以分別使用自己的Admin賬號創建子賬號。

    orderer.example.com

    使用 Admin@example.com 為唯一的orderer註冊賬號。

    注意這時候我們要指定的目錄是 example.com/users/Admin@example.com/ 。

    修改example.com/users/Admin@example.com/fabric-ca-client-config.yaml:

    id:
      name: orderer.example.com
      type: orderer
      affiliation: com.example
      maxenrollments: 0
      attributes:
        - name: role
          value: orderer:ecert

    註冊以及生成憑證:

    fabric-ca-client register --id.name orderer.example.com --id.type orderer --id.affiliation "com.example" \
    --id.attrs '"role=orderer",ecert=true' --id.secret=123123 --csr.cn=orderer.example.com --csr.hosts=['orderer.example.com'] \
    -H `pwd`/example.com/users/Admin@example.com -u http://admin:123123@localhost:7054
    
    mkdir ./example.com/orderer
    fabric-ca-client enroll -u http://orderer.example.com:123123@localhost:7054 -M `pwd`/example.com/orderer/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://orderer.example.com:123123@localhost:7054 -M `pwd`/example.com/orderer/tls

    將 Admin@example.com 的證書複製到example.com/orderer/msp/admincerts:

    mkdir example.com/orderer/msp/admincerts
    cp example.com/users/Admin@example.com/msp/signcerts/cert.pem example.com/orderer/msp/admincerts/

    peer0.org1.example.com

    注意這時候我們要指定的目錄是 org1.example.com/users/Admin@org1.example.com/ 。

    修改org1.example.com/users/Admin@org1.example.com/fabric-ca-client-config.yaml:

    id:
      name: peer0.org1.example.com
      type: peer
      affiliation: com.example.org1
      maxenrollments: 0
      attributes:
        - name: role
          value: peer:ecert

    註冊以及生成憑證:

    fabric-ca-client register --id.name peer0.org1.example.com --id.type peer --id.affiliation "com.example.org1" \
    --id.attrs '"role=peer",ecert=true' --id.secret=123123 --csr.cn=peer0.org1.example.com --csr.hosts=['peer0.org1.example.com'] \
    -H `pwd`/org1.example.com/users/Admin@org1.example.com -u http://admin:123123@localhost:7054
    
    mkdir ./org1.example.com/peer0
    fabric-ca-client enroll -u http://peer0.org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/peer0/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://peer0.org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/peer0/tls

    將 Admin@org1.example.com 的證書複製到org1.example.com/peer0/msp/admincerts:

    mkdir org1.example.com/peer0/msp/admincerts
    cp org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem org1.example.com/peer0/msp/admincerts/

    peer1.org1.example.com

    注意這時候我們要指定的目錄是 org1.example.com/users/Admin@org1.example.com/ 。

    修改org1.example.com/users/Admin@org1.example.com/fabric-ca-client-config.yaml:

    id:
      name: peer1.org1.example.com
      type: peer
      affiliation: com.example.org1
      maxenrollments: 0
      attributes:
        - name: role
          value: peer:ecert

    註冊以及生成憑證:

    fabric-ca-client register --id.name peer1.org1.example.com --id.type peer --id.affiliation "com.example.org1" \
    --id.attrs '"role=peer",ecert=true' --id.secret=123123 --csr.cn=peer1.org1.example.com --csr.hosts=['peer1.org1.example.com'] \
    -H `pwd`/org1.example.com/users/Admin@org1.example.com -u http://admin:123123@localhost:7054
    
    mkdir ./org1.example.com/peer1
    fabric-ca-client enroll -u http://peer1.org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/peer1/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://peer1.org1.example.com:123123@localhost:7054 -M `pwd`/org1.example.com/peer1/tls

    將 Admin@org1.example.com 的證書複製到org1.example.com/peer1/msp/admincerts:

    mkdir org1.example.com/peer1/msp/admincerts
    cp org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem org1.example.com/peer1/msp/admincerts/

    peer0.org2.example.com

    注意這時候我們要指定的目錄是 org2.example.com/users/Admin@org2.example.com/ 。

    修改org2.example.com/users/Admin@org2.example.com/fabric-ca-client-config.yaml:

    id:
      name: peer0.org2.example.com
      type: peer
      affiliation: com.example.org2
      maxenrollments: 0
      attributes:
        - name: role
          value: peer:ecert

    註冊以及生成憑證:

    fabric-ca-client register --id.name peer0.org2.example.com --id.type peer --id.affiliation "com.example.org2" \
    --id.attrs '"role=peer",ecert=true' --id.secret=123123 --csr.cn=peer0.org2.example.com --csr.hosts=['peer0.org2.example.com'] \
    -H `pwd`/org2.example.com/users/Admin@org2.example.com -u http://admin:123123@localhost:7054
    
    mkdir ./org2.example.com/peer0
    fabric-ca-client enroll -u http://peer0.org2.example.com:123123@localhost:7054 -M `pwd`/org2.example.com/peer0/msp
    fabric-ca-client enroll -d --enrollment.profile tls -u http://peer0.org2.example.com:123123@localhost:7054 -M `pwd`/org2.example.com/peer0/tls

    將 Admin@org2.example.com 的證書複製到org2.example.com/peer0/msp/admincerts:

    mkdir org2.example.com/peer0/msp/admincerts
    cp org2.example.com/users/Admin@org2.example.com/msp/signcerts/cert.pem org2.example.com/peer0/msp/admincerts/

    重新部署

    然後在 hyperledger的fabric項目的全手動部署 執行結束後得到的fabric-deploy目錄 基礎上,進行下面的操作。

    修改 configtx.yaml ,將其中的msp路徑修改為通過fabric-ca創建的msp目錄:

    Profiles:
        TwoOrgsOrdererGenesis:
            Orderer:
                <<: *OrdererDefaults
                Organizations:
                    - *OrdererOrg
            Consortiums:
                SampleConsortium:
                    Organizations:
                        - *Org1
                        - *Org2
        TwoOrgsChannel:
            Consortium: SampleConsortium
            Application:
                <<: *ApplicationDefaults
                Organizations:
                    - *Org1
                    - *Org2
    Organizations:
        - &OrdererOrg
            Name: OrdererOrg
            ID: OrdererMSP
            MSPDir: ./example.com/msp
        - &Org1
            Name: Org1MSP
            ID: Org1MSP
            MSPDir: ./org1.example.com/msp
            AnchorPeers:
                - Host: peer0.org1.example.com
                  Port: 7051
        - &Org2
            Name: Org2MSP
            ID: Org2MSP
            MSPDir: ./org2.example.com/msp
            AnchorPeers:
                - Host: peer0.org2.example.com
                  Port: 7051
    Orderer: &OrdererDefaults
        OrdererType: solo
        Addresses:
            - orderer.example.com:7050
        BatchTimeout: 2s
        BatchSize:
            MaxMessageCount: 10
            AbsoluteMaxBytes: 99 MB
            PreferredMaxBytes: 512 KB
        Kafka:
            Brokers:
                - 127.0.0.1:9092
        Organizations:
    Application: &ApplicationDefaults
        Organizations:

    注意 configtx.yaml 中使用的每個組織的msp,不是組件的或者用户的。這個文檔備用。

    更新msp和tls:

    rm -rf orderer.example.com/msp/ orderer.example.com/tls/
    cp -rf example.com/orderer/msp orderer.example.com/
    cp -rf example.com/orderer/tls orderer.example.com/
    
    rm -rf peer0.org1.example.com/msp/ peer0.org1.example.com/tls/
    cp -rf org1.example.com/peer0/msp  peer0.org1.example.com/
    cp -rf org1.example.com/peer0/tls  peer0.org1.example.com/
    
    rm -rf peer1.org1.example.com/msp/ peer1.org1.example.com/tls/
    cp -rf org1.example.com/peer1/msp  peer1.org1.example.com/
    cp -rf org1.example.com/peer1/tls  peer1.org1.example.com/
    
    rm -rf peer0.org2.example.com/msp/ peer0.org2.example.com/tls/
    cp -rf org2.example.com/peer0/msp  peer0.org2.example.com/
    cp -rf org2.example.com/peer0/tls  peer0.org2.example.com/
    

    然後重新部署下面的組件,參考 hyperledger的fabric項目的全手動部署: 開始部署 。

    scp -r orderer.example.com/*     root@192.168.88.10:/opt/app/fabric/orderer/
    scp -r peer0.org1.example.com/*  root@192.168.88.10:/opt/app/fabric/peer/
    scp -r peer1.org1.example.com/*  root@192.168.88.11:/opt/app/fabric/peer/
    scp -r peer0.org2.example.com/*  root@192.168.88.12:/opt/app/fabric/peer/

    重新部署的時候,注意將之前已經啟動的服務停止,並刪除安裝文檔。

    重新部署完成後,重新生成./genesisblock文檔,並上傳到orderer.example.com的安裝路徑中:

    ./bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./genesisblock

    這裏沒有使用中間CA,生成genesisblock的時候,會提示:

    2018-05-04 16:37:17.788 CST [msp] getPemMaterialFromDir -> WARN 002 Failed reading file ca-files/example.com/msp/intermediatecerts/localhost-7054.pem: no pem content for file ca-files/example.com/msp/intermediatecerts/localhost-7054.pem

    將intermediatecerts中的文檔刪除即可,

    rm example.com/msp/intermediatecerts/localhost-7054.pem
    rm org1.example.com/msp/intermediatecerts/localhost-7054.pem
    rm org2.example.com/msp/intermediatecerts/localhost-7054.pem

    如果是通過intermediateCA生成的證書,intermediatecerts中包含中間CA的證書。這裏只部署了一個fabric-ca作為rootCA,因此intermediatecerts中是一個空文檔。

    將生成的genesisblock上傳到orderer.example.com:

    scp genesisblock root@192.168.88.10:/opt/app/fabric/orderer/

    可以用下面的命令查看創始塊的內容:

    ./bin/configtxgen  -inspectBlock genesisblock

    然後重新啟動fabric的所有組件。

    更新用户的證書以及後續操作

    因為我們是在 hyperledger的fabric項目的全手動部署 執行結束後得到的fabric-deploy目錄基礎上,進行操作的。

    所有還要更新一下該目錄下用户目錄中的msp:

    $ rm -rf Admin\@org1.example.com/msp
    $ cp -rf org1.example.com/admin/msp Admin\@org1.example.com/
    $ cd Admin\@org1.example.com
    $ ./peer.sh node status
    status:STARTED
    2018-05-04 17:03:06.202 CST [main] main -> INFO 001 Exiting.....
    
    $ rm -rf Admin\@org2.example.com/msp
    $ cp -rf org2.example.com/admin/msp Admin\@org2.example.com/
    $ cd Admin\@org2.example.com
    $ ./peer.sh node status
    status:STARTED
    2018-05-04 17:08:27.959 CST [main] main -> INFO 001 Exiting.....

    重新創建channel,設置anchor peer:

    ./bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx mychannel.tx -channelID mychannel
    ./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
    ./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
    
    cd Admin\@org1.example.com/
    ./peer.sh channel create -o orderer.example.com:7050 -c mychannel -f ../mychannel.tx --tls true --cafile tlsca.example.com-cert.pem
    cp mychannel.block ../Admin\@org2.example.com/
    ./peer.sh channel join -b mychannel.block
    ./peer.sh channel join -b mychannel.block   //將peer.sh中的peer0修改為peer1後在執行一次
    ./peer.sh channel update -o orderer.example.com:7050 -c mychannel -f ../Org1MSPanchors.tx --tls true --cafile ./tlsca.example.com-cert.pem
    
    cd ../Admin\@org2.example.com/
    ./peer.sh channel join -b mychannel.block
    ./peer.sh channel update -o orderer.example.com:7050 -c mychannel -f ../Org2MSPanchors.tx --tls true --cafile ./tlsca.example.com-cert.pem

    這些操作的含義見: hyperledger的fabric項目的全手動部署-創建channel與peer的設置

    後續的合約創建、更新、調用等操作這裏就不演示了,請直接查看: hyperledger的fabric項目的全手動部署

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

2条评论
  • 希望明天

    2018年11月27日 下午5:22

    你这不是抄写的别人的嘛,还不写明出处:
    https://www.lijiaocn.com/项目/2018/05/04/fabric-ca-example.html

    1. melanc

      2018年12月7日 上午10:15

      没注明出处确实不对,以后会注意

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签云