開発者コンソール

SSIトークンライブラリ

SSIトークンライブラリ

SSIトークンライブラリは、リンクとSSIトークンを生成および検証するためのユーティリティクラスを含むJavaライブラリです。このライブラリを使用すると、次のアクションを実行できます。

  • アカウントリンクの設定中にAmazonに発行するリンクトークンを生成する。
  • アプリへの新規サインインリクエスト時に、SSIトークンを検証してデコードする。
  • アプリへの新規サインインリクエスト時にリンクトークンを検証してデコードする。SSIトークンのデコードにより、リンクトークンが抽出される。
  • SSIトークンを生成し、内部テストでのAmazon SSIサーバーの動作を模倣する。

プロジェクトでSSIトークンライブラリを使用するには、SimpleSignInTokenCommon-<x.y>.jarというJARファイルを追加します。このJARはここでダウンロードするか、Appstore SDKに含まれているSSIサンプルアプリのlibsフォルダから取得できます。このJARファイルはプロジェクトの依存関係になります。Mavenの直接的および遷移的な依存関係については、SSIサンプルアプリのbuild.gradleファイルを参照してください。

以下のセクションでは、上記のアクションでのライブラリの使用方法と、サンプルコードのリファレンスについて説明します。Javadoc形式の個々のクラスとメソッドの詳細については、SSIトークンライブラリAPIリファレンスを参照してください。

LinkTokenV1Providerクラスは、リンクトークンを生成するためのAPIを提供します。LinkTokenV1Providerのインスタンス化に使用されるコンストラクターは、2つの入力パラメーターを受け取ります。ILinkTokenCryptoKeyProviderインターフェイスとIAppStorePublicKeyProviderインターフェイスの実装インスタンスです。これらの実装には、キーストアから暗号化キーを取得するロジックが含まれています。

暗号化キーとパブリックキーペアを取得するロジックは、ILinkTokenCryptoKeyProviderインターフェイスおよびIAppStorePublicKeyProviderインターフェイス内にあります。次のコードを使用して、リンクトークンの生成に使用するLinkTokenV1Providerオブジェクトを作成します。

LinkTokenV1Provider linkTokenV1Provider =
  new LinkTokenV1Provider(
    new LinkTokenCryptoKeyProvider(), new AppStorePublicKeyProvider());

キーの生成とキャッシュ

次のILinkTokenCryptoKeyProviderの実装例では、クラスのインスタンス化中にAES 128ビットキーを生成してキャッシュし、リクエストされたらいつでもこのキーを提供します。実際の実装では、事前に作成されたアプリキーが格納されているセキュアストアからキーを取得するロジックを追加する必要があります。

 static class LinkTokenCryptoKeyProvider implements ILinkTokenCryptoKeyProvider {
   SimpleSignInCryptoKey < SecretKey > cryptoKey;

   LinkTokenCryptoKeyProvider() throws TokenException {
     try {
       KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
       keyGenerator.init(128, new SecureRandom());
       SecretKey secretKey = keyGenerator.generateKey();
       cryptoKey = new SimpleSignInCryptoKey(secretKey, "key.1");
     } catch (NoSuchAlgorithmException e) {
       throw new TokenException(e);
     }
   }

   @Override
   public SimpleSignInCryptoKey < SecretKey > getEncryptionKey(IRequestContext iRequestContext) throws TokenException {
     /*
     対象アプリリビジョンに使用する暗号化キーを返します。それぞれに複数の 
     リビジョンがある複数のアプリの所有者は、リクエスト元に関連する
     コンテキストを渡すIRequestContextオブジェクトを使用して、
     適切なキーを選択できます。
      */
     return cryptoKey;
   }

   @Override
   public SimpleSignInCryptoKey < SecretKey > getDecryptionKey(IRequestContext iRequestContext, String s) throws TokenException {
     /*
     対象アプリリビジョンに使用する復号化キーを返します。それぞれに複数の 
     リビジョンがある複数のアプリの所有者は、リクエスト元に関連する
     コンテキストを渡すIRequestContextオブジェクトを使用して、
     適切なキーを選択できます。
      */
     return cryptoKey;
   }
 }

静的キーペアの生成

次の2つのコードサンプルでは、IAppStorePublicKeyProviderを実装して、静的キーペアをローカルで生成し、生成されたキーペアのパブリックキーコンポーネントをgetPublicKey()メソッドで公開します。実際の実装では、Amazonアプリストアによって割り当てられたパブリックキーが保存されているセキュアストアからキーマテリアルを取得するためのロジックを追加する必要があります。

private static KeyPair appStoreKeyPair;

