メインコンテンツまでスキップ

認証

LINE Blockchain Developers APIは、有効なリクエストなのかどうかを確認するために認証を行います。認証のためのAPI keyを発行する方法は、APIの紹介を参照してください。

サーバーの時間を確認するAPIを除いたすべてのAPIリクエストは、HTTPヘッダーに認証情報を送る必要があります。 サーバーは、与えられた認証情報に基づいて以下のようにAPIリクエストの有効性を判断します。無効なAPIリクエストは処理しません。

  • リクエストのtimestampとサーバーの現在時刻(Unix Epoch time)が±5分以上の差が出るとエラーを返します。
  • 1つのservice-api-keyにおいて11分内に同じnonceを再利用することはできません。成功したリクエストのnonceを11分内に再利用すると、エラーを返します。
  • 1つのsignatureは一度のみ有効です。

以下は、リクエストヘッダーに含まれるべき認証情報です。

HeaderDescription
timestampリクエストが作成された時刻で、UTC基準ミリ秒単位のUnix Epoch時間で表示されます。この値とサーバーとの時間差は5分を超えてはなりません。
nonceアルファベットの大文字・小文字と数字からなる8桁の任意の文字列です。成功したリクエストのnonceは、11分内に再利用できません。
service-api-keyLINE Blockchain Developersコンソールで発行されたサービスのAPI keyです。LINE Blockchain Developersで正常に有効化された値であるべきです。
signatureAPIリクエストをLINE Blockchain Developersコンソールで発行されたサービスのAPI secretで署名した結果です。受信したサーバーで生成した署名と同じであるべきです。署名を生成するを参照してください。

API keyは、LINE Blockchain Developersコンソールのサービスの設定ページで確認できます。

署名の生成

署名は以下のように生成できます。

  1. ナンス(Nonce)、タイムスタンプ、HTTPメソッド、リクエストパス、クエリ文字列、リクエストボディの文字列を順番に付けた文字列を生成します。
  2. 発行されたAPI secretを利用して1の結果をHMAC-SHA512で署名します。
  3. 2の結果をBase64でエンコードします。

署名に使用される情報は以下のとおりです。

  • ナンスとタイムスタンプは、HTTPヘッダーに送信するnoncetimestampと同じです。
  • HTTPメソッドは、APIリクエストのHTTPメソッドを大文字で表記した文字列です。
  • リクエストパスは、API endpointのapi-pathです。
    例えば、リクエストAPI endpointが、https://test-api.blockchain.line.me/v1/wallets/link000000000000000000000000000000000000000?page=2&msgType=MsgSendの場合、リクエストパスは/v1/wallets/link000000000000000000000000000000000000000です。
  • クエリ文字列は、API endpointのクエリパラメータです。 例えば、リクエストAPI endpointがhttps://test-api.blockchain.line.me/v1/wallets/link000000000000000000000000000000000000000?page=2&msgType=MsgSendの場合、クエリ文字列はpage=2&msg=MsgSendです。
  • リクエストボディの文字列は、HTTPリクエストのボディに含まれているkeyとvalueをクエリ文字列のように'key=value&'でつなげたものです。すべてのkeyをアルファベットの昇順に並べ替えてからその順番につなげる必要があります。
  • リクエストボディに配列が入っている場合は、配列keyの下位要素のkeyをピリオド(.)でつなげ、全体のkeyとして使用します。また、配列の各要素で該当するvalueをコンマ(,)でつなげたものを全体のvalueとして使用します。例4を参照してください。
  • リクエストボディに配列が入っており、一部のkeyが配列内でオプションである場合、指定されていないvalueを空の文字列で処理する必要があります。例4を参照してください。

JavaScriptのサンプル

以下のコードは、JavaScriptで署名を生成する方法を示します。

ES6で書いたコードです。以前のバージョンをサポートするにはBabelを使用してください。

import CryptoJs from "crypto-js";
import _ from "lodash";
import RequestBodyFlattener from "./request-body-flattener";

export default class SignatureGenerator {
/*
* path has to include only path such as /v1/item-tokens/{contractId}/non-fungibles, without any query-string
* parameters is query-parameters
* body is request body of POST, PUT method
*/
static signature(apiSecret, method, path, timestamp, nonce, parameters = {}, body = {}) {
let obj = _.assignIn(parameters, body);
function createSignTarget() {
let signTarget = `${nonce}${timestamp}${method}${path}`;
if (obj && _.size(obj) > 0) {
if (signTarget.indexOf('?') < 0) {
signTarget += '?'
} else {
signTarget += '&'
}
}
return signTarget;
}

let signTarget = createSignTarget();
if (obj && _.size(obj) > 0) {
signTarget += RequestBodyFlattener.flatten(obj);
}
let hash = CryptoJs.HmacSHA512(signTarget, apiSecret);
return CryptoJs.enc.Base64.stringify(hash);
}
}

Pythonのサンプル

以下のコードは、Pythonで署名を生成する方法を示します。

import hmac
import hashlib
import base64
import logging
import sys
from sdk.request_flattener import RequestBodyFlattener

class SignatureGenerator:

def __createSignTarget(self, method, path, timestamp, nonce, parameters: dict = {}):
signTarget = f'{nonce}{str(timestamp)}{method}{path}'
if(len(parameters) > 0):
signTarget = signTarget + "?"

return signTarget

def generate(self, secret: str, method: str, path: str, timestamp: int, nonce: str, query_params: dict = {}, body: dict = {}):
body_flattener = RequestBodyFlattener()
all_parameters = {}
all_parameters.update(query_params)
all_parameters.update(body)

self.__logger.debug("query_params: " + str(query_params))

signTarget = self.__createSignTarget(method.upper(), path, timestamp, nonce, all_parameters)

if (len(query_params) > 0):
signTarget += '&'.join('%s=%s' % (key, value) for (key, value) in query_params.items())

if (len(body) > 0):
if (len(query_params) > 0):
signTarget += "&" + body_flattener.flatten(body)
else:
signTarget += body_flattener.flatten(body)

raw_hmac = hmac.new(bytes(secret, 'utf-8'), bytes(signTarget, 'utf-8'), hashlib.sha512)
result = base64.b64encode(raw_hmac.digest()).decode('utf-8')

return result

Kotlinのサンプル

以下のコードは、Kotlinで署名を生成する方法を示します。

import org.apache.commons.codec.binary.Base64
import java.util.TreeMap
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

interface SignatureGenerator {
fun generate(
serviceApiSecret: String,
httpMethod: String,
path: String,
timestamp: Long,
nonce: String,
flatQueryParam: String,
body: Map<String, Any?> = emptyMap(),
): String

fun generate(
serviceApiSecret: String,
httpMethod: String,
path: String,
timestamp: Long,
nonce: String,
queryParam: Map<String, List<String?>> = emptyMap(),
body: Map<String, Any?> = emptyMap(),
): String
}

class DefaultSignatureGenerator(
private val queryParameterFlattener: QueryParameterFlattener,
private val requestBodyFlattener: RequestBodyFlattener,
) : SignatureGenerator {
override fun generate(
serviceApiSecret: String,
httpMethod: String,
path: String,
timestamp: Long,
nonce: String,
queryParam: Map<String, List<String?>>,
body: Map<String, Any?>,
): String {
val flattenQueryParam = queryParameterFlattener.flatten(queryParam)

return generate(
serviceApiSecret,
httpMethod,
path,
timestamp,
nonce,
flattenQueryParam,
body,
)
}

override fun generate(
serviceApiSecret: String,
httpMethod: String,
path: String,
timestamp: Long,
nonce: String,
flatQueryParam: String,
body: Map<String, Any?>,
): String {
val data = signatureTarget(body, nonce, timestamp, httpMethod, path, flatQueryParam)
val rawHmac = rawSignature(serviceApiSecret, data)
return Base64.encodeBase64String(rawHmac)
}

private fun rawSignature(serviceApiSecret: String, data: String): ByteArray? {
val signingKey = SecretKeySpec(serviceApiSecret.toByteArray(), HMAC_512_SECRET_ALGORITHM)
val mac = Mac.getInstance(HMAC_512_SECRET_ALGORITHM)
mac.init(signingKey)
return mac.doFinal(data.toByteArray())
}

private fun signatureTarget(body: Map<String, Any?>, nonce: String, timestamp: Long, httpMethod: String, path: String, flatQueryParam: String): String {
val bodyTreeMap = sortBody(body)
val flattenBody = requestBodyFlattener.flatten(bodyTreeMap)
val stringBuilder = StringBuilder()
stringBuilder.append("$nonce$timestamp$httpMethod$path")
if (flatQueryParam.isNotBlank()) {
if ("?" in flatQueryParam) {
stringBuilder.append(flatQueryParam)
} else {
stringBuilder.append("?").append(flatQueryParam)
}
}
if (body.isNotEmpty()) {
if (!stringBuilder.contains('?')) {
stringBuilder.append("?").append(flattenBody)
} else {
stringBuilder.append("&").append(flattenBody)
}
}

return stringBuilder.toString()
}

private fun sortBody(body: Map<String, Any?>): TreeMap<String, Any?> {
val bodyTreeMap = TreeMap<String, Any?>()
bodyTreeMap.putAll(body)
return bodyTreeMap
}

companion object {
private const val HMAC_512_SECRET_ALGORITHM = "HmacSHA512"
}
}

Goのサンプル

以下のコードは、Go-langで署名を生成する方法を示します。

import (
"fmt"
"strings"
"crypto/hmac"
"crypto/sha512"
"encoding/base64"
)

func __createSignTarget(method string, path string, timestamp int, nonce string, parameters map[string]interface{}) string {
s := fmt.Sprint(timestamp)
signTarget := fmt.Sprintf("%s%s%s%s", nonce, s, method, path)

if len(parameters) > 0 {
signTarget = fmt.Sprintf("%s?", signTarget)
}

return signTarget;
}

func Generate(secret string,
method string,
path string,
timestamp int,
nonce string,
query_params map[string]interface{},
body map[string]interface{}) string {
//
// Generate signature with given arguments.
//
// Args:
// -secret- api-secret
// -method- http method
// -path- api path
// -timestamp- Unix timestamp value
// -nonce- random string with 8 length
// -query_params- query parameters
// -body- request body
//
// Returns:
// -signature- generated signature
//
//

all_parameters := make(map[string]interface{})

for k, v := range query_params {
all_parameters[k] = v
}

for k, v := range body {
all_parameters[k] = v
}

signTarget := __createSignTarget(strings.ToUpper(method), path, timestamp, nonce, all_parameters);

if len(all_parameters) > 0 {
signTarget = fmt.Sprintf("%s%s", signTarget, Flatten(all_parameters))
}

raw_hmac := hmac.New(sha512.New, []byte(secret))
raw_hmac.Write([]byte(signTarget))

result := base64.StdEncoding.EncodeToString(raw_hmac.Sum(nil))

return result
}

PHPのサンプル

以下のコードは、PHPで署名を生成する方法を示します。

<?php

require_once("request_body_flattener.php");

class SignatureGenerator {
// This is to generate signature with flatten request.

function __createSignTarget($method, $path, $timestamp, $nonce, $parameters) {
$s = strval($timestamp);
$signTarget = "{$nonce}{$s}{$method}{$path}";

if (count($parameters) > 0)
$signTarget = $signTarget."?";

return $signTarget;
}

public function generate($secret, $method, $path, $timestamp, $nonce, $query_params = array(), $body = array()) {
//
// Generate signature with given arguments.
//
// Args:
// -secret- api-secret
// -method- http method
// -path- api path
// -timestamp- Unix timestamp value
// -nonce- random string with 8 length
// -query_params- query parameters
// -body- request body
//
// Returns:
// -signature- generated signature
//
//

$body_flattener = new RequestBodyFlattener();
$all_parameters = array_replace($query_params, $body);

$signTarget = $this->__createSignTarget(strtoupper($method), $path, $timestamp, $nonce, $all_parameters);

if (count($all_parameters) > 0)
$signTarget = $signTarget.$body_flattener->flatten($all_parameters);

$raw_hmac = hash_hmac("sha512", $signTarget, $secret, true);
$result = base64_encode($raw_hmac);

return $result;
}
}

?>

署名の例

署名を生成し、それを利用してAPIリクエストを送る方法をいくつかの例で説明します。 例で共通して使用される情報は、以下のとおりです。

  • API Key: 136db0ad-0fe1-456f-96a4-329be3f93036
  • API Secret: 9256bf8a-2b86-42fe-b3e0-d3079d0141fe
  • Timestamp: 1581850266351
  • Nonce: Bp0IqgXE

例1.リクエストパスのみの場合

最初の例は、リクエストパスのみのAPIリクエストです。

  • HTTP method: GET
  • Request path: /v1/wallets

署名に必要な文字列は以下のように生成できます。

Bp0IqgXE1581850266351GET/v1/wallets

echoとOpenSSLを使用してHMAC SHA512署名を生成し、Base64でエンコードします。

echo -n "Bp0IqgXE1581850266351GET/v1/wallets" | openssl dgst -sha512 -binary -hmac "9256bf8a-2b86-42fe-b3e0-d3079d0141fe" | base64

> 2LtyRNI16y/5/RdoTB65sfLkO0OSJ4pCuz2+ar0npkRbk1/dqq1fbt1FZo7fueQl1umKWWlBGu/53KD2cptcCA==

curlを利用して生成した署名をAPIリクエストヘッダーに送信します。

curl -i GET 'https://test-api.blockchain.line.me/v1/wallets' \
--header 'service-api-key: 136db0ad-0fe1-456f-96a4-329be3f93036' \
--header 'nonce: Bp0IqgXE' \
--header 'timestamp: 1581850266351' \
--header 'signature: 2LtyRNI16y/5/RdoTB65sfLkO0OSJ4pCuz2+ar0npkRbk1/dqq1fbt1FZo7fueQl1umKWWlBGu/53KD2cptcCA=='

例2.リクエストパスとクエリパラメータがある場合

例2では、リクエストパスとクエリパラメータがあるAPIリクエストです。

  • HTTP method: GET
  • Request URI: /v1/wallets/tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq/transactions?page=2&msgType=coin/MsgSend
  • Request path: /v1/wallets/tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq/transactions
  • Query string: page=2&msgType=coin/MsgSend

署名に必要な文字列は以下のように生成できます。リクエストパスとクエリ文字列の間に「?」が含まれていることを覚えておいてください。クエリパラメータは順番の変更なしにそのまま入力します。

Bp0IqgXE1581850266351GET/v1/wallets/tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq/transactions?page=2&msgType=coin/MsgSend  

echoとOpenSSLを使用してHMAC SHA512署名を生成し、Base64でエンコードします。

echo -n "Bp0IqgXE1581850266351GET/v1/wallets/tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq/transactions?page=2&msgType=coin/MsgSend" | openssl dgst -sha512 -binary -hmac "9256bf8a-2b86-42fe-b3e0-d3079d0141fe" | base64

> fasfnqKVVClFam+Dov+YN+rUfOo/PMZfgKx8E36YBtPh7gB2C+YJv4Hxl0Ey3g8lGD0ErEGnD0gqAt85iEhklQ==

curlを利用して生成した署名をAPIリクエストヘッダーに送信します。

curl -i GET 'https://test-api.blockchain.line.me/v1/wallets/tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq/transactions?page=2&msgType=coin/MsgSend' \
--header 'service-api-key: 136db0ad-0fe1-456f-96a4-329be3f93036' \
--header 'nonce: Bp0IqgXE' \
--header 'timestamp: 1581850266351' \
--header 'signature: fasfnqKVVClFam+Dov+YN+rUfOo/PMZfgKx8E36YBtPh7gB2C+YJv4Hxl0Ey3g8lGD0ErEGnD0gqAt85iEhklQ=='

例3.リクエストパスとリクエストボディ(配列は除く)がある場合

例3では、リクエストパスとリクエストボディがあるAPIリクエストです。

  • HTTP method: PUT
  • Request path: /v1/item-tokens/61e14383/non-fungibles/10000001/00000001
  • Request body string: json { "ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq", "ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=", "name": "NewName" }

署名に必要な文字列は以下のように生成できます。

Bp0IqgXE1581850266351PUT/v1/item-tokens/61e14383/non-fungibles/10000001/00000001?name=NewName&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=

上記のように、クエリ文字列がなくてもリクエストパスとリクエストボディの間に「?」が含まれます。また、リクエストボディは、keyをアルファベットの昇順に並べ替えてから追加する必要があります。

使用中のプログラミング言語が提供する並べ替え関数を使用すると、簡単に並べ替えることができます。

echoとOpenSSLを使用してHMAC SHA512署名を生成し、Base64でエンコードします。

echo -n "Bp0IqgXE1581850266351PUT/v1/item-tokens/61e14383/non-fungibles/10000001/00000001?name=NewName&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=" | openssl dgst -sha512 -binary -hmac "9256bf8a-2b86-42fe-b3e0-d3079d0141fe" | base64

> 4L5BU0Ml/ejhzTg6Du12BDdElv8zoE7XD/iyOaZ2BHJIJG0SUOuCZWXu0YaF4i4C2CFJhjZoJFsje4CJn/wyyw==

curlを利用して生成した署名をAPIリクエストヘッダーに送信します。

curl -i -X PUT 'https://test-api.blockchain.line.me/v1/item-tokens/61e14383/non-fungibles/10000001/00000001' \
--header 'service-api-key: 136db0ad-0fe1-456f-96a4-329be3f93036' \
--header 'nonce: Bp0IqgXE' \
--header 'timestamp: 1581850266351' \
--header 'signature: 4L5BU0Ml/ejhzTg6Du12BDdElv8zoE7XD/iyOaZ2BHJIJG0SUOuCZWXu0YaF4i4C2CFJhjZoJFsje4CJn/wyyw==' \
--header 'Content-Type: application/json' \
--data-raw '{
"ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq",
"ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=",
"name": "NewName"
}'

例4.リクエストパスとリクエストボディ(配列を含む)がある場合

最後の例は、リクエストパスと配列を含むリクエストボディがあるAPIリクエストです。

  • HTTP method: POST
  • Request path: /v1/item-tokens/61e14383/non-fungibles/multi-mint
  • Request body: json { "ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq", "ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=", "toAddress": "tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp", "mintList": [ { "tokenType": "10000001", "name": "NewNFT" }, { "tokenType": "10000003", "name": "NewNFT2", "meta": "New nft 2 meta information" } ] }

署名に必要な文字列は以下のように生成できます。

Bp0IqgXE1581850266351POST/v1/item-tokens/61e14383/non-fungibles/multi-mint?mintList.meta=,New nft 2 meta information&mintList.name=NewNFT,NewNFT2&mintList.tokenType=10000001,10000003&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=&toAddress=tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp

上記のように、クエリパラメータがなくてもリクエストパスとリクエストボディの間に「?」が含まれます。また、リクエストボディは、keyをアルファベットの昇順に並べ替えてから追加する必要があります。

使用中のプログラミング言語が提供する並べ替え関数を使用すると、簡単に並べ替えることができます。

上記の例で、mintListの最初の要素には選択フィールドであるmetaが存在しません。この場合は、その値を空の文字列に代わり、"mintList.meta=,New nft 2 meta information"のように作ります。もし、2番目の要素にもmetaがない場合は、"mintList.meta"自体を除きます。

echoとOpenSSLを使用してHMAC SHA512署名を生成し、Base64でエンコードします。

echo -n "Bp0IqgXE1581850266351POST/v1/item-tokens/61e14383/non-fungibles/multi-mint?mintList.meta=,New nft 2 meta information&mintList.name=NewNFT,NewNFT2&mintList.tokenType=10000001,10000003&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=&toAddress=tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp" | openssl dgst -sha512 -binary -hmac "9256bf8a-2b86-42fe-b3e0-d3079d0141fe" | base64

> vhr5c3y2PAP5rmt+4YN1ojbMnT9IkYnIIB1yvWYM9OdECB2Y11fGTLDLRybB3lLKv0kvJQMAelSkQYBKdhSXbg==

を利用して生成した署名をAPIリクエストヘッダーに送信します。

curl -i -X POST 'https://test-api.blockchain.line.me/v1/item-tokens/61e14383/non-fungibles/multi-mint' \
--header 'service-api-key: 136db0ad-0fe1-456f-96a4-329be3f93036' \
--header 'nonce: Bp0IqgXE' \
--header 'timestamp: 1581850266351' \
--header 'signature: vhr5c3y2PAP5rmt+4YN1ojbMnT9IkYnIIB1yvWYM9OdECB2Y11fGTLDLRybB3lLKv0kvJQMAelSkQYBKdhSXbg==' \
--header 'Content-Type: application/json' \
--data-raw '{
"ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq",
"ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=",
"toAddress": "tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp",
"mintList": [
{
"tokenType": "10000001",
"name": "NewNFT"
},
{
"tokenType": "10000003",
"name": "NewNFT2",
"meta": "New nft 2 meta information"
}
]
}'

もし、以下のようにmintListmetaを使用していないリクエストボディの場合はどうなるでしょうか?

{    
"ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq",
"ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=",
"toAddress": "tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp",
"mintList": [
{
"tokenType": "10000001",
"name": "NewNFT"
},
{
"tokenType": "10000003",
"name": "NewNFT2"
}
]
}

この場合は、以下のように署名する文字列からmintList.meta keyを除きます。

Bp0IqgXE1581850266351POST/v1/item-tokens/61e14383/non-fungibles/multi-mint?mintList.name=NewNFT,NewNFT2&mintList.tokenType=10000001,10000003&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=&toAddress=tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp

今度は、mintListの選択フィールドであるmetaにnullを入力した状況を考えてみましょう。

{    
"ownerAddress": "tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq",
"ownerSecret": "uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=",
"toAddress": "tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp",
"mintList": [
{
"tokenType": "10000001",
"name": "NewNFT"
},
{
"tokenType": "10000003",
"name": "NewNFT2",
"meta": null
}
]
}

この場合は、以下のようにnull値を除いた文字列を署名して使用します。

Bp0IqgXE1581850266351POST/v1/item-tokens/61e14383/non-fungibles/multi-mint?mintList.name=NewNFT,NewNFT2&mintList.tokenType=10000001,10000003&ownerAddress=tlink1fr9mpexk5yq3hu6jc0npajfsa0x7tl427fuveq&ownerSecret=uhbdnNvIqQFnnIFDDG8EuVxtqkwsLtDR/owKInQIYmo=&toAddress=tlink18zxqds28mmg8mwduk32csx5xt6urw93ycf8jwp