概述
小米数字车钥匙是依托小米智能卡平台的数字车钥匙集成方案,实现将车钥匙装入智能设备。
相较于传统的车钥匙,小米数字车钥匙无需实体车钥匙,将车钥匙功能集成在移动智能终端设备(手机+穿戴),基于SE、TEE等安全功能,使用NFC、蓝牙、UWB等技术连接手机和车,可实现车辆的开门、启动、远程车控、朋友分享等功能。
流程概览
步骤 | 分类 | 内容 | 用途说明 |
1 | 接入准备 | 测试手机及环境准备 | 联调用测试手机刷机 |
测试账号及VPN账号 | 通过上述方法,不需要申请VPN账号,但需要小米配置IP白名单,以及需要在合作方处做一些其他配置工作 | ||
证书交换 | 数字钥匙证书信任链 服务器通信安全性验证 | ||
测试环境相关配置申请 | 用于车厂关于车钥匙功能的访问权限控制 | ||
2 | 相关文档及软件包下载 | 接口文档 | 用于开发联调的接口说明,包括客户端接口和服务端接口文档 |
小米数字钥匙框架 | |||
小米智能卡 应用及SDK输出文档 | |||
小米智能卡网页组件 应用及SDK输出文档 | |||
SDK 应用及SDK输出文档 | 用于车企app集成车钥匙功能,签署保密协议后,可查看 | ||
3 | 功能实现(系统联调) | 整体架构 | |
钥匙开通 | |||
认证交互 | |||
钥匙查询 | |||
钥匙删除 | |||
钥匙分享 | |||
远程车控 | |||
特殊场景说明 | 关于设备挂失、退出账号、恢复出厂设置等场景钥匙处理说明 | ||
车端读卡器要求 | |||
常见问题 | 联调过程中常见问题说明 | ||
4 | UI设计 | UI流程 | 数字车钥匙各功能模块在小米钱包内的开通流程 |
相关素材要求 | 车端相关元素在钱包内展示要求 | ||
5 | 测试验收 | 验收流程 | |
功能测试 | |||
性能测试 | |||
6 | 生产上线 | 生产证书交换 | |
上线申请及配置 | 上线流程及相关配置内容 | ||
7 | 新机上线 | 新机测试上线流程 | 新机的上线流程说明 |
8 | 存量机适配 | 存量机型适配流程 | 存量机适配流程 |
一、接入准备
1、测试手机及环境准备
联调阶段的测试手机,需要对手机进行解锁、刷机、安装系统等操作,一般来说, 小米近2年内发布的NFC手机可以用于WCC2功能联调, UWB手机可以用于WCC3功能联调。
车厂可以自行采购测试手机,并寄给小米刷机。手机采购或邮寄前, 请与小米相关人员确认手机型号是否支持CCC车钥匙。
注:测试手机刷机后, 与小米NFC卡片(公交卡/银行卡/门卡/车钥匙/校园卡等)相关的业务不能在生产环境下使用,手机其它功能不受影响。
2、测试环境小米账号和小米VPN账号
小米VPN账号
车钥匙联调过程中, 测试手机必须通过VPN网络接入小米测试环境, 因此要在测试手机上安装VPN软件。 VPN账号申请和使用方法如下:
1、发送VPN账号申请邮件至did-carkey@xiaomi.com,内容包括申请原因,申请人姓名、邮箱和手机号。中国籍申请人需要提供中文姓名。
2、收到VPN账号开通短信后,通过手机短信实名认证,并设置密码后即可使用。
3、扫码下载APP(小米飞兔),登录后可以一键链接VPN。

