Install
openclaw skills install cmcc-credentialManage China Mobile Digital Credential flow by loading credentials, binding agent, and authorizing sensitive operations with secure HmacSHA256 signatures and...
openclaw skills install cmcc-credentialThis skill manages the China Mobile Digital Credential authorization flow in three phases:
Phase 1 - Credential Loading (Setup)
Phase 1.5 - Agent Binding
Phase 2 - Authorization Flow (Runtime)
"Javis" (default, can be overridden)"qfx9pkizs42up7y61jsehs9v8e1xms4m" (fixed, cannot be changed)Steps:
Implementation:
# Sort JSON by dictionary order
body_json = json.dumps(body, sort_keys=True, separators=(',', ':'))
# Calculate HMAC-SHA256
signature = hmac.new(
app_key.encode('utf-8'),
body_json.encode('utf-8'),
hashlib.sha256
).digest()
# Convert to uppercase hex
return signature.hex().upper()
Steps:
Implementation:
# Derive 16-byte key from appKey using MD5
key_bytes = hashlib.md5(app_key.encode('utf-8')).digest()
# AES/ECB/PKCS5Padding encryption
cipher = AES.new(key_bytes, AES.MODE_ECB)
encrypted = cipher.encrypt(pad(phone_bytes, AES.block_size))
# Base64 encode
return base64.b64encode(encrypted).decode('utf-8')
New Format:
appId: <app_id>
signValue: <signature>
Content-Type: application/json
Note: Old headers (X-App-Id, X-Sign, X-Timestamp, X-Nonce) are no longer used.
Activate Phase 1 when:
DO NOT make any API calls during Phase 1. Only parse and store credentials.
Read the credential file and extract:
appId: Application ID (24 characters)appKey: Application secret keyStore credentials to memory/cmcc-digital-credential.json:
{
"appId": "...",
"appKey": "..."
}
Important:
appName is predefined as "Javis" (not loaded from file)templateId is predefined as "qfx9pkizs42up7y61jsehs9v8e1xms4m" (not loaded from file)The credential file can be:
Plain text format:
智能体DID=AI20260314152030X7K9M2
智能体密钥=your-secret-key-here
JSON format:
{
"智能体DID": "AI20260314152030X7K9M2",
"智能体密钥": "your-secret-key-here"
}
Note:
appName is predefined as "Javis" (can be overridden via parameter)templateId is predefined as "qfx9pkizs42up7y61jsehs9v8e1xms4m" (fixed)智能体DID (maps to appId) and 智能体密钥 (maps to appKey)Use the load_credentials.py script:
python3 scripts/load_credentials.py load <credential-file>
Or check if credentials exist:
python3 scripts/load_credentials.py check
Activate Phase 1.5 when:
Load credentials from memory (Phase 1):
Call binding API:
scripts/bind_agent.py to bind the agent--appName parameterStore binding status (optional):
Endpoint:
POST /api/cmvc-tocp-server/agent/bind
Request Headers:
appId: <app_id>
signValue: <signature>
Content-Type: application/json
Request Body:
{
"appName": "Javis",
"appId": "your-app-id"
}
Response:
{
"code": 0,
"desc": "Success"
}
Response Codes:
0: Success - Agent bound successfullyUse the bind_agent.py script:
python3 scripts/bind_agent.py \
--appId "$APP_ID" \
--appKey "$APP_KEY"
Or with custom appName (optional):
python3 scripts/bind_agent.py \
--appName "MyCustomApp" \
--appId "$APP_ID" \
--appKey "$APP_KEY"
Or use credentials from memory:
APP_ID=$(python3 scripts/load_credentials.py get --field appId)
APP_KEY=$(python3 scripts/load_credentials.py get --field appKey)
python3 scripts/bind_agent.py --appId "$APP_ID" --appKey "$APP_KEY"
--appName parameter if neededActivate Phase 2 when:
The following operations require authorization:
CRITICAL: Never proceed with a sensitive operation without successful authorization.
Before requesting authorization, verify credentials exist:
python3 scripts/load_credentials.py check
If credentials don't exist, inform user: "Credentials not found. Please provide a credential file first."
scripts/request_authorization.py to call the authorization API/vc/auth/request endpoint with new headersauthRecordId and trustedAuthUrlPresent the authorization link to user:
Please authorize this operation at:
<trustedAuthUrl>
This link is valid for 3 days.
Waiting for authorization...
scripts/poll_authorization.py to check status/vc/auth/query endpoint with authRecordIdstatusCode in responsestatusCode === "000000"If authorization successful (statusCode === "000000"):
credentialSubject if neededIf authorization fails or times out:
https://vctest.cmccsign.com/
/cmvc-tocp-server/vc/auth/requestappId: app_idsignValue: HMAC-SHA256 signature (64 hex chars, uppercase)Content-Type: application/json{
"nonce": "...",
"timestamp": 1710403200000,
"phoneNo": "<AES-encrypted-phone>",
"returnUrl": "", // optional
"notifyUrl": "", // optional
"templateId": "qfx9pkizs42up7y61jsehs9v8e1xms4m",
"sendSmsFlag": "0", // optional, default 0
"smsIntranetTemplateId": "", // optional
"smsExternalTemplateId": "", // optional
"forwardedCredentials": {}, // optional
"authScene": "" // optional
}
{
"code": 0,
"desc": "Success",
"authRecordId": "...",
"trustedAuthUrl": "https://..."
}
/cmvc-tocp-server/vc/auth/queryappId: app_idsignValue: HMAC-SHA256 signature (64 hex chars, uppercase)Content-Type: application/json{
"authRecordId": "..."
}
{
"code": 0,
"desc": "Success",
"statusCode": "000000", // 000000 = authorized
"statusDesc": "已授权",
"credentialSubject": {}
}
User: Here's my credential file:
appId=AI20260314152030X7K9M2
appKey=my-secret-key
Assistant: Credentials loaded successfully.
appId: AI20260314152030X7K9M2
appKey: *** (hidden)
appName: Javis (predefined)
templateId: qfx9pkizs42up7y61jsehs9v8e1xms4m (predefined)
Stored to: memory/cmcc-digital-credential.json
User: Delete the sensitive-data.txt file
Assistant: To perform this operation, I need authorization.
Please provide your phone number:
User: 13800138000
Assistant: [Checking if agent is bound...]
[Binding agent using bind_agent.py with appName="Javis"...]
Agent binding successful.
[Calling request_authorization.py with templateId=qfx9pkizs42up7y61jsehs9v8e1xms4m...]
Please authorize this operation at:
https://vctest.cmccsign.com/auth/xxx
This link is valid for 3 days.
Waiting for authorization...
[Starting poll_authorization.py with 5s interval...]
[After 30 seconds]
Authorization confirmed. Proceeding with operation...
Deleting sensitive-data.txt...
python3 scripts/load_credentials.py load <credential-file>
python3 scripts/bind_agent.py \
--appId "$APP_ID" \
--appKey "$APP_KEY"
Or with custom appName (optional):
python3 scripts/bind_agent.py \
--appName "MyCustomApp" \
--appId "$APP_ID" \
--appKey "$APP_KEY"
Or load from memory:
APP_ID=$(python3 scripts/load_credentials.py get --field appId)
APP_KEY=$(python3 scripts/load_credentials.py get --field appKey)
python3 scripts/bind_agent.py --appId "$APP_ID" --appKey "$APP_KEY"
python3 scripts/request_authorization.py \
--appId "$APP_ID" \
--appKey "$APP_KEY" \
--phoneNo "$PHONE_NUMBER"
Note: templateId is predefined and not required as a parameter
python3 scripts/poll_authorization.py \
--appId "$APP_ID" \
--appKey "$APP_KEY" \
--authRecordId "$AUTH_RECORD_ID" \
--interval 5 \
--timeout 600 \
--verbose
appId and signValue instead of X-App-Id and X-SignX-Timestamp, X-Nonce, X-Sign-Version are no longer requiredsort_keys=True when generating JSON for signature