Fabric-CA-1.0-Alpha 小结

1. 整体概览#

在Fabric 1.0版本中,原0.6版本中的“Membership Service”已经被“Fabric CA”所替代。

Fabric CA是Hyperledger Fabric的证书颁发机构,它提供的功能如下:
1)身份的注册,或连接到LDAP作为用户注册表;
2)发放登记证书(ECerts);
3)发布交易证书(TCerts),在Hyperledger Fabric blockchain上进行交易时提供匿名性和不可链接性;
4)证书更新和撤销。

Fabric CA包含一个client端和一个server端。在Fabric 1.0版本中,CA可以脱离Docker镜像,作为一个独立的服务来运行。若使用docker启动,所有的CA服务都是在一个专门的镜像(名称类似于“ca”)中进行执行。

Fabric CA提供了两种访问方式调用Server服务,一种是通过Client调用,另一种是通过SDK调用。两种调用都是REST风格的。SDK的API接口位于fabric-ca工程的fabric-ca/swagger/swagger-fabric-ca.json。本文使用的是通过Client调用。

1.1 Fabric CA整体架构图##


[1]
Server端由一个集群组成,包括前端的一个高可用的代理服务器,连接着若干个CA Server集群,这些集群将数据共同存放在同一个数据服务器上。数据库可能是MySQL、LDAP、PostgresSQL或者SQLite。

1.2 Fabric CA运行流程的时序图##

具体步骤包括:

1) Server端初始化
2) CA根证书生成
3) Server端启动服务
4) Client端向Server端请求登记
5) Server端向Client端返回登记证书ECert
6) Client端向Server端请求注册节点
7) Server端向Client端返回节点注册信息结果
8) Client端向Server端请求登记节点
9) Server端生成TCert,存入数据库
10) Server端向Client端返回登记结果

2. 安装#

2.1 Dokcer启动##

2.1.1 拉取镜像###

docker pull hyperledger/fabric-ca:x86_64-1.0.0-alpha

2.1.2 使用Docker-Compose启动###

将下部分代码添加到docker-compose.yaml中的service中,使用docker-compose up启动ca服务节点

  ca:
    image: hyperledger/fabric-ca:x86_64-1.0.0-alpha
    container_name: fabric-ca
    ports:
      - "8888:8888"
    environment:
      - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca
    volumes:
      - "./fabric-ca:/etc/hyperledger/fabric-ca"
    command: sh -c 'fabric-ca-server start -b admin:adminpw'

2.2 Native启动##

2.2.1前提条件###

  • Go 1.7版本或以上
  • GOPATH环境配置正确
  • 安装libtool和libtdhl-dev

2.2.2 安装方法

可直接使用“go get”命令进行安装。“go get”相当于“git clone”+“go install”

#go get -u github.com/hyperledger/fabric-ca/cmd/...

后续的操作和在Docker中类似,这里我们主要讲Docker启动的情况。

3. Fabric-CA-Server#

官方说明如下:

Hyperledger Fabric Certificate Authority Server

Usage:
  fabric-ca-server [command]

Available Commands:
  init        Initialize the fabric-ca server
  start       Start the fabric-ca server

