简体   繁体   中英

Hyperledger Fabric: Unable to Update Certificate Attributes in Node SDK

I'm making a block chain application for which I'm using Hyperledger Fabric Node SDK . When a user signs up then the user's certificate is added to the wallet as an identity. Now the attributes that I'm storing in a user's certificate are the email and password . Also, the user's password is not stored anywhere else, but only in the certificate. Now, I also need to update the user's password.

Searching on the internet, I got to know that the attributes in the certificate can be updated using the identityService's update function provided in Node and you don't have to re-enroll a user if you're using the identityService's update function. The issue is that I tried it and the response showed the updated password. But then when I checked the certificate, the old password was still there ie it had not been updated. I'm using the fabric-ca-client package for the FabricCAServices and the x509 package for the x509 certificates.

The code with which I am enrolling and registering the user is as follows:

let ccp = await getCCP(orgName);
const caURL = await getCaUrl(orgName, ccp)
const ca = new FabricCAServices(caURL);
    
const walletPath = await getWalletPath(orgName);
const wallet = await Wallets.newFileSystemWallet(walletPath);
logger.info(`Wallet path: ${walletPath}`);

const userIdentity = await wallet.get(email); //getting user from the wallet
if (userIdentity) //if found so user is already registered
{
    return "User Already Exists";
}
    
//else user does not exist so checking if admin exists
let adminIdentity = await wallet.get('admin'); //getting admin from wallet
if (!adminIdentity) { //if not found so admin does not exist
    logger.debug('An identity for the admin user "admin" does not exist in the wallet');
    await enrollAdmin(orgName, ccp); //enrolling the admin
    adminIdentity = await wallet.get('admin');
    logger.debug("Admin Enrolled Successfully");
}

//now admin exists, so making a user object for authenticating with the CA(i.e. admin)
const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
const adminUser = await provider.getUserContext(adminIdentity, 'admin');
let secret;
try {
    //registering the user, enrolling the user, and importing the new identity into the wallet.
    secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: email, role: 'client', attrs: [{name: 'userType', value: userType, ecert: true},{name: 'password', value: password, ecert: true}]}, adminUser);
} catch (error) {
    return error.message;
}

const enrollment = await ca.enroll({ enrollmentID: email, enrollmentSecret: secret, attr_reqs: [{  name: 'userType', optional: false }, {  name: 'password', optional: false }] });

let x509Identity;
if (orgName == "Org1") {
    x509Identity = {
        credentials: {
            certificate: enrollment.certificate,
            privateKey: enrollment.key.toBytes(),
        },
        mspId: 'Org1MSP',
        type: 'X.509',
    };
} else if (orgName == "Org2") {
    x509Identity = {
        credentials: {
            certificate: enrollment.certificate,
            privateKey: enrollment.key.toBytes(),
        },
        mspId: 'Org2MSP',
        type: 'X.509',
    };
}
await wallet.put(email, x509Identity); //adding user to wallet
return "Registered!";

And the code with which I check the attributes stored in the password is:

//Getting wallet path for orgName...
const walletPath = await getWalletPath(orgName);
const wallet = await Wallets.newFileSystemWallet(walletPath);
logger.info(`Wallet path: ${walletPath}`);

//getting this user from the wallet
const userIdentity = await wallet.get(email);
if (userIdentity) { //if found i.e. user is registered
    //parsing certificate to get password
    var issuer = x509.parseCert(userIdentity.credentials.certificate);
    var jsn = issuer.extensions['1.2.3.4.5.6.7.8.1'];
    jsn = jsn.substring(2);
    jsn = (JSON.parse(jsn));
    //here jsn.attrs.password has the password
}

And the code that I'm using to update the certificate attributes is:

let ccp = await getCCP(orgName);
const caURL = await getCaUrl(orgName, ccp);
const ca = new FabricCAServices(caURL);

//Getting wallet path for orgName...
const walletPath = await getWalletPath(orgName);
const wallet = await Wallets.newFileSystemWallet(walletPath);
logger.info(`Wallet path: ${walletPath}`);

const userIdentity = await wallet.get(email); //getting this user from the wallet
if (userIdentity) { //if found i.e. user is registered

    //Create a new gateway for connecting to our peer node
    const gateway = new Gateway();
    await gateway.connect(ccp, { wallet, identity: email, discovery: { enabled: true, asLocalhost: true } });

    const identityService = ca.newIdentityService();

    let adminIdentity = await wallet.get('admin'); //getting admin from wallet
    //now making a user object of the admin
    const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
    const adminUser = await provider.getUserContext(adminIdentity, 'admin');

    var theIdentityRequest = { enrollmentID: email, affiliation: 'org1.department1', attrs: [ {name: 'userType', value: 'Student', ecert: true},{name: 'password', value: newPassword, ecert: true} ] };

    logger.warn("The Request: ", theIdentityRequest);

    let response = await identityService.update(email, theIdentityRequest, adminUser);
                
    logger.warn("userIdenity attributes: ",response.result.attrs);

    await gateway.disconnect();
}