static {
  KeyPairGenerator keyPairGenerator;
  try {
    keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  } catch (NoSuchAlgorithmException ex) {
    throw new RuntimeException(ex);
  }
  keyPairGenerator.initialize(2048);
  appStoreKeyPair = keyPairGenerator.generateKeyPair();
}
 static class AppStorePublicKeyProvider implements IAppStorePublicKeyProvider {
   @Override
   public SimpleSignInCryptoKey < PublicKey > getPublicKey(IRequestContext iRequestContext)
   throws AppStorePublicKeyException {
     /*
     ここではKeyIdentifier「key.1」はハードコードされています。実際の実装では、開発者によってリクエスト元に関する
     関連情報(アプリとそのバージョンなど)がIRequestContextオブジェクトに
     追加されているでしょう。その内容は、キーIDおよび使用するのに適切なパブリックキーを
     決定するために使用されます。
      */
     SimpleSignInCryptoKey < PublicKey > appStorePublicKey =
       new SimpleSignInCryptoKey < > (appStoreKeyPair.getPublic(), "key.1");
     return appStorePublicKey;
   }
 }

generateLinkToken()メソッドを呼び出せばリンクトークンを生成できます。GenerateLinkTokenV1Requestタイプのリクエストオブジェクトには、トークン内でエンコードされる詳細情報が含まれています。

GenerateLinkTokenV1Request request = GenerateLinkTokenV1Request.builder()
  .directedAmazonUserId("amazonUserId")
  .partnerUserId("partnerUserId")
  .customFields(null) // カスタムフィールドのマップ
  .requestContext(requestContext)
  .build();
LinkTokenContainer linkTokenContainer = linkTokenV1Provider.generateLinkToken(request);

出力として返されるLinkTokenContainerオブジェクトには、以下のデータが含まれます。

  • token: リンクトークンペイロード。システム内のユーザーのIDをキャプチャするプレーンな文字列です。Amazonユーザーとのリンクを証明するために使用されます。
  • tokenSchema: リンクトークンスキーマLINK-TOKEN-1.0
  • linkSigningKeyEncrypted: リンク署名キー。このリンクのSSIトークンに署名するECプライベートキー。Amazonアプリストアのパブリックキーを使用して暗号化されます。

SSIトークンの検証とデコード

SSIトークンを検証してデコードするには、SSITokenV1ValidatorクラスのdecodeAndVerifyToken()メソッドを使用します。このメソッドは、SSIトークンの署名を検証し、トークンの有効期限が切れていないことを確認します。

SSITokenV1Validator ssiTokenV1Validator
  = new SSITokenV1Validator(linkTokenV1Provider);
SSITokenInfo ssiTokenInfo = ssiTokenV1Validator
  .decodeAndVerifyToken(ssiToken, requestContext);

この例で使用されている入力パラメーターは以下のとおりです。

  • linkTokenV1Provider - SSITokenV1Validatorのコンストラクターに渡されるLinkTokenV1Providerオブジェクト。これは、リンクトークンの生成用に設定したLinkTokenV1Providerのインスタンスと同じです。
  • requestContext - IRequestContextインターフェイスの実装のインスタンス。このオブジェクトには、ILinkTokenCryptoKeyProviderを実装して、適切な復号化キーの取得、リンクトークンの復号化、リンク検証キーの抽出を行うために必要な詳細情報が含まれています。リンク検証キーは、その中に埋め込まれたSSIトークン署名を検証するために使用されます。
  • ssiToken - デコードして検証するSSITokenオブジェクト。

SSIトークン

SSIトークンの形式は、SSIToken(token=<xxxxx.yyyyy.zzzzz>, schema=SSI-TOKEN-1.0)です。このトークンはJSON Web Token(JWT)で、通常は「xxxxx.yyyy.zzzzz」のように表示されます。xxxxxはBase64Urlでエンコードされたヘッダー、yyyyyはBase64Urlでエンコードされたペイロード、zzzzzはxxxxxとyyyyyの計算された署名で、アプリ固有のプライベートキーを使用します。

デコードされたSSIトークンの形式は以下のとおりです。

SSITokenInfo(linkInfo=SSITokenInfo.LinkInfo(directedAmazonUserId=amazonUserId,
directedPartnerUserId=partnerUserId,
linkToken=LinkToken(token=<システム内のユーザーのIDをキャプチャし、
Amazonユーザーとのリンクを証明するプレーンな文字列のリンクトークン。このトークンの 
文字列はアカウントリンクのリクエスト中に渡されました>, tokenSchema=LINK-TOKEN-1.0)),
tokenMetadata=SSITokenV1Metadata(super=SSITokenMetadata(issuedAt=1628082286000,
notValidBefore=1628082286000, expiresAt=1628082586000),issuer=http://ssi.amazon.com,
audience=TEST_DEVELOPER_ID,jwtId=66141c38-1bb5-4336-a2a6-55247345dc99))