Flags:
      --address string                  Listening address of fabric-ca-server (default "0.0.0.0")
  -b, --boot string                     The user:pass for bootstrap admin which is required to build default config file
      --ca.certfile string              PEM-encoded CA certificate file (default "ca-cert.pem")
      --ca.chainfile string             PEM-encoded CA chain file (default "ca-chain.pem")
      --ca.keyfile string               PEM-encoded CA key file (default "ca-key.pem")
  -n, --ca.name string                  Certificate Authority name
  -c, --config string                   Configuration file (default "fabric-ca-server-config.yaml")
      --csr.cn string                   The common name field of the certificate signing request to a parent fabric-ca-server
      --csr.serialnumber string         The serial number in a certificate signing request to a parent fabric-ca-server
      --db.datasource string            Data source which is database specific (default "fabric-ca-server.db")
      --db.tls.certfiles string         PEM-encoded comma separated list of trusted certificate files (e.g. root1.pem, root2.pem)
      --db.tls.client.certfile string   PEM-encoded certificate file when mutual authenticate is enabled
      --db.tls.client.keyfile string    PEM-encoded key file when mutual authentication is enabled
      --db.tls.enabled                  Enable TLS for client connection
      --db.type string                  Type of database; one of: sqlite3, postgres, mysql (default "sqlite3")
  -d, --debug                           Enable debug level logging
      --ldap.enabled                    Enable the LDAP client for authentication and attributes
      --ldap.groupfilter string         The LDAP group filter for a single affiliation group (default "(memberUid=%s)")
      --ldap.url string                 LDAP client URL of form ldap://adminDN:adminPassword@host[:port]/base
      --ldap.userfilter string          The LDAP user filter to use when searching for users (default "(uid=%s)")
  -p, --port int                        Listening port of fabric-ca-server (default 7054)
      --registry.maxenrollments int     Maximum number of enrollments; valid if LDAP not enabled
      --tls.certfile string             PEM-encoded TLS certificate file for server's listening port (default "ca-cert.pem")
      --tls.enabled                     Enable TLS on the listening port
      --tls.keyfile string              PEM-encoded TLS key for server's listening port (default "ca-key.pem")
  -u, --url string                      URL of the parent fabric-ca-server

Use "fabric-ca-server [command] --help" for more information about a command.

3.1 初始化Server服务##

3.1.1 指令介绍###

# fabric-ca-server init -b admin:adminpw

-b在这里指的是bootstrap,也就是启动加载状态。

有一个名为fabric-ca-server-config.yaml的配置文件会在节点启动时自动生成。我们也可以根据里面的内容自定义配置CSR信息,使用 --config 文件名来进行启动配置。

CSR为“Certificate Signing Request”的缩写,即证书签名请求。

目前在keys中支持的算法和相关长度如下:


Fabric-CA 1.0支持MySQL、LDAP、PostgresSQL和SQLite,这里配置用的是默认的SQLite。其他配置方法暂且不表。

3.1.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-server init -b admin:adminpw
2017/03/21 08:54:23 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/fabric-ca-server-config.yaml
2017/03/21 08:54:23 Initialize BCCSP [SW]
2017/03/21 08:54:23 [INFO] The CA key and certificate files already exist
2017/03/21 08:54:23 [INFO] Key file location: /etc/hyperledger/fabric-ca/ca-key.pem
2017/03/21 08:54:23 [INFO] Certificate file location: /etc/hyperledger/fabric-ca/ca-cert.pem
2017/03/21 08:54:23 [INFO] Initialized sqlite3 data base at /etc/hyperledger/fabric-ca/fabric-ca-server.db
2017/03/21 08:54:23 [INFO] Initialization was successful

3.2 启动Server服务##

3.2.1 指令介绍###

fabric-ca-server start -b <admin>:<adminpw>

这里,默认调用的启动配置文件为fabric-ca-server-config.yaml,如果需要自定义配置,还是使用 --config 文件名来进行启动配置。
成功后,server端会在预先配置的环境路径下生成相应的ca证书文件,并在配置的端口号上进行服务监听。这里我们在端口8888上进行监听。

3.2.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-server start -b admin:adminpw
2017/03/21 08:54:50 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/fabric-ca-server-config.yaml
2017/03/21 08:54:50 Initialize BCCSP [SW]
2017/03/21 08:54:50 [INFO] The CA key and certificate files already exist
2017/03/21 08:54:50 [INFO] Key file location: /etc/hyperledger/fabric-ca/ca-key.pem
2017/03/21 08:54:50 [INFO] Certificate file location: /etc/hyperledger/fabric-ca/ca-cert.pem
2017/03/21 08:54:50 [INFO] Initialized sqlite3 data base at /etc/hyperledger/fabric-ca/fabric-ca-server.db
2017/03/21 08:54:50 [INFO] Listening at http://0.0.0.0:8888

