販売者向け支払いデータの暗号方式

Google Pay API は、お支払い方法を署名付きの暗号化された PaymentMethodToken ペイロードに格納して返します。返されるお支払い方法は、PAN からなるカード、またはデバイスの PAN とクリプトグラムからなるトークン化されたカードのどちらかです。

このペイロードには protocolVersion というフィールドがあり、使用されている暗号プリミティブと適切な形式をペイロードの受信者に知らせます。

このガイドでは、Google の署名付きの暗号化された支払い方法トークンをリクエストするために公開鍵を生成する方法と、トークンを検証して復号する詳細な手順について説明します。

このガイドは、protocolVersion = ECv2 にのみ適用されます。

支払いカード情報を直接受信することになるため、作成するアプリが PCI DSS に準拠していること、ユーザーの支払い認証情報を安全に処理するために必要なインフラストラクチャがサーバーに存在することをあらかじめ確認してください。

Google Pay API の ECv2 PaymentMethodToken ペイロードを消費するためにインテグレータが行わなければならない大まかな手順は次のとおりです。

  1. Google ルート署名鍵をフェッチします。
  2. 有効期限内のいずれかのルート署名鍵によって中間署名鍵の署名が有効であることを確認します。
  3. ペイロードの中間署名鍵が期限切れでないことを確認します。
  4. 中間署名鍵によってペイロードの署名が有効であることを確認します。
  5. 署名を検証した後、ペイロードの内容を復号します。
  6. メッセージが期限切れでないことを確認します。そのためには、復号された内容に含まれる messageExpiration フィールドが現在の時刻より後であることを確認する必要があります。
  7. 復号された内容に含まれるお支払い方法を使用して請求します。

Tink ライブラリのサンプルコードは、上記のステップ 1~6 に対応しています。

支払い方法トークンの構造

Google から PaymentData レスポンスで返されるメッセージは UTF-8 エンコード形式のシリアル化された JSON オブジェクトで、次の表に示すキーが含まれます。

名前 説明
protocolVersion 文字列 メッセージ作成の基となる暗号化または署名スキームを示します。必要に応じて、プロトコルの機能を徐々に向上させることができます。
signature 文字列 メッセージの送信元が Google かどうかを確認します。これは Base64 でエンコードされ、中間署名鍵によって ECDSA で作成されています。
intermediateSigningKey オブジェクト Google からの中間署名鍵を含む JSON オブジェクト。これには、keyValuekeyExpiration を含む signedKey と、signatures が含まれます。中間署名鍵の署名検証プロセスが簡単になるようにシリアル化されています。
signedMessage 文字列 encryptedMessageephemeralPublicKeytag を含む HTML セーフ文字列としてシリアル化された JSON オブジェクト。署名検証プロセスが簡単になるようにシリアル化されています。

支払い方法トークンの JSON 形式でのレスポンスの例を次に示します。

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

中間署名鍵

intermediateSigningKey は UTF-8 エンコード形式のシリアル化された JSON オブジェクトで、次の値が含まれます。

名前 説明
signedKey 文字列 この鍵の支払いに関する記述を含む Base64 でエンコードされたメッセージ。
signatures 文字列 中間署名鍵の送信元が Google かどうかを確認します。これは Base64 でエンコードされ、ECDSA を使用して作成されています。

署名付き鍵

signedKey は UTF-8 エンコード形式のシリアル化された JSON オブジェクトで、次の値が含まれます。

名前 説明
keyValue 文字列 ASN.1 形式でエンコードされた鍵の Base64 バージョン。SubjectPublicKeyInfo は X.509 規格で定義されています。
keyExpiration 文字列 中間鍵の有効期限が切れる日時(エポックからの UTC ミリ秒)。 インテグレータは期限切れの鍵を拒否します。

署名付きメッセージ

signedMessage は UTF-8 エンコード形式のシリアル化された JSON オブジェクトで、次の値が含まれます。

名前 説明
encryptedMessage 文字列 支払い情報と追加のセキュリティ フィールドを含む Base64 でエンコードされた暗号化メッセージ。
ephemeralPublicKey 文字列 非圧縮ポイント形式でメッセージを暗号化するために秘密鍵に関連付けられた、Base64 でエンコードされた一時的な公開鍵。詳しくは、暗号化公開鍵の形式をご覧ください。
tag 文字列 encryptedMessage の Base64 でエンコードされた MAC。

暗号化メッセージ

復号した encryptedMessage は、UTF-8 でエンコードしてシリアル化された JSON オブジェクトです。この JSON には 2 つのレベルがあります。外側のレベルにはセキュリティのためのメタデータとフィールドが含まれ、内側のレベルは実際の支払い認証情報を表すもう 1 つの JSON オブジェクトです。

