# 数字车钥匙接口

#### 预置模板

卡片模板的创建是接入流程的第一步，这一步您将会通过http/https请求的方式向华为钱包云服务提供卡券样式的关键信息，如卡面主标题、副标题、logo、背景图片等；用于华为钱包钥匙页面的展示。

您可以创建多个模板，它们有着相同的机构名和服务号，而模板ID不同；当您申请车钥匙时，每一张卡必须和唯一的模板ID关联；即一个模板可供多张钥匙使用，而一张钥匙只能使用一个模板ID。

#### 接口原型

*   **承载协议**：HTTPS POST
    
*   **接口方向**：三方业务管理服务->钱包云服务
    
*   **接口URL**：
    
*   **数据格式**：
    
    请求消息：Content-Type: application/json;charset=UTF-8
    
    响应消息：Content-Type: application/json;charset=UTF-8
    

#### 请求参数

**Request Header**

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| Content-Type | 是 | String | 取值为：Content-Type: application/json;charset=UTF-8 |
| Authorization | 是 | String | 将获取AccessToken获取到的“access_token”的值拼接在字符串“Bearer”之后，以空格符相隔，组成“Authorization”参数的值 |
| Accept | 是 | String | 取值为：Content-Type: application/json;charset=UTF-8 |

**Request Body**

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| passTypeIdentifier | 是 | String | hwpass.stdcarkey.xxx.xxx（xxx可为公司/产品名称，总长度不超过32个英文小写字符，请严格按照此规则定义) 创建Wallet Kit服务时注册的服务号，需要开发者到华为AGC网站申请。 |
| passStyleIdentifier | 是 | String | 自定义，模板ID，即modelId。在同一个appId下唯一。长度不超过64个字符，只能是字母、数字和（.）、（-）、（_）。 |
| organizationName | 是 | String | 自定义，商户名称，最长64个字节，无具体格式要求，中英文均可。 |
| passVersion | 是 | String | 版本号，固定10.0 钱包对象版本，开发者可以依据此确定钱包对象的版本信息 |
| fields | 是 | fields | 自定义 卡券展示信息：appendFields和commonFields |

| appendFields参数 | value | label | 是否必须 | 描述 |
| --- | --- | --- | --- | --- |
| isCreateWhiteCard | true | NFCCardFlag | 是 | 用于表明是否是NFC卡的flag |

| commonFields参数 | value | label | 是否必须 | 描述 |
| --- | --- | --- | --- | --- |
| logo |  | / | 是 | 卡面logo 128*128px，大小<=20kb，直角图片，无需切圆角 |
| backgroundImage |  | / | 是 | 卡面背景 1312*820px，直角图片，无需切圆角 |
| picUrl |  | / | 是 | 带logo的卡面背景 1312*820px，直角图片，无需切圆角 |
| merchantName | 车厂定义 | 卡面主标题 | 是 | 卡面主标题 小于256字节 |
| name | 车厂定义 | 卡面副标题 | 是 | 卡面副标题 小于256字节 |

#### 请求示例

```
{
  "passVersion": "10.0",
  "passTypeIdentifier": "Replace with the Service ID you applied on AGC",
  "passStyleIdentifier": "DigitalCarKeyTestModel",
  "organizationName": "Replace with your organization name",
  "fields": {
    "appendFields": [
      {
        "label": "NFCCardFlag",
        "value": "true",
        "key": "isCreateWhiteCard"
      }
    ],
    "commonFields": [
      {
        "label": "卡面主标题",
        "value": "我的车",
        "key": "merchantName"
      },
      {
        "label": "卡面副标题",
        "value": "XXX车钥匙",
        "key": "name"
      },
      {
        "label": "",
        "value": "",
        "key": "logo"
      },
      {
        "label": "",
        "value": "",
        "key": "backgroundImage"
      },
      {
        "label": "",
        "value": "",
        "key": "picUrl"
      }
    ]
  }
}
```

#### 响应参数

模板预置成功，即http响应为200时，钱包云服务会将DK业务管理服务请求的数据原样返回，即和上面的请求体中的数据一致；其他错误情况，可见REST API错误码。

#### 调用示例

```
    public void createStdCarKeyModel() {
        JSONObject model = JSONObject.parseObject(ConfigUtil.readFile("StdCarKeyModel.json"));
        HwWalletObjectUtil.validateModel(model);
        String urlSegment = "/v2/key_stdcar/model";
        JSONObject responseModel = serverApiService.postToWalletServer(urlSegment, JSONObject.toJSONString(model));
    }
```

#### 申请ICCE钥匙

车主APP向DK业务管理服务申请开通ICCE车钥匙，DK业务管理服务将车钥匙卡片信息添加至钱包云服务中；其中：车主APP->DK业务管理服务之间的交互由车厂自行实现，本章主要侧重于DK业务管理服务->钱包云服务申请ICCE车钥匙的过程，主要包括：申请钥匙卡片和生成JWE数据。

#### 申请钥匙卡片

DK业务管理服务向华为钱包云服务请求创建车钥匙卡片。

#### 接口原型

*   **承载协议**：HTTPS POST
    
*   **接口方向**：三方业务管理服务->钱包云服务
    
*   **接口URL**：
    
*   **数据格式**：
    
    请求消息：Content-Type: application/json;charset=UTF-8
    
    响应消息：Content-Type: application/json;charset=UTF-8
    

#### 请求参数

**Request Header**

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| Content-Type | 是 | String | 取值为：Content-Type: application/json;charset=UTF-8 |
| Authorization | 是 | String | 将获取AccessToken获取到的“access_token”的值拼接在字符串“Bearer”之后，以空格符相隔，组成“Authorization”参数的值 |
| Accept | 是 | String | Content-Type: application/json;charset=UTF-8 |

**Request Body**

| 参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| passTypeIdentifier | 是 | String | hwpass.stdcarkey.xxx.xxx（xxx可为公司/产品名称，总长度不超过32个英文小写字符，请严格按照此规则定义) 创建Wallet Kit服务时注册的服务号，需要开发者到华为AGC网站申请。 |
| passStyleIdentifier | 是 | String | 自定义，注：示例代码中的默认值仅作说明，请勿直接使用 请使用“预置模板中创建的”模板ID，即modelId。在同一个appId下唯一。长度不超过64个字符，只能是字母、数字和（.）、（-）、（_）。 |
| organizationName | 是 | String | 自定义，注：示例代码中的默认值仅作说明，请勿直接使用 请使用预置模板中创建的商户名称，最长64个字节。 |
| organizationPassId | 是 | String(64) | 自定义，注：示例代码中的默认值仅作说明，请勿直接使用 车钥匙卡片在开发者服务器中的卡号。在同一个appId下唯一。长度16个字节，为保证唯一性，请勿手动输入，建议使用代码随机生成，只能是字母、数字（全大写）。当前和serialNumber保持一致 |
| serialNumber | 是 | String(64) | 自定义，注：示例代码中的默认值仅作说明，请勿直接使用 车钥匙卡片在华为钱包服务器中的卡号，即instanceId。在同一个appId下唯一。长度16个字节，为保证唯一性，请勿手动输入，建议使用代码随机生成，只能是字母、数字（全大写）。当前和organizationPassId保持一致。 |
| fields | 是 | fields | 自定义 卡券展示信息：commonFields、timeList和status |
| linkDevicePass | 是 | linkDevicePass | 自定义linkDevicePass 此参数用于保存车钥匙管理台服务器地址、公钥信息以及是否使能卡券的NFC能力。 |

| timeList参数 | value | label | 是否必须 | 描述 |
| --- | --- | --- | --- | --- |
| linkDevicePassExpireTime | 自定义 | / | 是 | 时间列表 |

| status参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| state | 是 | String | 状态值。取值如下 - active：生效 - inactive：未激活 - completed：已使用 - expired：已过期 |
| effectTime | 是 | String | UTC格式，生效时间 |
| expireTime | 是 | String | UTC格式，失效时间，如果超过此事件，卡券自动按照expired状态处理 |

| commonFields参数 | value | label | 是否必须 | 描述 |
| --- | --- | --- | --- | --- |
| bleServiceUuid | 由车厂定义 | / | 是 | 车厂蓝牙设备的SERVICE_UDID，用于手机发现车端蓝牙模块开启的车钥匙服务；请使用蓝牙标准规范：如0000xxxx-0000-1000-8000-00805f9b34fb；如需自行定义，请联系ICCE确认 |
| ownerPassTypeIdentifier | 固定值 | 主卡服务号 | 是 | hwpass.stdcarkey.std：归属ICCE车钥匙类别 |
| readerMatchValue | 由车厂定义 | readerId | 是 | 车钥匙标识，建议字节：不超过20字节（第一字节：车厂标识，第二字节：品牌/系列标识，后续字节保证在车厂内唯一）, 只能包含0-9，A-F |
| bleTargetPackage | 由车厂定义 | / | 是 | 车厂app的包名，用于钱包在特定场景下拉起车厂app，注：请提供应用安装后的全包名，即应用的applicationId, 如com.huawei.wallet |
| bleTargetActivity | 由车厂定义 | / | 是 | 车厂app的activity，供钱包在特定场景下拉起车厂app；注：全路径名称，不带文件后缀，如com.huawei.xxx.xxx.ShowCarDetailActivity |
| bleTargetService | 由车厂定义 | / | 是 | 车厂app的service，供钱包在特定场景下拉起车厂app，注：全路径名称，不带文件后缀名，如com.huawei.xxx.xxx.BleICCEService |
| bleMacAddress | 由车厂定义 | / | 是 | 车端蓝牙设备的mac地址，用于华为手机扫描车端蓝牙模块、向车端蓝牙发起Gatt连接等。请注意格式合法性，如：01:23:45:67:89:0A；注：字符必须全大写 |
| bleFeature | 固定值 | / | 是 | hwpass.carkey.ble：标识支持蓝牙车钥匙 |
| deviceType | 设备类型 | / | 是 | 当前钥匙开通的设备类型，如手机：Phone，穿戴：Wear |
| keyHolderType | 钥匙持有者性质 | / | 是 | 当前钥匙持有者的性质，如车主：Owner，分享：Share |
| vehicleId | 由车厂定义 | / | 是 | 车辆vin码，不超过20个字节 |
| personalizedData | 由车厂定义 | / | 否 | 车厂个性化数据，可用于储存车辆标定数据等 |
| logo |  | / | 否 | 卡面logo；128*128px，大小<=20kb，直角图片，无需切圆角 如果此处携带该参数，则会覆盖对应模板中的相应字段数据。 |
| backgroundImage |  | / | 否 | 卡面背景；1312*820px，直角图片，无需切圆角 如果此处携带该参数，则会覆盖对应模板中的相应字段数据。 |
| picUrl |  | / | 否 | 带logo的卡面背景；1312*820px，直角图片，无需切圆角 如果此处携带该参数，则会覆盖对应模板中的相应字段数据。 |
| merchantName | 车厂定义 | 卡面主标题 | 否 | 卡面主标题；小于256字节 如果此处携带该参数，则会覆盖对应模板中的相应字段数据。 |
| name | 车厂定义 | 卡面副标题 | 否 | 卡面副标题；小于256字节 如果此处携带该参数，则会覆盖对应模板中的相应字段数据。 |

| linkDevicePass参数 | 是否必选 | 参数类型 | 描述 |
| --- | --- | --- | --- |
| webServiceURL | 是 | String | DK业务管理服务地址，用于开通激活过程中向DK业务管理服务发起设备认证、获取个人化token以及获取个人化数据。 |
| token | 是 | String | DK业务管理服务自行生成，没有格式要求，用于开通激活过程中钱包向DK业务管理服务进行“设备认证”和“获取个人化数据Token”请求头中携带的Authorization信息。 |
| serialNumber | 是 | String | 请和上述提及的serialNumber保持一致 |
| passVersion | 否 | String | DevicePass卡券包版本，固定10.0 |
| spPublickey | 是 | String | 开发者创建Wallet Kit服务步骤5生成的公钥。用于对DK业务管理服务返回的“Applet个人化数据”进行验签。NFC卡片信息不会上传到钱包云服务器，所以终端设备需要这个参数来进行验签。 |
| nfcType | 是 | String | 固定值"1"，表示开启NFC能力。如果这个值不是"1"，当前个人化实例的NFC能力会被关闭。 |

#### 请求示例