4. Fabric-CA-Client#

官方说明如下:

Hyperledger Fabric Certificate Authority Client

Usage:
  fabric-ca-client [command]

Available Commands:
  enroll      Enroll user
  getcacert   Get CA certificate chain
  reenroll    Reenroll user
  register    Register user
  revoke      Revoke user

Flags:
  -c, --config string                Configuration file (default "/etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml")
      --csr.cn string                The common name field of the certificate signing request to a parent fabric-ca-server
      --csr.serialnumber string      The serial number in a certificate signing request to a parent fabric-ca-server
  -d, --debug                        Enable debug level logging
      --enrollment.hosts string      Comma-separated host list
      --enrollment.label string      Label to use in HSM operations
      --enrollment.profile string    Name of the signing profile to use in issuing the certificate
      --id.affiliation string        The identity's affiliation
      --id.attr string               Attributes associated with this identity (e.g. hf.Revoker=true)
      --id.maxenrollments int        The maximum number of times the secret can be reused to enroll.
      --id.name string               Unique name of the identity
      --id.secret string             The enrollment secret for the identity being registered
      --id.type string               Type of identity being registered (e.g. 'peer, app, user')
  -M, --mspdir string                Membership Service Provider directory (default "msp")
  -m, --myhost string                Hostname to include in the certificate signing request during enrollment (default "0f86c3e1cf15")
      --tls.certfiles string         PEM-encoded comma separated list of trusted certificate files (e.g. root1.pem, root2.pem)
      --tls.client.certfile string   PEM-encoded certificate file when mutual authenticate is enabled
      --tls.client.keyfile string    PEM-encoded key file when mutual authentication is enabled
      --tls.enabled                  Enable TLS for client connection
  -u, --url string                   URL of fabric-ca-server (default "http://localhost:7054")

Use "fabric-ca-client [command] --help" for more information about a command.

4.1启动用户登记##

4.1.1 指令介绍###

#fabric-ca-client enroll -u http://admin:adminpw@localhost:8888

登记启动用户会在用户的home路径下生成登记证书文件ECert。

4.1.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-client enroll -u http://admin:adminpw@localhost:8888
2017/03/21 08:59:09 [INFO] User provided config file: /etc/hyperledger/fabric-ca/fabric-ca-client-config.yaml
2017/03/21 08:59:09 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/fabric-ca-client-config.yaml
2017/03/21 08:59:09 Initialize BCCSP [SW]
2017/03/21 08:59:09 [INFO] received CSR
2017/03/21 08:59:09 [INFO] generating key: ecdsa-256
2017/03/21 08:59:09 [INFO] encoded CSR
2017/03/21 08:59:09 [INFO] Stored client key at /etc/hyperledger/fabric-ca/msp/keystore/key.pem
2017/03/21 08:59:09 [INFO] Stored client certificate at /etc/hyperledger/fabric-ca/msp/signcerts/cert.pem
2017/03/21 08:59:09 [INFO] Stored CA certificate chain at /etc/hyperledger/fabric-ca/msp/cacerts/.pem

4.2 新身份注册##

4.2.1 指令介绍##

# fabric-ca-client register

在注册阶段,server端需要检查被注册对象的两方面内容:

  • 1 被注册的对象的角色一定要在预先配置的“hf.Registrar.Roles”属性中约定的范围内。比如,如果“hf.Registrar.Roles”约定的角色包括“peer”和“client”,那么被注册对象就只能是这两个角色中的一个,如果被注册对象的角色为“user”,那么注册就不能被通过。
  • 2 被注册对象的从属关系参数一定要归属于预先配置的组织属性。换句话说,被注册对象的affiliation参数一定要是预先配置的affiliation参数的一个前缀。例如,假设预先配置的组织关系为:“a.b.c”,那么新注册的对象的affiliation属性可以是“a.b.c”,也可以是“a.b”,但“a.c”就不能通过注册。

下面是id配置的一个示例:

id:
  name: MyPeer1
  type: peer
  affiliation: org1.department1
  attributes:
    - name: SomeAttrName
      value: SomeAttrValue
    - name: foo
      value: bar

如果想要在后面允许对这个ID进行注销,在这里配置的attributes中要包含“hf.Revoker”,即:

  attributes:
    - name: hf.Revoker
      value: true

4.2.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-client register --id.name cliTest00
2017/03/21 09:05:16 [INFO] User provided config file: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 09:05:16 Initialize BCCSP [SW]
2017/03/21 09:05:16 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
Password: oLtPkJRCzdTH

这里随机生成了一个Password,将在后面的节点登记中用到。
在指令中使用flag来临时自定义用户名和密码:--id.name <用户名> --id.secret <密码>

4.3 节点身份登记##

4.3.1 指令介绍###

官方文档上说,在节点身份登记之前,要先指定FABRIC_CA_CERT_FILE和FABRIC_CA_KEY_FILE两个环境变量,生成的peer.pem和key.pem两个证书文件作为TCert。但是我的试验中,生成的证书文件路径仍然是在FABRIC_CA_CLIENT_HOME相应的路径下,不知是否是仍然存在的bug。

# export FABRIC_CA_CERT_FILE=$MSP_DIR/signcerts/peer.pem
# export FABRIC_CA_KEY_FILE=$MSP_DIR/keystore/key.pem
fabric-ca client enroll -u http://<peer ID>:<password>@localhost:port

这里Peer ID为注册时的id name,password为上文提到的注册时生成的密码。

4.3.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-client enroll -u http://cliTest00:oLtPkJRCzdTH@localhost:8888
2017/03/21 09:07:48 [INFO] User provided config file: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 09:07:48 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 09:07:48 Initialize BCCSP [SW]
2017/03/21 09:07:48 [INFO] received CSR
2017/03/21 09:07:48 [INFO] generating key: ecdsa-256
2017/03/21 09:07:48 [INFO] encoded CSR
2017/03/21 09:07:48 [INFO] Stored client key at /etc/hyperledger/fabric-ca/clients/admin/msp/keystore/key.pem
2017/03/21 09:07:48 [INFO] Stored client certificate at /etc/hyperledger/fabric-ca/clients/admin/msp/signcerts/cert.pem
2017/03/21 09:07:48 [INFO] Stored CA certificate chain at /etc/hyperledger/fabric-ca/clients/admin/msp/cacerts/.pem

4.4 身份重登记##

4.4.1 指令介绍###

# fabric-ca-client reenroll

当登记的身份过期时,可以利用reenroll命令对身份进行重新登记。注意,这个指令后面不能添加登记的名称和密码,只是按照之前配置好的内容进行重新登记。

4.4.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-client reenroll                                                                                                        
2017/03/21 10:11:36 [INFO] User provided config file: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 10:11:36 Initialize BCCSP [SW]
2017/03/21 10:11:36 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 10:11:36 [INFO] received CSR
2017/03/21 10:11:36 [INFO] generating key: ecdsa-256
2017/03/21 10:11:37 [INFO] encoded CSR
2017/03/21 10:11:37 [INFO] Stored client key at /etc/hyperledger/fabric-ca/clients/admin/msp/keystore/key.pem
2017/03/21 10:11:37 [INFO] Stored client certificate at /etc/hyperledger/fabric-ca/clients/admin/msp/signcerts/cert.pem
2017/03/21 10:11:37 [INFO] Stored CA certificate chain at /etc/hyperledger/fabric-ca/clients/admin/msp/cacerts/.pem

4.5 证书或身份撤销##

4.5.1 指令介绍###

Usage:
  fabric-ca-client revoke [flags]

Flags:
  -a, --aki string      AKI
  -e, --eid string      Enrollment ID (Optional)
  -r, --reason string   Reason for revoking
  -s, --serial string   Serial Number

官方给出的说明有些错误,被标(Optional)的应该是“Reason for revoking”部分。实际上,要求指令后面的flag内容为下面两种格式中的一种:

fabric-ca-client revoke -a xxx -s yyy -r <reason>

fabric-ca-client revoke -e <enrollment_id> -r <reason>

支持的<reason>包括:

Reasons:
- unspecified
- keycompromise
- cacompromise
- affiliationchange
- superseded
- cessationofoperation
- certificatehold
- removefromcrl
- privilegewithdrawn
- aacompromise

AKI(Authority Key Identifier)和Serial Number都是在身份登记后生成的cert.pem文件中。我们需要对这个文件进行解析。可以使用OpenSSL进行解析:

root@xiao-virtual-machine:/home/xiao/test/fabric-ca/clients/admin/msp/signcerts# openssl x509 -in cert.pem -text -noout -serial
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4a:ad:ce:89:16:9d:36:23:99:c5:37:7a:e9:2b:06:d7:8a:f3:f3:da
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=North Carolina, O=Hyperledger, OU=Fabric, CN=fabric-ca-server
        Validity
            Not Before: Mar 21 09:21:00 2017 GMT
            Not After : Feb 17 17:21:00 2018 GMT
        Subject: C=US, ST=North Carolina, O=Hyperledger, OU=Fabric, CN=test001
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:8a:ff:a4:44:2c:10:e5:4c:4e:7d:7d:0f:bb:28:
                    aa:c0:30:b8:53:2c:0d:1d:26:92:c9:85:7c:0d:24:
                    7a:c0:25:5c:18:c9:f7:fa:d2:53:e9:00:00:99:d1:
                    04:30:a1:d5:dd:a1:3c:30:37:5a:f9:70:e5:aa:6e:
                    89:6c:54:ad:18
                ASN1 OID: prime256v1
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                6F:76:CA:12:7D:20:5F:27:6F:93:55:15:48:F1:32:6C:83:2A:F6:03
            X509v3 Authority Key Identifier: 
                keyid:A5:1F:27:F8:09:78:46:4D:63:A8:23:48:B3:B4:01:1D:FB:FF:C9:43

            X509v3 Subject Alternative Name: 
                DNS:38cdca9067ac
    Signature Algorithm: ecdsa-with-SHA256
         30:46:02:21:00:a6:3e:33:78:e2:59:8c:e2:ba:6f:0f:7b:01:
         66:5d:67:08:af:81:de:1b:47:20:c1:00:e5:11:ba:1e:7a:f1:
         f2:02:21:00:95:1a:31:06:6e:ab:cd:91:85:02:38:8c:72:87:
         70:8e:e5:bc:7d:a8:0f:05:a1:55:60:c9:49:a0:72:1d:a1:11
serial=4AADCE89169D362399C5377AE92B06D78AF3F3DA

这里,AKI为

A5:1F:27:F8:09:78:46:4D:63:A8:23:48:B3:B4:01:1D:FB:FF:C9:43```
(需要去除冒号),Serial Number为

4AADCE89169D362399C5377AE92B06D78AF3F3DA


###4.5.2 示例###

root@0f86c3e1cf15:/etc/hyperledger/fabric-ca# fabric-ca-client revoke -e test01
2017/03/21 11:11:05 [INFO] User provided config file: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 11:11:05 Initialize BCCSP [SW]
2017/03/21 11:11:05 [INFO] Configuration file location: /etc/hyperledger/fabric-ca/clients/admin/fabric-ca-client-config.yaml
2017/03/21 11:11:05 [INFO] Revocation was successful

这里只是表述了通过eid进行撤销的方式,另一种方法总是不能成功,与Hyperledger项目组的成员沟通后,说是存在bug,目前仍在修复中。所以这里暂且不表。

#5. 其他#

##5.1 优先级##
命令执行的优先级从高到低依次是:
 - 1 命令行flag
 - 2 环境变量
 - 3 配置文件

#References#
[1] https://github.com/hyperledger/fabric/blob/master/docs/source/Setup/ca-setup.rst
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容