测试手机必须登录测试小米账号才能使用数字钥匙功能,发送测试小米账号申请邮件至did-carkey@xiaomi.com,内容包括申请原因,申请人姓名、邮箱和手机号。
手机连上VPN网络后, 在手机“设置-登录小米账号”页面,使用测试小米账号和密码登录。
多台测试手机可以共用一个测试账号。
3、证书交换
1、车厂发起证书交换申请, 邮件发送至 did-carkey@xiaomi.com
2、小米发送Device OEM CA Certificate 的CSR文件给车厂,车厂签署 [F] - Device OEM CA Certificate (Signed by Vehicle OEM)
3、车厂回复以下证书给小米
- [F] Device OEM CA Certificate (Signed by Vehicle OEM)
- [J] Vehicle OEM CA Certificate
- [P] Vehicle OEM Privacy Encryption Certificate
- [Q] Vehicle OEM Signature Key Certificate
同时提供以下车厂ID信息。
Vehicle OEM | Vehicle Brand | Vehicle OEM Identifier | Vehicle Brand Name | Vehicle Brand Identifier |
小米服务器和车服务器都需要支持双向SSL认证,以保证双方通信时数据安全可靠
1、小米发送小米侧SSL Client Certificate的CSR文件给车厂,由车厂签发
2、车厂提供车厂侧SSL Client Certificate的CSR文件给小米,由小米签发
证书交换过程中,证书文件均需要加密,秘钥与证书文件分不同渠道发送
二、功能实现
小米数字车钥匙满足CCC规范中对手机的技术要求,下面的内容将帮助开发者了解如何快速接入小米CCC数字车钥匙服务。
1、架构
CCC车钥匙架构如下

CCC车钥匙架构中的每个节点各自都发挥着不同作用,他们职责明确并相互连接,从而支撑起整个数字车钥匙系统
按照角色划分如下
1、车
负责管理车端证书和钥匙数据
实现NFC、蓝牙、UWB模块的相关功能,如配对、开关车门、启动引擎等
2、车服务器
负责管理车服务器证书
记录并管理钥匙生命周期
建立与车端的通信连接并下发命令
3、设备(手机)
Native App:手机厂商的数字车钥匙应用,一般指手机钱包App
车App:汽车厂商的数字车钥匙应用
DKF:
负责管理设备和钥匙的证书信息
负责管理SE中的钥匙数据
实现和车端通信的能力,包括NFC、蓝牙、UWB
将如上的能力暴露接口供Native App和车App使用
4、设备服务器
负责安装和管理SE中的CCC Applet
负责和车服务器进行对接,接收钥匙事件通知并执行和钥匙有关的操作,例如云端删除
记录并管理设备中钥匙的生命周期,例如云端挂失
5、Relay Server
由设备厂商或者第三方厂商提供,实现跨品牌的钥匙分享能力
2、开发准备
双向SSL证书互签
小米与车服务器采用双向SSL方式进行安全通信,汽车厂商需要提供通信证书链,小米会对其进行信任配置
车服务器SSL证书到期更换前,需要将新证书链发送小米进行配置
交换云端地址
小米服务器接口地址如下
API类型 | 测试环境URL | 生产环境URL |
Health Check API | https://staging3-sp-server.tsm.pay.xiaomi.com/ccc/v1/healthcheck | https://sp-server.tsm.pay.xiaomi.com/ccc/v1/healthcheck |
Manage Key API | https://staging3-sp-server.tsm.pay.xiaomi.com/ccc/v1/manageKey | https://sp-server.tsm.pay.xiaomi.com/ccc/v1/manageKey |
Event Notification API | https://staging3-sp-server.tsm.pay.xiaomi.com/ccc/v1/eventNotification | https://sp-server.tsm.pay.xiaomi.com/ccc/v1/eventNotification |
车服务器需要提供如下API给小米(did-carkey@xiaomi.com),车服务器的接口使用说明参考:“小米请求车服务器接口说明.pdf”(签署协议后可查看)
API类型 | 测试环境URL | 生产环境URL |
Health Check API | ||
Track Key API | ||
Manage Key API |
3、功能开发
CCC车钥匙主要包括了如下使用场景:钥匙开通、钥匙查询、钥匙删除、刷卡使用、钥匙分享、远程车控
开发者需要实现各个场景下的相关功能,从而串起整个车钥匙业务
功能开发准备,向小米申请SDK服务访问权限,需要提供的信息:应用包名,应用SHA1签名
3.1、钥匙开通
3.1.1、钥匙开通
遵循CCC协议数字车钥匙添加方式有三种:从小米智能卡侧发起添加、从车厂App发起开通、从车端Reader发起开通(可选)
- 从钱包侧发起开通
从钱包主页面车钥匙入口进入开通流程,参考UI流程
- 从车厂App发起开通