SSIトークンが正常に検証されたら、ラップされたリンクトークンを検証し、トークンからユーザーIDの詳細を抽出します。これを実行するには、LinkTokenV1ProvidervalidateLinkToken()メソッドを使用します。

LinkTokenInfo linkTokenInfoFromSSIToken = linkTokenV1Provider
  .validateLinkToken(ssiTokenInfo, requestContext);

この例で使用されているvalidateLinkToken()メソッドの入力パラメーターは以下のとおりです。

  • ssiTokenInfo - SSITokenInfoタイプのインスタンス。SSIトークンからデコードされた詳細情報が含まれています。
  • requestContext - IRequestContextインターフェイスの実装のインスタンス。このオブジェクトには、リンクトークンの復号に使用する復号化キーを取得するためのILinkTokenCryptoKeyProviderの実装に必要な詳細情報が含まれています。

SSIトークンの生成

本番アプリではSSIトークンを自分で生成する必要はありませんが、テスト目的ではトークンを生成することができます。SSIトークンライブラリを使用してSSIトークンを生成する手順は以下のとおりです。

  1. リンクトークンの生成で説明されている手順に従ってリンクトークンを生成します。
  2. テストアプリで、Amazonアプリストアのキーペアのプライベートキーとパブリックキーの両方にアクセスできると仮定します。ただし、本番アプリの場合、アクセスできるのはパブリックキーだけです。IAppStorePrivateKeyProviderインターフェイスを実装し、プライベートキーにアクセスしてリンク署名キーを復号できるようにします。このリンク署名キーは、リンクトークンと共に発行され、Amazonアプリストアのパブリックキーを使用して暗号化されます。

    次のIAppStorePrivateKeyProviderインターフェイスの実装例では、静的キーペアをローカルで生成し、getPrivateKey()メソッドを使用してプライベートキーを公開します。両方のコードサンプルを使用します。

     private static KeyPair appStoreKeyPair;
    
     static {
       KeyPairGenerator keyPairGenerator;
       try {
         keyPairGenerator = KeyPairGenerator.getInstance("RSA");
       } catch (NoSuchAlgorithmException ex) {
         throw new RuntimeException(ex);
       }
       keyPairGenerator.initialize(2048);
       appStoreKeyPair = keyPairGenerator.generateKeyPair();
     }
    
     static class AppStorePrivateKeyProvider implements IAppStorePrivateKeyProvider {
       @Override
       public SimpleSignInCryptoKey < PrivateKey > getPrivateKey(String app, String appVersion) throws AppStorePrivateKeyException {
        SimpleSignInCryptoKey < PrivateKey > appStorePrivateKey =
          new SimpleSignInCryptoKey < > (appStoreKeyPair.getPrivate(), "key.1");
         return appStorePrivateKey;
       }
     }
    
  3. LinkSigningKeyDecryptorオブジェクトを使用してリンク署名キーを復号化します。LinkSigningKeyDecryptorインスタンスは、IAppStoreprivateKeyProviderインターフェイスの実装を使用して構築されます。

     LinkSigningKeyDecryptor linkSigningKeyDecryptor =
       new LinkSigningKeyDecryptor(new AppStorePrivateKeyProvider());
     String linkSigningKey = linkSigningKeyDecryptor
       .decryptLinkSigningKey(linkTokenContainer.getLinkSigningKeyEncrypted(),
         "TEST_ASIN", "TEST_APP_VERSION");
    
  4. 次のようにLinkオブジェクトを作成し、アカウントのリンク関係を表すAmazonでの表記を生成します。

     Link link = Link.builder()
       .linkId("amzn1.ssi.link.123456")
       .directedAmazonUserId("amazonUserId")
       .directedPartnerUserId("partnerUserId")
       .identityProvider("partnerIdp")
       .linkedTime(System.currentTimeMillis())
       .linkSigningKey(linkSigningKey)
       .linkToken(linkTokenContainer.getLinkToken())
       .build();
    
  5. 前の手順で作成したLinkオブジェクトを使用して、リンク済みアカウントのSSIトークンを生成します。SSITokenV1Generatorクラスで公開されたgenerateToken()APIを使用して、次のようにSSIトークンを作成します。
SSITokenV1Generator ssiTokenV1Generator = new SSITokenV1Generator();
ProductInfo productInfo = ProductInfo.builder()
  .vendorId("TEST_DEVELOPER_ID")
  .asin("TEST_ASIN")
  .productVersion("TEST_APP_VERSION")
  .build();
SSIToken ssiToken = ssiTokenV1Generator.generateToken(link, productInfo);