开发者控制台

SSI令牌库

SSI令牌库

SSI令牌库是一个Java库,包含用于生成、验证关联和SSI令牌的实用工具类。您可以使用此库执行以下操作。

  • 生成关联令牌以在账户关联设置期间发放给亚马逊。
  • 在针对您应用的新登录请求期间验证和解码SSI令牌。
  • 在应用的新登录请求期间验证和解码关联令牌;通过对SSI令牌进行解码来提取关联令牌。
  • 生成SSI令牌以模拟内部测试中Amazon SSI服务器的行为。

要在项目中使用SSI令牌库,添加JAR文件:SimpleSignInTokenCommon-<x.y>.jar。您可以在这里下载JAR,也可以从Appstore SDK中包含的SSI示例应用的libs文件夹中获取它。这个JAR文件将是您项目中的一个依赖项。有关其他直接和可传递的Maven依赖项,请参阅SSI示例应用中的build.gradle文件。

以下各部分通过示例代码参考解释了上述操作的库用法。有关Javadoc格式的各个类和方法的更详细描述,请参阅完整的SSI令牌库API参考

LinkTokenV1Provider类提供API以生成关联令牌。用于实例化LinkTokenV1Provider的构造函数接受两个输入参数 - ILinkTokenCryptoKeyProviderIAppStorePublicKeyProvider接口的实现实例。这些实现包括从密钥库中检索加密密钥的逻辑。

检索加密密钥和公有密钥对的逻辑位于ILinkTokenCryptoKeyProviderIAppStorePublicKeyProvider接口内部。使用以下代码创建一个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;
   }
 }

生成静态密钥对

以下两个代码示例用于实现IAppStorePublicKeyProvider,在本地生成一个静态密钥对,并通过其getPublicKey()方法公开生成的密钥对的公有密钥组件。在实际实现中,必须添加逻辑以从存储亚马逊应用商店分配的公有密钥的安全存储中获取密钥材料。

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对象中。这些详细信息将用于确定密钥标识符,
     从而确定要使用的适当公有密钥。
      */
     SimpleSignInCryptoKey < PublicKey > appStorePublicKey =
       new SimpleSignInCryptoKey < > (appStoreKeyPair.getPublic(), "key.1");
     return appStorePublicKey;
   }
 }

现在,您可以通过调用generateLinkToken()方法来生成关联令牌。GenerateLinkTokenV1Request类型的请求对象包含要在令牌中编码的详细信息。

GenerateLinkTokenV1Request request = GenerateLinkTokenV1Request.builder()
  .directedAmazonUserId("amazonUserId")
  .partnerUserId("partnerUserId")
  .customFields(null) //Map of custom fields
  .requestContext(requestContext)
  .build();
LinkTokenContainer linkTokenContainer = linkTokenV1Provider.generateLinkToken(request);

作为输出返回的LinkTokenContainer对象包含以下数据。

  • 令牌: 关联令牌有效负载。这是一个简单的字符串,用于捕获系统中用户的身份。它被用作与亚马逊用户关联的证据。
  • tokenSchema: 关联令牌架构,LINK-TOKEN-1.0
  • linkSigningKeyEncrypted: 关联签名密钥。一个EC私钥,应为其将此关联的SSI令牌签名。使用您的亚马逊应用商店公有密钥进行加密。

SSI令牌验证和解码

要验证和解码SSI令牌,请使用SSITokenV1Validator类的decodeAndVerifyToken()方法。此方法验证SSI令牌的签名,并确保该令牌未过期。

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

本例中使用的输入参数为:

  • linkTokenV1Provider - 一个LinkTokenV1Provider对象,传递至SSITokenV1Validator的构造函数中。这与您为生成关联令牌而设置的LinkTokenV1Provider实例相同。
  • requestContext - IRequestContext接口的实现实例。此对象包含实现ILinkTokenCryptoKeyProvider所需的详细信息,以便获得适当的解密密钥,解密关联令牌以及提取关联验证密钥。关联验证密钥用于验证嵌入其中的SSI令牌签名。
  • ssiToken - 要解码和验证的SSIToken对象。

SSI令牌

SSI令牌的格式为SSIToken(token=<xxxxx.yyyyy.zzzzz>, schema=SSI-TOKEN-1.0)。此处的token为JSON Web令牌(JWT),通常形式为"xxxxx.yyyyy.zzzzz"。其中xxxxx是Base64Url编码的标头,yyyyy是Base64Url编码的有效载荷,zzzzz是xxxxx和yyyyy的计算签名,带有特定于应用的私钥。

解码的SSI令牌具有以下格式:

SSITokenInfo(linkInfo=SSITokenInfo.LinkInfo(directedAmazonUserId=amazonUserId,
directedPartnerUserId=partnerUserId,
linkToken=LinkToken(token=<一个普通字符串关联令牌,
用于捕获系统中用户的身份,并证明与亚马逊用户的关联。此令牌 
字符串是在账户关联请求期间传递的>,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令牌后,验证封装的关联令牌,并从令牌中提取客户标识详细信息。使用LinkTokenV1ProvidervalidateLinkToken()方法可实现这一点。

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

本例中使用的validateLinkToken()方法的输入参数为:

  • ssiTokenInfo - SSITokenInfo类型的实例,其中包含从SSI令牌解码的详细信息。
  • requestContext - IRequestContext接口的实现实例。此对象包含实现ILinkTokenCryptoKeyProvider所需的详细信息,以获取用于解密关联令牌的解密密钥。

生成SSI令牌

您不需要为生产应用自己生成SSI令牌,但可能需要为测试生成令牌。您可以使用SSI令牌库生成SSI令牌,按照以下步骤操作:

  1. 按照生成关联令牌中所述的步骤生成关联令牌。
  2. 假设对于您的测试应用,您可以访问亚马逊应用商店密钥对的私钥和公有密钥。但对于生产应用,您只能访问公有密钥。实现IAppStorePrivateKeyProvider接口,以允许访问私钥来解密关联签名密钥,该密钥与关联令牌一起发放,并使用亚马逊应用商店公有密钥进行加密。

    以下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对象,生成账户关联关系的亚马逊表示。

     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);