encryptedMessage について詳しくは、次の表と JSON オブジェクトの例をご覧ください。

名前 説明
gatewayMerchantId 文字列

一意の販売者 ID。決済代行業者はこの ID を使用して、これがリクエストを発行した販売者宛てのメッセージであることを確認します。決済代行業者によって作成され、Android またはウェブPaymentMethodTokenizationSpecification として販売者から Google に渡されます。

messageExpiration 文字列 メッセージの有効期限が切れる日時(エポックからの UTC ミリ秒)。 インテグレータは期限切れのメッセージを拒否する必要があります。
messageId 文字列 後でメッセージの取り消しや検索を行う場合に使用する、メッセージを識別するための一意の ID。
paymentMethod 文字列 支払い認証情報のタイプ。現時点でサポートされているのは CARD のみです。
paymentMethodDetails オブジェクト 支払い認証情報。このオブジェクトの形式は paymentMethod によって決まります。これについては、次の表で説明します。

カード

CARD 支払い方法の支払い認証情報は、次のプロパティから構成されます。

名前 説明
pan 文字列 請求先のカード会員番号。この文字列には数字だけが含まれます。
expirationMonth 数字 カードの有効期限の月(1 = 1 月、2 = 2 月など)。
expirationYear 数字 カードの有効期限の 4 桁の年(2020 など)。
authMethod 文字列 カード取引の認証方法。
assuranceDetails AssuranceDetailsSpecifications このオブジェクトは、返された支払い認証情報で行われた検証に関する情報を提供します。

PAN_ONLY

次の JSON スニペットは、CARD paymentMethod の完全な encryptedMessage の例です。PAN_ONLY authMethod を使用しています。

  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "PAN_ONLY",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2020
  },  "gatewayMerchantId": "some-merchant-id",  "messageId": "some-message-id",
  "messageExpiration": "1577862000000"
}

CRYPTOGRAM_3DS

3D セキュア クリプトグラム(CRYPTOGRAM_3DS authMethod)を使用して認証される CARD には、次の追加フィールドが含まれます。

名前 説明
cryptogram 文字列 3D セキュア クリプトグラム。
eciIndicator 文字列 常に存在するとは限りません。Visa のカード ネットワークのトークンの場合にのみ返されます。この値は支払いカードの信用照会リクエストで渡されます。

次の JSON スニペットは、CRYPTOGRAM_3DS authMethod が指定された CARD paymentMethod の完全な encryptedMessage の例です。

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2020,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator"
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1577862000000"
}

署名の検証

署名(中間鍵の署名とメッセージの署名を含む)を検証するには次のものが必要です。

  • 署名の作成に使用されたアルゴリズム
  • 署名の作成に使用されたバイト文字列
  • 署名の作成に使用された秘密鍵に対応する公開鍵
  • 署名

署名アルゴリズム

Google は楕円曲線デジタル署名アルゴリズム(ECDSA)を利用してメッセージに署名します。使用するパラメータは、ECDSA の NIST P-256 とハッシュ関数の SHA-256(FIPS 186-4 で定義)です。

署名

署名はメッセージの最も外側のレベルに含まれ、ASN.1 バイト形式を使用して Base64 でエンコードされています。ASN.1 の詳細については、IETF ツールの付録 A をご覧ください。署名は ECDSA の整数 r と s から構成されています。詳しくは、署名生成アルゴリズムをご覧ください。

規定された ASN.1 バイト形式の例を次に示します。これは Java Cryptography Extension(JCE)の ECDSA 実装によって生成される標準形式です。

ECDSA-Sig-Value :: = SEQUENCE {
 r INTEGER,
 s INTEGER
}

中間署名鍵の署名用のバイト文字列を構築する方法

サンプル支払い方法トークン内の中間署名鍵の署名を検証するには、signedStringForIntermediateSigningKeySignature を次のように構築します。

signedStringForIntermediateSigningKeySignature =
length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key

「||」は連結を意味します。各要素(sender_idprotocolVersionsignedKey)は UTF-8 でエンコードされている必要があります。signedKeyintermediateSigningKey.signedKey の文字列である必要があります。各要素のバイト長は 4 バイトです(リトルエンディアン形式)。

この例では、次に示すサンプルの支払い方法トークンを使用します。

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

sender_id は常に Google で、protocol_versionECv2 です。

たとえば sender_idGoogle の場合、signedString は次の例のように表示されます。

signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}

signedStringForIntermediateSigningKeySignature の署名を検証する方法