调用createDigitalKey接口将拉起NativeApp(Wallet)的钥匙开通页面,待钥匙开通完成,将返回车辆OEMApp页面。
接口调用步骤为:getInstance->isSupportedByDevice->getWirelessCapabilities(可选)->isCreateDigitalKeyPossible->createDigitalKey
具体见《DKF_SDK_API_20230404(外部修正版)》第3.3节
钥匙信息DigitalKeyData,具体见《DKF_SDK_API_20230404(外部修正版)》第3.4.2节中DigitalKeyData
签署保密协议后,可查看上述文档。
- 从车端Reader发起开通
将小米手机放置在开通数字车钥匙的控制台上,手机中弹出提示弹窗,按照步骤操作即可,具体支持情况以车厂为主
3.1.2、配对流程
配对流程指设备与车端进行认证以及数据交换的过程,具体内容可以参考CCC数字车钥匙规范
3.1.3、云端注册钥匙
当配对流程完成后,设备会发送钥匙注册请求,通过Track Key API将钥匙数据注册到车服务器
代码示例
@PostMapping("/ccc/v1/trackKey")
public Map<String, Object> trackKey(@RequestHeader("x-device-oemId") String deviceOemId, @RequestBody Map<String, Object> param) {
// 加密证书链
List<String> encryptionCertChain = (List<String>) param.get("encryptionCertChain");
String encryptionVersion = String.valueOf(param.get("encryptionVersion"));
// 车主钥匙信息
String keyId = String.valueOf(param.get("keyID"));
String keyType = String.valueOf(param.get("keyType"));
String deviceType = String.valueOf(param.get("deviceType"));
String accountIdHash = String.valueOf(param.get("accountIdHash"));
// 车主钥匙数据
Map<String, Object> keyData = (Map<String, Object>) param.get("keyData");
String version = String.valueOf(keyData.get("version"));
String ephemeralPublicKey = String.valueOf(keyData.get("ephemeralPublicKey"));
String data = String.valueOf(keyData.get("data"));
// 解密keyData
byte[] keyDataBytes = ECIES.decrypt(version, Hex.decodeHex(ephemeralPublicKey.toCharArray()), Base64.getDecoder().decode(data));
// 验证钥匙信息
verifyKeyData(keyDataBytes);
// 记录钥匙信息
recordKeyToKts(deviceOemId, keyId, keyType, deviceType, accountIdHash, keyDataBytes);
// 加密uiBundle
String uiBundleData = getUiBundleData(keyId);
Map<String, Object> encryptedUiBundle = ECIES.encrypt(uiBundleData, encryptionVersion, encryptionCertChain);
// 加密vehicleMobilizationData
String vehicleMobilizationData = getVehicleMobilizationData(keyId);
Map<String, Object> encryptedVehicleMobilizationData = ECIES.encrypt(vehicleMobilizationData);
// 返回数据
Map<String, Object> responseHeader = new HashMap<>();
responseHeader.put("statusCode", "200");
Map<String, Object> response = new HashMap<>();
response.put("responseHeader", responseHeader);
response.put("uiBundle", encryptedUiBundle);
response.put("vehicleMobilizationData", encryptedVehicleMobilizationData);
response.put("brand", "abc");
response.put("model", "xyz");
return response;
}
3.1.4、钥匙激活
对于车主侧钥匙处于未激活状态,如何进行激活,需要使用钥匙进行引擎激活
3.2、钥匙查询
从小米服务获取已经开通所有钥匙以及钥匙信息

1、获取车厂已经开通的全部钥匙ID
2、通过钥匙ID获取钥匙的详细信息
3.3、钥匙删除
1、从车App发起删除钥匙流程
调用terminateDigitalKey接口

钥匙删除成功后,设备会将删除凭证发送至车服务器,开发者需要实现Manage Key API,校验钥匙的删除凭证并通知车端删除该钥匙
代码示例
@PostMapping("/ccc/v1/manageKey")
public Map<String, Object> manageKey(@RequestHeader("x-device-oemId") String deviceOemId, @RequestBody Map<String, Object> param) {
String keyId = String.valueOf(param.get("keyID"));
String action = String.valueOf(param.get("action"));
String terminationAttestation = String.valueOf(param.get("terminationAttestation"));
String vehicleOEMProprietaryData = String.valueOf(param.get("vehicleOEMProprietaryData"));
ManageKeyAction manageKeyAction = ManageKeyAction.valueOf(action);
// 本地删除钥匙
if (ManageKeyAction.TERMINATE == manageKeyAction && StringUtils.isNotEmpty(terminationAttestation)) {
// 验证钥匙删除凭证
verifyTerminationAttestation(keyId, terminationAttestation);
// 标记钥匙状态
updateKeyStatus(keyId, DigitalKeyStatus.TERMINATED);
// 通知车端删除钥匙
deleteKeyInVehicle(keyId);
}
// 返回数据
Map<String, Object> responseHeader = new HashMap<>();
responseHeader.put("statusCode", "200");
Map<String, Object> response = new HashMap<>();
response.put("responseHeader", responseHeader);
return response;
}
从钱包侧发起删除钥匙流程
流程同车App发起删除,仅发起路径不同
从车/车服务器发起的删除钥匙流程
此流程是通过云端接口远程删除设备上的车钥匙,车服务器需要分别调用小米服务器的Event Notification API和
Manage Key API进行删除请求
代码示例
public void keyTerminationEventNotification(String keyId, String keyValidFrom, String keyValidTo) {
// 请求头
HttpHeaders header = new HttpHeaders();
header.add("x-requestId", UUID.randomUUID().toString());
header.add("x-timestamp", String.valueOf(System.currentTimeMillis()));
header.add("x-vehicle-oemId", "MOCK");
// 通知钥匙删除中
Map<String, Object> eventData = new HashMap<>();
eventData.put("status", 15);
eventData.put("keyValidTo", keyValidTo);
eventData.put("keyValidFrom", keyValidFrom);
eventData.put("reason", "Key is being terminated");
// 请求参数
Map<String, Object> param = new HashMap<>();
param.put("keyID", keyId);
param.put("eventType", "IN_TERMINATION");
param.put("eventData", eventData);
HttpEntity httpEntity = new HttpEntity(param, header);
ResponseEntity<Object> res = restTemplate.postForEntity(eventNotificationApi, httpEntity, Object.class);
logger.info("api response: {}", res.toString());
}
public void remoteTerminateKey(String keyId, String serverRemoteTerminationRequest) {
// 请求头
HttpHeaders header = new HttpHeaders();
header.add("x-requestId", UUID.randomUUID().toString());
header.add("x-timestamp", String.valueOf(System.currentTimeMillis()));
header.add("x-vehicle-oemId", "MOCK");
// 远程删除请求
Map<String, Object> param = new HashMap<>();
param.put("keyID", keyId);
param.put("action", "TERMINATE");
param.put("serverRemoteTerminationRequest", serverRemoteTerminationRequest);
HttpEntity httpEntity = new HttpEntity(param, header);
ResponseEntity<Object> res = restTemplate.postForEntity(manageKeyApi, httpEntity, Object.class);
logger.info("api response: {}", res.toString());
}
3.4、刷卡使用
具体内容可以参考CCC数字车钥匙规范
3.5、钥匙分享
1、调用startDigitalKeySharing接口调起钱包页面,进行钥匙分享

2、钥匙分享的过程是在车主设备、relay server、好友设备这三方之间完成的,此过程不经过车服务器,具体内容可以参数CCC数字车钥匙规范
3、好友设备接收钥匙成功后,设备会发送分享钥匙的注册请求,通过Track Key API将钥匙数据注册到车服务器,此过程与车主注册钥匙类似,请参考云端注册钥匙的示例代码
4、激活分享钥匙以车厂设定的激活方式为准,如果车厂不需要,钥匙默认为激活状态
5、分享钥匙激活成功后,车服务器还需要调用车主设备服务器的Event Notification API将分享成功事件通知车主,这样车主设备也能显示分享的钥匙信息了
代码示例
public void sharedKeyAddEventNotification(String ownerKeyId, String keyValidFrom, String keyValidTo, List<SharedKey> sharedKeys) {
// 请求头
HttpHeaders header = new HttpHeaders();
header.add("x-requestId", UUID.randomUUID().toString());
header.add("x-timestamp", String.valueOf(System.currentTimeMillis()));
header.add("x-vehicle-oemId", "MOCK");
// 组装分享钥匙列表
List<Map<String, Object>> sharedKeysData = new ArrayList<>();
for (SharedKey sharedKey : sharedKeys) {
Map<String, Object> sharedKeyData = new HashMap<>();
sharedKeyData.put("keyID", sharedKey.getKeyId());
Map<String, Object> keyConfiguration = new HashMap<>();
keyConfiguration.put("friendlyName", sharedKey.getFriendlyName());
keyConfiguration.put("rights", sharedKey.getRight());
keyConfiguration.put("keyValidTo", sharedKey.getValidTo());
keyConfiguration.put("keyValidFrom", sharedKey.getValidFrom());
sharedKeyData.put("keyConfiguration", keyConfiguration);
sharedKeyData.put("status", sharedKey.getStatus());
sharedKeyData.put("groupIdentifier", sharedKey.getGroupIdentifier);
sharedKeyData.put("deviceType", sharedKey.getDeviceType());
sharedKeysData.add(sharedKeyData);
}
// 事件通知数据
Map<String, Object> eventNotificationData = new HashMap<>();
eventNotificationData.put("keyID", ownerKeyId);
eventNotificationData.put("version", "1");
eventNotificationData.put("sharedKeysData", sharedKeysData);
// 加密事件通知数据
Map<String, Object> encryptedData = ECIES.encrypt(eventNotificationData);
// 通知分享钥匙添加成功
Map<String, Object> eventData = new HashMap<>();
eventData.put("status", 1);
eventData.put("sharedKeys", sharedKeys.size());
eventData.put("shareableKeys", MAX_SHARE_KEY_NUMBER - sharedKeys.size());
eventData.put("keyValidTo", keyValidTo);
eventData.put("keyValidFrom", keyValidFrom);
eventData.put("reason", "Shared key is added");
eventData.put("encryptedData", encryptedData);
// 请求参数
Map<String, Object> param = new HashMap<>();
param.put("keyID", ownerKeyId);
param.put("eventType", "SHARED_KEY_ADDED");
param.put("eventData", eventData);
HttpEntity httpEntity = new HttpEntity(param, header);
ResponseEntity<Object> res = restTemplate.postForEntity(eventNotificationApi, httpEntity, Object.class);
logger.info("api response: {}", res.toString());
}
3.6、远程车控(RKE)

1、钱包侧可配置基常用RKE功能键,需提供信息如下信息:
functionID、actionID、以及功能描述
2、车厂App可自行实现远程车控逻辑
- 注册钥匙事件监听,registerDigitalKeyEventCallback
- 注册远程车控事件监听,registerRkeCallback
- 请求车端车控当前状态,requestRkeStatus
- 请求指定车控状态,requestRkeAction
关于RKE订阅待定
4、其他要求
NFC读卡器要求
车端NFC读卡器应符合CCC规范中要求的NFC-A的要求。
车端读卡器应不校验SAK、UID等参数,通过SELECT AID命令来选择车钥匙应用。如果SELECT AID命令返回9000,表明手机支持车钥匙应用。 验证方法如下:
1、在测试手机上开通一把车钥匙, 一张公交卡, 和一张空白门卡(或模拟一张门卡)
2、双击电源键,右上角设置菜单, 设置 - 快捷卡片设置 - 快捷选卡 关闭;双击电源键,手动选中公交卡, 测试车钥匙交易正常, 熄屏, 再次测试车钥匙交易正常; 双击电源键,手动选中门卡, 测试车钥匙交易正常,熄屏,再次测试车钥匙交易正常,关机,再次测试车钥匙交易正常。
3、双击电源键,右上角设置菜单, 设置 - 快捷卡片设置 - 快捷选卡 开启,选中门卡为默认卡片;熄屏, 测试车钥匙交易正常,关机,再次测试车钥匙交易正常。
无线充WPC和NFC刷卡冲突问题
对于车端WPC和NFC二合一设备,建议采用以下策略避免误唤醒手机刷卡页面:
1、根据车辆状态,设定NFC Polling启用和关闭,当用户需要二次刷卡点火或车钥匙配对场景下,打开NFC Polling,其他场景保持NFC关闭。
2、车主钥匙配对过程中,无线充应保持关闭。
常见问题说明
联调过程常见问题说明
1、车厂App如何判断当前设备是否具备开通CCC车钥匙能力?
- 先调用接口isSupportedByDevice(判断是否为True) 。这样可以避免在某些过低版本上直接调用DKF APIs造成SDK Crash。isSupportedByDevice判断了DKF和小米智能卡的最低版本
- 之后,调用 isCreateDigitalKeyPossible(是否返回DIGITAL_KEY_CREATION_POSSIBLE), 调用 getWirelessCapabilities来判断手机是否支持NFC/BLE/UWB, 这2个API 无顺序要求。
- 以上判断完成后,就可以调用createDigitalKey发起配对流程。
- 钱包配对流程启动后,会判断当前设备是否开放了机型白名单机制,通过onActivityResult中resultCode回调对应执行结果, 如果当前设备没有开放, 返回配对错误码是 DIGITAL_KEY_VEHICLE_OEM_NOT_SUPPORTED。
2、关于分享时applink问题,如果在安装版本正确时,出现无法打开原生App问题,如何排查?
- 首先确认Applink是否通过验证,在终端中输入adb shell pm get-app-links com.miui.tsmclient,确认相关域名是否为verify
- 如果不是,在终端执行adb shell pm verify-app-links --re-verify com.miui.tsmclient重新确认一下,或者进入设置-应用设置-应用管理-搜索小米智能卡结束进程,双击电源后,重新使用1命令查看
三、测试验收
1、测试/验收流程

2、验收通过标准
用例通过率:
- P4+P5用例通过率=100%(必过)
- P3测试用例通过率≥90% (必过)
feature完成度:
- 无B/C类问题遗留(必过)
- Major 问题解决率 ≥90%,未解决的 Major 问题需经过产品、研发确认(参考)
- 多语言通过标准: 中英翻译适配完成(必过),全部翻译适配完成(参考)
发现问题解决率
遗留 DI ≤10(必过)(DI根据bug级别来计算:DI= 致命Blocker级别的问题个数*10+严重Critical级别的问题个数*3+一般Major级别的问题个数*1+提示Minor级别的问题个数*0.1)
测试报告模板参考文档附件
四、生产上线
生产上线需进行相关配置,请发送邮件至did-carkey@xiaomi.com进行上线申请。
邮件内容:
1、提供测试报告及申请生产上线说明
2、提供品牌名称、品牌logo、卡面等素材(若在测试环境已提供则不用重复提供)
3、生产证书
4、提供生产环境测试白名单小米账号
5、提供车企App包名及签名
1、生产证书交换
与测试证书互签流程相同
2、服务端生产配置
业务后台相关配置
3、客户端生产配置
客户端访问白名单相关配置
上线流程

五、新机上线
新机需由小米员工陪同现场测试或者发布后邮寄给车厂进行测试
适配流程同8
六、存量机适配
- 车厂适配流程:车厂测试,小米邮件验收