```
{
  "organizationName": "Replace with your organization name",
  "passTypeIdentifier": "Replace with the Service ID you applied on AGC",
  "passStyleIdentifier": "DigitalCarKeyTestModel",
  "organizationPassId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "serialNumber": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "fields": {
    "timeList": [
      {
        "value": "timeListValue",
        "key": "linkDevicePassExpireTime"
      }
    ],
    "status": {
      "state": "active",
      "effectTime": "2020-04-06T00:00:00.111Z",
      "expireTime": "2030-04-06T00:00:00.111Z"
    },
    "commonFields": [
      {
        "value": "xxx.CarActivity",
        "key": "bleTargetActivity"
      },
      {
        "value": "hwpass.carkey.ble",
        "key": "bleFeature"
      },
      {
        "value": "01:23:45:67:89:AB",
        "key": "bleMacAddress"
      },
      {
        "label": "readerId",
        "value": "CAD34B258391C097",
        "key": "readerMatchValue"
      },
      {
        "label": "主卡服务号",
        "value": "hwpass.stdcarkey.std",
        "key": "ownerPassTypeIdentifier"
      },
      {
        "value": "0000xxxx-0000-1000-8000-00805f9b34fb",
        "key": "bleServiceUuid"
      },
      {
        "value": "xxx",
        "key": "bleTargetPackage"
      },
      {
        "value": "xxx",
        "key": "bleTargetService"
      },
      {
        "value": "Phone",
        "key": "deviceType"
      },
      {
        "value": "Owner",
        "key": "keyHolderType"
      },
      {
        "value": "xxx",
        "key": "vehicleId"
      }
    ]
  },
  "linkDevicePass": {
    "webServiceURL": "",
    "nfcType": "1",
    "serialNumber": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "passVersion": "10.0",
    "spPublickey": "xxx",
    "token": "xxx"
  }
}
```

#### 响应参数

返回结果中会携带预置模板中的信息一并返回。

#### 调用示例

完整的调用示例，请参见钱包服务-服务端卡片开通示例代码。

```
    public void addStdCarKeyInstance() {
        JSONObject instance = JSONObject.parseObject(ConfigUtil.readFile("StdCarKeyInstance.json"));
        HwWalletObjectUtil.validateInstance(instance);
        String urlSegment = "/v2/key_stdcar/instance";
        JSONObject responseInstance =
            serverApiService.postToWalletServer(urlSegment, JSONObject.toJSONString(instance));

        if (responseInstance.containsKey("serialNumber")) {
            String serialNumber = responseInstance.getString("serialNumber");
            JweTest test = new JweTest();
            test.generateThinJWEToBindUser(serialNumber);
        }
    }
```

#### 生成JWE数据

华为钱包车钥匙卡片的开通是基于JWE（JSON Web Encryption）方式。因此DK业务管理服务向钱包云服务申请创建车钥匙成功后，基于创建成功的车钥匙serialNumber生成JWE数据，并将其返回给车厂app，如下所示：

JWE串包含JWE Encrypted Key，iv，Ciphertext，signature；可参见如下步骤：

1.  生成一个随机的Content Encryption Key（CEK）。
    
2.  使用RSA-OAEP加密算法，用钱包服务器给的公钥加密CEK，生成JWE Encrypted Key。
    
3.  生成JWE初始化向量。
    
4.  使用AES GCM加密算法对明文部分进行加密生成密文Ciphertext，算法会随之生成一128位的认证标记Authentication Tag。对以上部分分别进行base64编码。
    
5.  Signature是使用开发者创建Wallet Kit服务步骤5生成的私钥对以上部分进行的签名。
    

#### 调用示例

完整的调用示例，请参见钱包服务-服务端卡片开通示例代码。

```
     public static String generateJwe(String jwePrivateKey, String payload) {
        Map<String, String> jweHeader = getHeader();
        String jweHeaderEncode = getEncodeHeader(jweHeader);
        String sessionKey = generateSecureRandomFactor(16);
        String sessionKeyPublicKey = "MIIBojA****"; // 填入真实的公钥值
        String encryptedKeyEncode = getEncryptedKey(sessionKey, sessionKeyPublicKey);
        byte[] iv = AesUtil.getIvByte(12);
        String ivHexStr = new String(Hex.encodeHex(iv, false));
        String ivEncode = Base64.encodeBase64URLSafeString(ivHexStr.getBytes(StandardCharsets.UTF_8));
        String cipherTextEncode = getCipherText(payload, sessionKey, iv);
        String signature = getSignature(jwePrivateKey, sessionKey, payload, jweHeaderEncode, ivEncode);
        StringBuilder stringBuilder = new StringBuilder().append(jweHeaderEncode)
            .append(".")
            .append(encryptedKeyEncode)
            .append(".")
            .append(ivEncode)
            .append(".")
            .append(cipherTextEncode)
            .append(".")
            .append(signature);
        return stringBuilder.toString();
    }
```