中間署名鍵の署名用の署名文字列を構築したときは、標準の ECDSA 検証アルゴリズムが使用されます。ECv2 プロトコルの場合は、intermediateSigningKey.signatures 内のすべての署名を反復処理し、keys.json に含まれる期限切れでない Google 署名鍵を使用して各署名を検証する必要があります。少なくとも 1 つの署名の検証が成功した場合は、検証全体が完了したとみなします。後で intermediateSigningKey.signedKey.keyValue を使用して signedStringForMessageSignature を検証します。署名の検証には、独自のコードを実装するよりも既存の暗号ライブラリを使用することを強くおすすめします。

メッセージの署名用のバイト文字列を構築する方法

サンプル支払い方法トークン内の署名を検証するには、signedStringForMessageSignature を次のように構築します。

signedStringForMessageSignature =
length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage

「||」は連結を意味します。各要素(sender_idrecipient_idprotocolVersionsignedMessage)は UTF-8 でエンコードされている必要があります。各要素のバイト長は 4 バイトです(リトルエンディアン形式)。バイト文字列を作成するときは、signedMessage を解析したり変更したりしないでください。たとえば、\u003d= 文字に置き換えたりしないでください。

サンプルの支払い方法トークンの例を次に示します。

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

sender_id は常に Google で、recipient_idmerchant:merchantId です。merchantId は、本番環境へのアクセス権を持つ販売者の Google Pay Business Console に表示される値と一致します。

sender_idGoogle で、recipient_idmerchant:12345 の場合、signedString は次の例のようになります。

signedStringForMessageSignature =
\x06\x00\x00\x00 || Google || \x0e\x00\x00\x00 || merchant:12345 || | \x04\x00\x00\x00 || ECv2 || \xd2\x00\x00\x00 || {"tag":"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\u003d","ephemeralPublicKey":"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\u003d","encryptedMessage":"mKOoXwi8OavZ"}

signedStringForMessageSignature の署名を検証する方法

署名文字列を構築したときは、標準の ECDSA 検証アルゴリズムが使用されます。前のステップで検証された intermediateSigningKey.signedKey.keyValue を使用して、signedMessage を検証します。署名の検証には、独自のコードを実装するよりも既存の暗号ライブラリを使用することを強くおすすめします。

暗号化スキームの仕様

Google では、楕円曲線統合暗号化スキーム(ECIES)を使用して、Google Pay API レスポンスで返される支払い方法トークンを保護します。この暗号化スキームでは、次のパラメータを使用します。

パラメータ 定義
鍵カプセル化方式

ISO 18033-2 で定義されている ECIES-KEM。

  • 楕円曲線: NIST P-256(OpenSSL では prime256v1 とも呼ばれます)。
  • CheckModeOldCofactorModeSingleHashModeCofactorMode は 0 です。
  • ポイント形式は非圧縮です。
鍵導出関数

HMAC ベースの SHA-256(HKDFwithSHA256)。

  • ソルトは指定しないでください。
  • プロトコル バージョンが ECv2 の場合、情報は Google によって ASCII でエンコードされている必要があります。
  • AES256 鍵用に 256 ビットを導出し、HMAC_SHA256 鍵用に別の 256 ビットを導出する必要があります。
対称暗号化アルゴリズム

ISO 18033-2 で定義されている DEM2

暗号化アルゴリズム: AES-256-CTR、ゼロ IV、パディングなし。

MAC アルゴリズム 256 ビットの鍵を使用して鍵導出関数から導出された HMAC_SHA256

暗号化公開鍵の形式

Google のペイロードで返される暗号化公開鍵と ephemeralPublicKey は、非圧縮ポイント形式の鍵を Base64 で表したものです。これは次の 2 つの要素で構成されています。

  • 形式(0x04)を指定する 1 つのマジック ナンバー。
  • 楕円曲線の X 座標と Y 座標を表す 2 つの大きな 32 バイト整数。

この形式について詳しくは、「Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)」(ANSI X9.62、1998 年)をご覧ください。

OpenSSL を使用して公開鍵を生成する

ステップ 1: 秘密鍵を生成する

次の例では、NIST P-256 での使用に適した楕円曲線の秘密鍵を生成して、key.pem に書き込みます。

openssl ecparam -name prime256v1 -genkey -noout -out key.pem

(省略可)秘密鍵と公開鍵を表示する

秘密鍵と公開鍵の両方を表示するには、次のコマンドを使用します。

openssl ec -in key.pem -pubout -text -noout

このコマンドを実行すると、次のような出力が生成されます。

read EC key
Private-Key: (256 bit)
priv:
    08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
    c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
    f5:d7
pub:
    04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
    53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
    b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
    4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
    64:a1:d8:17:66