I want to know why the certificate still has the old password and how to update the password in the certificate. And I also want to know that does the identityService update the attributes in the x509 certificate or is reenrolling the user necessary for this?

You can fix it by doing reenroll as you said.

The update command of IdentityService is to update the identity of registered user in CA.
Since this is not the process of updating the certificate, the only way to obtain a certificate containing the newly modified Attr is to obtain a new certificate (the certificate is the X.509 standard).


Why does the attr change require a new certificate?

  • Fabric generally uses the X.509 certificate standard method.(when not using idemix)
  • Actually not only Fabric, almost all certificates on the web follow the standard.
  • In Fabric, the Attr is existed in extensions in the X509(v3) certificate and has the following format.
X509v3 extensions:
    ...
    1.2.3.4.5.6.7.8.1:
        {"attrs":{"hf.Affiliation":"org1.department1","hf.EnrollmentID":"appUser5","hf.Type":"client","userType":"userType"}}
    ...
  • In other words, when Attr is modified, extensions are modified.
  • In X.509(v3), extenstions are also included in the certificate integrity verification process.
  • The integrity verification process is implemented based on PKI and Hash , and is provided in the form of attaching the signature signed by the CA to the hashed data (including extensions) to the certificate.
  • That is, if extensions are modified, the signature in the certificate must be modified, which means that a new certificate must be issued .

Come back to Fabric,

  • The reenroll command is an act of newly issuing enrolled user data based on the registered user .
  • After updating , a new certificate is issued through the reenroll command to obtain a certificate that includes new Attrs in the corresponding certificate.

The following example code has been tested and works. See this.

// registerUser, enrollUser, updateUser, reEnrollUser

/*
 * Copyright IBM Corp. All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

'use strict';

const { Wallets } = require('fabric-network');
const FabricCAServices = require('fabric-ca-client');
const fs = require('fs');
const path = require('path');

async function main() {
    try {
        // 0. given, already enrolled 'admin'
        const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
        const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
        const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
        const ca = new FabricCAServices(caURL);
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = await Wallets.newFileSystemWallet(walletPath);
        const provider = wallet.getProviderRegistry().getProvider('X.509');
        
        // 1. register testUser
        const adminIdentity = await wallet.get('admin');
        const appUser = 'testUser';
        const adminUser = await provider.getUserContext(adminIdentity, 'admin');
        const secret = await ca.register({
                affiliation: 'org1.department2',
                attrs: [{name: 'userType', value: 'userType', ecert: true}],
                enrollmentID: appUser,
                role: 'client'
        }, adminUser);

        // 2. enroll testUser
        const enrollment = await ca.enroll({
                enrollmentID: appUser,
                enrollmentSecret: secret
        });
        const x509Identity = {
                credentials: {
                        certificate: enrollment.certificate,
                        privateKey: enrollment.key.toBytes(),
                },
                mspId: 'Org1MSP',
                type: 'X.509',
        };
        await wallet.put(appUser, x509Identity);

        // 3. update testUser's Identity in Fabric-CA
        const appUserIdentity = await wallet.get(appUser);
        const newAppUser = await provider.getUserContext(appUserIdentity, appUser);
        const identityService = ca.newIdentityService();
        var theIdentityRequest = { enrollmentID: appUser, affiliation: 'org1.department1', attrs: [ {name: 'userType', value: 'Student', ecert: true} ] };
        let response = await identityService.update(appUser, theIdentityRequest, adminUser);
        console.log("userIdenity attributes: ", response.result.attrs);

        // 4. reenroll testUser
        const newEnrollment = await ca.reenroll(newAppUser);
        const newX509Identity = {
                credentials: {
                        certificate: newEnrollment.certificate,
                        privateKey: newEnrollment.key.toBytes(),
                },
                mspId: 'Org1MSP',
                type: 'X.509',
        };
        await wallet.put(`new_${appUser}`, newX509Identity);

    } catch (error) {
        console.error(`Failed to register user "appUser": ${error}`);
        process.exit(1);
    }
}
ls wallet
testUser.id  new_testUser.id

The result of docode the issued certificate ( hf.Affiliation and userType are updated)

// attrs in 'enroll' Certificate's extensions
{"attrs": "hf.Affiliation":"org1.department2","hf.EnrollmentID":"testUser","hf.Type":"client","userType":"userType"}}

// attrs in 'reenroll' Certificate's extensions
{"attrs":{"hf.Affiliation":"org1.department1","hf.EnrollmentID":"testUser","hf.Type":"client","userType":"Student"}}

[PS] Looking at the Version indicated in the client's identity, it seems that there may be a way to upgrade the existing one, but I have not checked or tried it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM