Indy部署及开发
1、开发环境
开发基于indy-sdk
,其支持的系统有 Ubuntu (1604、1804)、Windows、IOS、Android、Centos、MacOS,支持的语言和平台有Java、Python、IOS、Nodejs、.Net、Rust。
这里我将使用 Ubuntu18.04 作为开发环境,使用Python进行开发测试。
-
安装Docker(略)
-
安装libindy
环境不同参考上面链接
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 sudo add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic master" sudo apt-get update sudo apt-get install -y libindy
-
安装libvcx
环境不同请参考:https://github.com/hyperledger/indy-sdk/tree/master/vcx
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 sudo add-apt-repository "deb https://repo.sovrin.org/sdk/deb bionic master" sudo apt-get update sudo apt-get install -y libvcx
-
下载indy-sdk
git clone https://github.com/hyperledger/indy-sdk.git
-
安装Python依赖
注意:应使用Python3 !
pip3 install python3-indy asyncio
可以执行下面命令检测依赖是否正常(提示连接超时为正常现象)
# 当前目录:indy-sdk cd samples/python python3 -m src.main
-
在本地主机上启动测试pool
其他启动方式清参考上面第一个链接中的文档
docker build -f ci/indy-pool.dockerfile -t indy_pool . docker run -itd -p 9701-9708:9701-9708 indy_pool
-
运行Sample进行测试
依赖于上面测试pool,正常运行则说明环境没问题
# 当前目录:indy-sdk cd samples/python python3 -m src.main
成功运行截图:
2020-04-22 15-18-08屏幕截图.png -
Indy-Node详细部署
请参考:
2、开发指南(Python)
具体请参考:
https://digitid.cn/hyperledger/indy/indy-walkthrough
https://github.com/hyperledger/indy-sdk/tree/master/docs/how-tos
相关完整代码:
https://github.com/hyperledger/indy-sdk/blob/master/samples/python/src/getting_started.py
1)steward agent 连接pool
pool_name = 'pool1'
pool_genesis_txn_path = get_pool_genesis_txn_path(pool_name)
#get_pool_genesis_txn_path在utils.py定义,创世交易
pool_config = json.dumps({"genesis_txn": str(pool_genesis_txn_path)})
await pool.create_pool_ledger_config(pool_name, pool_config)
#创建一个已命名的 pool configuration
pool_handle = await pool.open_pool_ledger(pool_name, None)
#连接在这个 configuration 里提到的 nodes pool
2) steward agent 创建钱包和DID
steward agent 应该取得在账本上以 steward角色记录的拥有对应的 NYM transactions 的 DID 的 ownership。
steward_wallet_name = 'sovrin_steward_wallet'
await wallet.create_wallet(pool_name, steward_wallet_name, None, None, None)
#创建一个已命名钱包
steward_wallet = await wallet.open_wallet(steward_wallet_name, None, None)
#打开/连接钱包
steward_did_info = {'seed': '000000000000000000000000Steward1'}
(steward_did, steward_key) = await did.create_and_store_my_did(steward_wallet, json.dumps(steward_did_info))
#指定seed生成didi和verkey
3)第三方机构连接 steward agent
每个连接实际上应该是一个 Pairwise-Unique Identifiers(DIDs)对(pair)。一个 DID 是由与之连接的一方所有,第二个应该是另一方所有。
双方都知道两个 DIDs 并且知道这个对儿描述的是什么连接。
他们之间的关系是不会共享给其他人的,对于这两方来说,每一个 pairwise 关系都会使用不同的 DIDs,并且每个 DID 都是唯一的。
我们将建立连接的这个过程称为 Onboarding。
以下以faber学校与steward连接为例:
-
Steward 在它的钱包中创建了一个新的 DID,并且这个 DID 仅仅会被用于同 Faber 之间的互动。
(steward_faber_did, steward_faber_key) = await did.create_and_store_my_did(steward_wallet, "{}")
-
Steward 将对应的
NYM
transaction 发送到账本中,创建并发送 NYM 请求。nym_request = await ledger.build_nym_request(steward_did, steward_faber_did, steward_faber_key, None, role) await ledger.sign_and_submit_request(pool_handle, steward_wallet, steward_did, nym_request)
-
Steward 创建了一个包含新建的
DID
和Nonce
的连接请求,并把请求发送给Faber。connection_request = { 'did': steward_faber_did, 'nonce': 123456789 }
-
Faber 接受请求
-
Faber 创建对应DID,这个 DID 仅仅会被用于同 Steward 进行安全的交互。
如果Faber没有钱包会先创建钱包:
await wallet.create_wallet(pool_name, 'faber_wallet', None, None, None) faber_wallet = await wallet.open_wallet('faber_wallet', None, None)
创建DID:
(faber_steward_did, faber_steward_key) = await did.create_and_store_my_did(faber_wallet, "{}")
-
Faber 创建了一个连接反馈(connection response),这个反馈包含了来自于接收到的连接请求的新创建的
DID
,Verkey
和Nonce
。connection_response = json.dumps({ 'did': faber_steward_did, 'verkey': faber_steward_key, 'nonce': connection_request['nonce'] })
-
Faber 向账本请求 Steward 的 DID 的 Verification key。
steward_faber_verkey = await did.key_for_did(pool_handle, faber_wallet, connection_request['did'])
-
Faber 使用 Steward verkey 将连接的 response 进行了匿名的加密。
anoncrypted_connection_response = await crypto.anon_crypt(steward_faber_verkey, connection_response.encode('utf-8'))
-
Faber 将匿名加密过的连接 response 发送给 Steward。
-
Steward 匿名地将这个连接 response 进行解密。
decrypted_connection_response = \ (await crypto.anon_decrypt(steward_wallet, steward_faber_key, anoncrypted_connection_response)).decode("utf-8")
-
Steward 通过对比 Nonce 来对 Feber 进行授权。
assert connection_request['nonce'] == decrypted_connection_response['nonce']
-
Steward 将 Faber 的 DID 的
NYM
transaction 发送给账本。nym_request = await ledger.build_nym_request(steward_did, decrypted_connection_response['did'], decrypted_connection_response['verkey'], None, role) await ledger.sign_and_submit_request(pool_handle, steward_wallet, steward_did, nym_request)
-
Faber 成功跟 Steward 建立了连接
4)第三方机构创建DID,发送给Steward进行验证
第三方机构依旧以Faber为例:
-
Faber 在它的钱包中创建了一个新的 DID。
(faber_did, faber_key) = await did.create_and_store_my_did(faber_wallet, "{}")
-
Faber 准备了一个将要包含这个新创建的 DID 以及 verkey 的消息。
# Faber Agent faber_did_info_json = json.dumps({ 'did': faber_did, 'verkey': faber_key })
-
Faber 对消息进行授权及加密,这个是对 authenticated-encryption schema 的一个实现。
被授权的加密被设计用来向指定的接收者发送一个对接收者比较特别的机密的消息。发送者可以使用接收者的公钥(verkey)和它的密钥(signing)计算出一个共享的安全密钥。接收者使用发送者的公钥(verkey)和它的密钥(signing)能够计算出完全一致的共享的安全密钥。这个共享的密钥(secret key)可以在最终解密前可以用来验证被加密过的消息并没有被篡改过。
authcrypted_faber_did_info_json = \ await crypto.auth_crypt(faber_wallet, faber_steward_key, steward_faber_key, faber_did_info_json.encode('utf-8'))
-
Faber 将加密过的消息发送给 Steward。
-
Steward 将接收到的消息进行解密。
sender_verkey, authdecrypted_faber_did_info_json = \ await crypto.auth_decrypt(steward_handle, steward_faber_key, authcrypted_faber_did_info_json) faber_did_info = json.loads(authdecrypted_faber_did_info_json)
-
Steward 请求 Faber 的 DID 的确认密钥(verification key)。
faber_verkey = await did.key_for_did(pool_handle, from_wallet, faber_did_info['did'])
-
Steward 通过对比消息发送者的 Verkey 和从账本中获得的 Faber 的 Verkey 来给 Faber 授权(相同的情况下)。
assert sender_verkey == faber_verkey
-
Steward 会以
TRUST ANCHOR
的角色将对应的 NYM transaction 发送给账本。nym_request = await ledger.build_nym_request(steward_did, decrypted_faber_did_info_json['did'], decrypted_faber_did_info_json['verkey'], None, 'TRUST_ANCHOR') await ledger.sign_and_submit_request(pool_handle, steward_wallet, steward_did, nym_request)
5)设置 Credential Schemas
凭证的数据模板。指凭证架构的整体,包括元数据和JSON架构。
它描述了一个特定的 Credential 可以包含的属性列表,是颁发凭证的前提。(已存在的不能被更新)
一个 Credential Schema 可以被任何的 Trust Anchor 创建并存储到账本中。
例如:
{
"id": "1",
"name": "gvt",
"version": "1.0",
"ver": "1.0",
"attrNames": ["age", "sex", "height", "name"]
}
下边就是 政府(Government) 是如何创建和发布成绩单的 Credential Schema 到账本中的:
-
Trust Anchor 来创建一个 Credential Schema,这个会返回新生成的 Credential Schema。
(transcript_schema_id, transcript_schema) = \ await anoncreds.issuer_create_schema(government_did, 'Transcript', '1.2', json.dumps(['first_name', 'last_name', 'degree', 'status', 'year', 'average', 'ssn']))
-
Trust Anchor 创建 Schema 请求,发送新创建的请求,以此将对应的 Schema transaction 发送给账本。
schema_request = await ledger.build_schema_request(government_did, transcript_schema) await ledger.sign_and_submit_request(pool_handle, government_wallet, government_did, schema_request)
-
用同样的方式,政府创建并发布了 Job-Certificate Credential Schema 到账本中:
(job_certificate_schema_id, job_certificate_schema) = \ await anoncreds.issuer_create_schema(government_did, 'Job-Certificate', '0.2', json.dumps(['first_name', 'last_name', 'salary', 'employee_status', 'experience'])) schema_request = await ledger.build_schema_request(government_did, json.dumps(to the Ledger)) await ledger.sign_and_submit_request(pool_handle, government_wallet, government_did, schema_request)
-
到目前为止,由政府发布的 成绩单(Transcript) 和 工作证明(Job-Certificate) Credential Schemas 已经被发布在了账本上。
6)设置 Credential Definition
这引用了上面的 Credential Schemas,并宣布谁将使用该架构发行凭据(在这种情况下,我们的信任锚身份),他们计划使用哪种签名方法(“ CL” =“ Camenisch Lysyanskya” ,是indy用于零知识证明的默认方法),它们计划如何处理吊销,等等。(已存在的不能被更新)
一个 Credential Definition 可以被任何 Trust Anchor 创建并保存到账本中。
这里,Faber 大学创建并发布了一个有关已知的 Transacript Credential Schema 的 Credential Definition 到账本中。
-
Trust Anchor 从账本中获得指定的 Credential Schema。
get_schema_request = await ledger.build_get_schema_request(faber_did, transcript_schema_id) get_schema_response = await ledger.submit_request(pool_handle, get_schema_request) (transcript_schema_id, transcript_schema) = await ledger.parse_get_schema_response(get_schema_response)
-
Trust Anchor 创建Credential Definition。对于这个 Credential Schema 的私有的 Credential Definition 部分也将会被存储在钱包中,但是是不可能直接读取出来的。
(faber_transcript_cred_def_id, faber_transcript_cred_def_json) = \ await anoncreds.issuer_create_and_store_credential_def(faber_wallet, faber_did, transcript_schema, 'TAG1', 'CL', '{"support_revocation": false}')
-
Trust Anchor 将对应的
CredDef
transaction 发送到账本中。cred_def_request = await ledger.build_cred_def_request(faber_did, faber_transcript_cred_def_json) await ledger.sign_and_submit_request(pool_handle, faber_wallet, faber_did, cred_def_request)
-
同样的方式,Acem 公司 为这个已知的 Job-Certificate Credential Schema 创建并发布了一个 Credential Definition 到账本中。
# Acme Agent get_schema_request = await ledger.build_get_schema_request(acme_did, job_certificate_schema_id) get_schema_response = await ledger.submit_request(pool_handle, get_schema_request) (job_certificate_schema_id, job_certificate_schema) = await ledger.parse_get_schema_response(get_schema_response) (acme_job_certificate_cred_def_id, acme_job_certificate_cred_def_json) = \ await anoncreds.issuer_create_and_store_credential_def(acme_wallet, acme_did, job_certificate_schema, 'TAG1', 'CL', '{"support_revocation": false}') cred_def_request = await ledger.build_cred_def_request(acme_did, acme_job_certificate_cred_def_json) await ledger.sign_and_submit_request(pool_handle, acme_wallet, acme_did, cred_def_request)
-
到目前为止,我们已经有了一个由 Acem 公司 发布的关于 Job-Certificate Credential Schema 的 Credential Definition,和一个由 Faber 大学 发布的有关 Transcript Credential Schema 的 Credential Definition。
7)获取Credential
一个 credential 是有关一个身份的部分信息 – 一个名字,年龄,信用积分…这些信息都应该是真实的。
Credential 是由一个发行方提供的。
这里以Alice成绩单获取为例,Alice毕业于Faber学校。
-
Faber为Alice创建了一个发行 成绩单 Credential 的一个 Credential Offer。
# Faber Agent transcript_cred_offer_json = await anoncreds.issuer_create_credential_offer(faber_wallet, faber_transcript_cred_def_id)
-
Alice 想要查看这个 成绩单 Credential 所包含的属性。
# Alice Agent get_schema_request = await ledger.build_get_schema_request(alice_faber_did, transcript_cred_offer['schema_id']) get_schema_response = await ledger.submit_request(pool_handle, get_schema_request) transcript_schema = await ledger.parse_get_schema_response(get_schema_response) print(transcript_schema['data']) # Transcript Schema: { 'name': 'Transcript', 'version': '1.2', 'attr_names': ['first_name', 'last_name', 'degree', 'status', 'year', 'average', 'ssn'] }
-
创建一个 Master Secret是获取成绩单的前提。
注意:一个 Master Secret 是一个供证明人使用的关于私有数据的 item,用来保证一个 credential 能够唯一地应用于自己。Master Secret 是一个 input,这个 input 合并了来自于多个 Credentials 的数据,用来证明这些 Credentials 有一个通用的主题(common subject)(证明者)。一个 Master Secret 应该只有证明者自己知道。
# Alice Agent alice_master_secret_id = await anoncreds.prover_create_master_secret(alice_wallet, None)
-
Alice 还需要得到对应于在 成绩单 Credential Offer 中的
cred_def_id
的 Credential Definition。# Alice Agent get_cred_def_request = await ledger.build_get_cred_def_request(alice_faber_did, transcript_cred_offer['cred_def_id']) get_cred_def_response = await ledger.submit_request(pool_handle, get_cred_def_request) faber_transcript_cred_def = await ledger.parse_get_cred_def_response(get_cred_def_response)
-
Alice 已经有了用于创建一个有关发行 Faber 大学成绩单 Credential 的请求的所有信息。
# Alice Agent (transcript_cred_request_json, transcript_cred_request_metadata_json) = \ await anoncreds.prover_create_credential_req(alice_wallet, alice_faber_did, transcript_cred_offer_json, faber_transcript_cred_def, alice_master_secret_id)
-
Faber 大学 为这个 成绩单 Credential Schema 中的每个属性准备 raw 和 encoded 值。Faber 大学为 Alice 创建了 成绩单 Credential。
# Faber Agent # note that encoding is not standardized by Indy except that 32-bit integers are encoded as themselves. IS-786 transcript_cred_values = json.dumps({ "first_name": {"raw": "Alice", "encoded": "1139481716457488690172217916278103335"}, "last_name": {"raw": "Garcia", "encoded": "5321642780241790123587902456789123452"}, "degree": {"raw": "Bachelor of Science, Marketing", "encoded": "12434523576212321"}, "status": {"raw": "graduated", "encoded": "2213454313412354"}, "ssn": {"raw": "123-45-6789", "encoded": "3124141231422543541"}, "year": {"raw": "2015", "encoded": "2015"}, "average": {"raw": "5", "encoded": "5"} }) transcript_cred_json, _, _ = \ await anoncreds.issuer_create_credential(faber_wallet, transcript_cred_offer_json, transcript_cred_request_json, transcript_cred_values, None, None)
-
现在,成绩单 Credential 已经被发行出来了。Alice 将它保存在了自己的钱包中。
# Alice Agent await anoncreds.prover_store_credential(alice_wallet, None, transcript_cred_request_json, transcript_cred_request_metadata_json, transcript_cred_json, faber_transcript_cred_def, None)
8)为其他第三方机构提供证明
这里以Alice申请工作为例,Alice需要为Acme Corp提供**工作申请表(Job Application)**,这个申请表中要求一个名字、学历、状态、SSN 和是否满足在校平均分数的一些条件。
Job-Application Proof Request如下:(属性部分是不可证实的)
# Acme Agent
job_application_proof_request_json = json.dumps({
'nonce': '1432422343242122312411212',
'name': 'Job-Application',
'version': '0.1',
'requested_attributes': {
'attr1_referent': {
'name': 'first_name'
},
'attr2_referent': {
'name': 'last_name'
},
'attr3_referent': {
'name': 'degree',
'restrictions': [{'cred_def_id': faber_transcript_cred_def_id}]
},
'attr4_referent': {
'name': 'status',
'restrictions': [{'cred_def_id': faber_transcript_cred_def_id}]
},
'attr5_referent': {
'name': 'ssn',
'restrictions': [{'cred_def_id': faber_transcript_cred_def_id}]
},
'attr6_referent': {
'name': 'phone_number'
}
},
'requested_predicates': {
'predicate1_referent': {
'name': 'average',
'p_type': '>=',
'p_value': 4,
'restrictions': [{'cred_def_id': faber_transcript_cred_def_id}]
}
}
})
-
显示 Alice 可以用来创建 Job-Application Proof 请求的 Proof 的 Credentials
# Alice Agent creds_for_job_application_proof_request = json.loads( await anoncreds.prover_get_credentials_for_proof_req(alice_wallet, job_application_proof_request_json))
Alice 只有一项 credential 满足 Job Application 的 proof 要求。
# Alice Agent { 'referent': 'Transcript Credential Referent', 'attrs': { 'first_name': 'Alice', 'last_name': 'Garcia', 'status': 'graduated', 'degree': 'Bachelor of Science, Marketing', 'ssn': '123-45-6789', 'year': '2015', 'average': '5' }, 'schema_id': job_certificate_schema_id, 'cred_def_id': faber_transcript_cred_def_id, 'rev_reg_id': None, 'cred_rev_id': None }
现在 Alice 可以将这些属性分为三个组:
- 属性值将会被透漏的
- 属性值将不会被透漏的
- 对于创建可证实的 proof 不需要的属性
对于这个 Job-Application Proof Request,Alice 将属性按照下边的方式分组:
# Alice Agent job_application_requested_creds_json = json.dumps({ 'self_attested_attributes': { 'attr1_referent': 'Alice', 'attr2_referent': 'Garcia', 'attr6_referent': '123-45-6789' }, 'requested_attributes': { 'attr3_referent': {'cred_id': cred_for_attr3['referent'], 'revealed': True}, 'attr4_referent': {'cred_id': cred_for_attr4['referent'], 'revealed': True}, 'attr5_referent': {'cred_id': cred_for_attr5['referent'], 'revealed': True}, }, 'requested_predicates': {'predicate1_referent': {'cred_id': cred_for_predicate1['referent']}} })
-
Alice 必须为每一个使用的 Credential 取得 Credential Schema 和对应的 Credential Definition,就像在创建 Credential 请求时候的步骤一样。
-
现在 Alice 有了创建 Acme Job-Application 创建 Proof Request 的 Proof 的所有信息。
# Alice Agent apply_job_proof_json = \ await anoncreds.prover_create_proof(alice_wallet, job_application_proof_request_json, job_application_requested_creds_json, alice_master_secret_id, schemas_json, cred_defs_json, revoc_states_json)
-
当 Acme 公司 收到了这个 Proof 的时候,他们看到的应该是下边的结构:
# Acme Agent { 'requested_proof': { 'revealed_attrs': { 'attr4_referent': {'sub_proof_index': 0, 'raw':'graduated', 'encoded':'2213454313412354'}, 'attr5_referent': ['sub_proof_index': 0, 'raw':'123-45-6789', 'encoded':'3124141231422543541'}, 'attr3_referent': ['sub_proof_index': 0, 'raw':'Bachelor of Science, Marketing', 'encoded':'12434523576212321'} }, 'self_attested_attrs': { 'attr1_referent': 'Alice', 'attr2_referent': 'Garcia', 'attr6_referent': '123-45-6789' }, 'unrevealed_attrs': {}, 'predicates': { 'predicate1_referent': {'sub_proof_index': 0} } }, 'proof' : [] # Validity Proof that Acme can check 'identifiers' : [ # Identifiers of credentials were used for Proof building { 'schema_id': job_certificate_schema_id, 'cred_def_id': faber_transcript_cred_def_id, 'rev_reg_id': None, 'timestamp': None } } }
-
Acem 得到了所有所需的属性。现在 Acem 想要校验这个 Validity Proof。
Acem 必须要获得在 Proof 中提及的每个 identifier 的 Credential Schema 和相对应的 Credential Definition,就像 Alice 之前做的一样。现在 Acme 有了验证来自于 Alice 的 Job-Application Proof 的所有信息。
# Acme Agent assert await anoncreds.verifier_verify_proof(job_application_proof_request_json, apply_job_proof_json, schemas_json, cred_defs_json, revoc_ref_defs_json, revoc_regs_json)
-
假设这个申请表被接受了并且 Alice 最终得到了这份工作。Acem 为 Alice 创建了一个新的 Credential Offer。
# Acme Agent job_certificate_cred_offer_json = await anoncreds.issuer_create_credential_offer(acme_wallet, acme_job_certificate_cred_def_id)
网友评论