ASN1 OID: prime256v1

ステップ 2: Base64 でエンコードされた公開鍵を生成する

上記の省略可能なステップの例で生成された秘密鍵と公開鍵は 16 進数でエンコードされています。Base64 でエンコードされた非圧縮ポイント形式の公開鍵を取得するには、次のコマンドを使用します。

openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt

このコマンドを実行すると、次のようなコンテンツの publicKey.txt ファイルが生成されます。これは非圧縮ポイント形式の鍵の Base64 バージョンです。

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

ファイルのコンテンツに余分な空白や改行が含まれないようにしてください。これを確認するには、Linux または MacOS で次のコマンドを実行します。

od -bc publicKey.txt

ステップ 3: Base64 でエンコードされた PKCS #8 形式の秘密鍵を生成する

Tink ライブラリでは、秘密鍵は Base64 でエンコードされた PKCS #8 形式であるとみなされます。最初のステップで生成した秘密鍵からこの形式の秘密鍵を生成するには、次のコマンドを使用します。

openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -

このコマンドを実行すると、次のような出力が生成されます。

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

支払い方法トークンを復号する方法

トークンを復号する手順は次のとおりです。

  1. 秘密鍵と提供された ephemeralPublicKey を使用して、ECIES-KEM を使用する 512 ビット長の共有鍵を導出します。次のパラメータを使用します。
    • 楕円曲線: NIST P-256(OpenSSL では prime256v1 とも呼ばれます)。
    • CheckModeOldCofactorModeSingleHashModeCofactorMode0 です。
    • エンコード関数: 非圧縮ポイント形式。
    • 鍵導出関数: RFC 5869 で規定された HKDFwithSHA256。次のパラメータを使用します。
      • ソルトは指定しないでください。RFC により、これは 32 個のゼロ化されたバイトからなるソルトと同等でなければなりません。
  2. 生成された鍵を 2 つの 256 ビット長の鍵(symmetricEncryptionKeymacKey)に分割します。
  3. tag フィールドが encryptedMessage の有効な MAC であることを確認します。

    適切な MAC を生成するには、HMAC(RFC 5869)とハッシュ関数 SHA256、およびステップ 2 で取得した macKey を使用します。

  4. AES-256-CTR モードを使用して次のとおりに encryptedMessage を復号します。

    • ゼロ IV。
    • パディングなし。
    • ステップ 2 で導出した symmetricEncryptionKey

鍵の管理

販売者の暗号鍵

販売者は、暗号化スキームの仕様に示す仕様に従って公開鍵を生成します。

Google ルート署名鍵

Google の現在有効なルート署名用公開鍵のセットは公開 URL からフェッチできます。これらの鍵は、その URL から返される HTTP キャッシュ ヘッダーに示されている限り有効で、有効期限(keyExpiration フィールドによって決定されます)が切れるまでキャッシュされます。有効期限が切れたときは、公開 URL から有効な鍵の最新リストを再度フェッチすることをおすすめします。

ECv2 プロトコルの例外: 実行時に Google から鍵をフェッチできない場合は、Google の本番環境 URL から keys.json をフェッチしてシステムに保存し、それを定期的に手動で更新してください。通常の状況下では、ECv2 の新しいルート署名鍵は、有効期限が最も長い鍵が期限切れになる 5 年前に発行されます。鍵が危険にさらされた場合は、keys.json を速やかに再読み込みしていただくため、セルフサービス ポータルで指定された連絡先情報を使って Google からすべての販売者にその旨が通知されます。通常のローテーションに確実に追随するため、keys.json 内の Google の鍵を保存することを選んだ販売者は、年 1 回義務付けられている鍵のローテーションの一環として毎年更新することをおすすめします。

公開 URL から提供される鍵は、次の形式でマップされます。

{
  "keys": [
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"2000000000000"
    },
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"3000000000000"
    }
  ]
}

keyValue は、X.509 規格で定義されている ASN.1 型 SubjectPublicKeyInfo でエンコードされた鍵の Base64 バージョン(折り返しなし、パディングなし)です。Java では、この ASN.1 エンコードは X509EncodedKeySpec クラスで表されます。 これは ECPublicKey.getEncoded() を使用して取得できます。

テスト環境と本番環境の URL が次のリンクによって提供されています。

鍵のローテーション

直接統合を使用してサーバー上で直接支払い方法トークンを復号する場合は、毎年鍵をローテーションする必要があります。

暗号鍵をローテーションするには、次の手順を行います。

  1. OpenSSL を使用して、新しい鍵ペアを生成します
  2. Google アカウントでログインして Google Pay Business Console を開きます。ログインには、以前に Google Play でアプリを管理するために使用したアカウントを使用します。
  3. [Google Pay API] タブの [Direct integration] パネルで、既存の公開鍵の横にある [管理] をクリックします。[Add another key] をクリックします。
  4. [Public encryption key] テキスト入力フィールドを選択し、新しく生成した非圧縮ポイント形式の公開鍵を表す Base64 文字列を追加します。
  5. [Save encryption keys] をクリックします。
  6. 鍵のローテーションをシームレスに行えるように、鍵の移行中は新旧どちらの秘密鍵でも復号できるようにします。

    Tink ライブラリを使用してトークンを復号する場合は、次の Java コードを使用して複数の秘密鍵をサポートします。

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    必ずこの復号コードを本番環境に導入し、復号が正常に行われるかどうかを確認してください。

  7. コードで使用される公開鍵を変更します。

    PaymentMethodTokenizationSpecification parameters プロパティの publicKey 属性の値を置き換えます。

    /**
     * @param publicKey public key retrieved from your server
     */
    private static JSONObject getTokenizationSpecification(String publicKey) {
      JSONObject tokenizationSpecification = new JSONObject();
      tokenizationSpecification.put("type", "DIRECT");
      tokenizationSpecification.put(
        "parameters",
        new JSONObject()
            .put("protocolVersion", "ECv2")
            .put("publicKey", publicKey));
      return tokenizationSpecification;
    }
  8. ステップ 4 のコードを本番環境に導入します。コードの導入後は、暗号化および復号トランザクションで新しい鍵ペアが使用されます。
  9. 古い公開鍵がトランザクションの暗号化に使用されていないことを確認します。

  10. 古い秘密鍵を削除します。
  11. Google アカウントでログインして Google Pay Business Console を開きます。ログインには、以前にデベロッパーとして Google Pay に登録したときに使用したアカウントを使用します。
  12. [Google Pay API] タブの [Direct integration] パネルで、既存の公開鍵の横にある [管理] をクリックします。古い公開鍵の横にある [Delete] をクリックし、[Save encryption keys] をクリックします。

Google では、次のような、PaymentMethodTokenizationSpecification parameters オブジェクト内の publicKey プロパティで指定された鍵を使用します。

{
  "protocolVersion": "ECv2",
  "publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}

Tink ライブラリを使用して暗号化されたレスポンスを管理する

署名を検証してメッセージを復号するには Tink 暗号ライブラリを使用します。Tink と統合して検証や復号を行うには、次の手順に従います。

  1. pom.xml 内で、Tink paymentmethodtoken アプリを依存関係として追加します。

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.2.0</version>  <!-- or latest version -->
      </dependency>
    </dependencies>
    
  2. サーバーの起動時に Google 署名鍵をプリフェッチして、鍵をメモリ内に保持します。こうすると、復号処理で鍵をフェッチするときにネットワーク レイテンシが発生しません。

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. 次のコードで鍵を復号します。ここでは paymentMethodTokenencryptedMessage 変数に格納されていることを前提としています。太字の部分はシナリオに合わせて置き換えてください。

    環境テストの場合は、INSTANCE_PRODUCTIONINSTANCE_TEST に、[YOUR MERCHANT ID] に置き換えます。

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .fetchSenderVerifyingKeysWith(
            GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
        .recipientId("merchant:YOUR_MERCHANT_ID")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);
    
  4. PrivateKey1PrivateKey2 を独自の鍵に置き換えます。これらの変数には、Base64 でエンコードされた PKCS8 文字列と ECPrivateKey オブジェクトのどちらかを指定できます。Base64 でエンコードされた PKCS8 秘密鍵を生成する方法について詳しくは、OpenSSL を使用して鍵のペアを生成するをご覧ください。

  5. 鍵を復号するたびに Google サーバーを呼び出すことができない場合は、次のコードで復号します。太字の部分はシナリオに合わせて置き換えてください。

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .addSenderVerifyingKey("ECv2 key fetched from test or production url")
        .recipientId("merchant:YOUR_MERCHANT_ID")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);
    

    本番環境における現在の鍵は、鍵が危険にさらされない限り、通常の状況下で 2038 年 4 月 14 日まで有効です。鍵が危険にさらされた場合は、keys.json を速やかに再読み込みしていただくため、セルフサービス ポータルで指定された連絡先情報を使って Google からすべての販売者にその旨が通知されます。

    次のセキュリティに関する部分は上記のコード スニペットによって処理されるため、デベロッパーはペイロードをどのように消費するかに専念できます。

    • Google 署名鍵のフェッチとメモリへのキャッシュ
    • 署名の検証
    • 復号