

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用以下方法将您的容器产品与 AWS Marketplace 计量服务集成 适用于 Java 的 AWS SDK
<a name="java-integration-example-registerusage"></a>

您可以使用与 AWS Marketplace 计量服务集成。 适用于 Java 的 AWS SDK 软件使用的连续计量由自动处理 AWS Marketplace Metering Control Plane。除了调用 `RegisterUsage` 一次以启动软件使用的计量外，您的软件无需执行任何计量特定操作。本主题提供了一个使用与[AWS Marketplace 计量服务](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html)`RegisterUsage`操作集成的实现示例。 适用于 Java 的 AWS SDK 

`RegisterUsage` 必须在启动容器时立即调用。如果您在容器启动后的前 6 个小时内没有注册容器，AWS Marketplace Metering Service 将不为前几个月提供任何计量保证。但是，计量将在当月继续进行，直到容器结束。

有关完整源代码，请参阅[RegisterUsage Java 示例](#registerusage-java-example)。无论使用哪种 AWS SDK 语言，其中的许多步骤都适用。



**AWS Marketplace Metering Service 集成的示例步骤**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/tour)。

1. 从**资产**中，选择**容器**以开始创建新容器产品。创建产品会生成产品的产品代码以与您的容器映像集成。有关设置 IAM 权限的信息，请参阅[AWS Marketplace 计量和授权 API 权限](iam-user-policy-for-aws-marketplace-actions.md)。

1.  下载公开的 [AWS Java SDK](https://aws.amazon.com/sdk-for-java/)。
**重要**  
 要 APIs 从 Amazon EKS 调用计量，您必须[使用支持的 AWS 软件开发工具包并在运行 K](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html) ubernetes 1.13 或更高版本的亚马逊 EKS 集群上运行。

1.  （可选）如果您要与`RegisterUsage`操作集成，并且想要执行数字签名验证，则需要在应用程序类路径中配置[BouncyCastle](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on)签名验证库。

   如果要使用 JSON Web Token (JWT)，则还必须在应用程序类路径中包括 [JWT Java](https://jwt.io/) 库。使用 JWT 提供了一种更简单的签名验证方法，但这不是必需的，您可以 BouncyCastle 改用独立版。无论您使用 JWT 还是 BouncyCastle，都需要使用诸如 Maven 之类的构建系统在应用程序类路径中包含 BouncyCastle 或 JWT 的传递依赖关系。

   ```
   // Required for signature verification using code sample
   <dependency>
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcpkix-jdk15on</artifactId>
       <version>1.60</version>
   </dependency>
   
   // This one is only required for JWT
   <dependency>
       <groupId>com.nimbusds</groupId>
       <artifactId>nimbus-jose-jwt</artifactId>
       <version>6.0</version>
   </dependency>
   ```

1.  从您的产品中的每个付费容器映像调用 `RegisterUsage`。`ProductCode` 和 `PublicKeyVersion` 是必需参数，所有其他输入都是可选的。以下是 `RegisterUsage` 的示例负载。

   ```
   {
       "ProductCode" : "string", // (required)
       "PublicKeyVersion": 1,    // (required)
       "Nonce": "string",        // (optional) to scope down the registration
                                 //            to a specific running software
                                 //            instance and guard against
                                 //            replay attacks
   }
   ```
**注意**  
可能出现 AWS Marketplace Metering Service 瞬时连接问题。 AWS Marketplace 强烈建议实施最长 30 分钟的重试，并添加指数退避，以避免短期中断或网络问题。

1.  `RegisterUsage` 使用 SHA-256 生成可用于验证请求真实性的 RSA-PSS 数字签名。该签名包括以下字段：`ProductCode`、`PublicKeyVersion` 和 `Nonce`。要验证数字签名，您必须保留请求中的这些字段。以下代码是 `RegisterUsage` 调用的示例响应。

   ```
   {
   "Signature": "<<JWT Token>>"
   }
   
   // Where the JWT Token is composed of 3 dot-separated, 
   // base-64 URL Encoded sections.
   // e.g. eyJhbGcVCJ9.eyJzdWIMzkwMjJ9.rrO9Qw0SXRWTe
   
   // Section 1: Header/Algorithm
   {
   "alg": "PS256",
   "typ": "JWT"
   }
   
   // Section 2: Payload
   {
   "ProductCode" : "string",
   "PublicKeyVersion": 1,
   "Nonce": "string",
   "iat": date // JWT issued at claim 
   }
   
   // Section 3: RSA-PSS SHA256 signature
   "rrO9Q4FEi3gweH3X4lrt2okf5zwIatUUwERlw016wTy_21Nv8S..."
   ```

1. 重建包含 `RegisterUsage` 调用的新版本容器映像，标记容器，然后将其推送至与 Amazon ECS 或 Amazon EKS 兼容的任何容器注册表，如 Amazon ECR 或 Amazon ECR Public。如果您使用的是 Amazon ECR，请确保启动 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 的账户对 Amazon ECR 存储库拥有权限。否则，启动失败。

1.  创建一个 [IAM](https://aws.amazon.com/iam/) 角色来授予容器调用 `RegisterUsage` 的权限，如以下代码所定义。您必须在 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 定义的[任务角色](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_role_arn)参数中提供此 IAM 角色。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Action": [
                   "aws-marketplace:RegisterUsage"
                   ],
                   "Effect": "Allow",
                   "Resource": "*"
           }
       ]
   }
   ```

------

1. 创建 Amazon ECS 任务或 Amazon EKS 容器定义，引用已与您在步骤 7 中创建的 IAM 角色集成的容器 AWS Marketplace 并引用该角色。如果要查看 AWS CloudTrail 日志记录，则应在任务定义中启用日志记录。

1. 创建 Amazon ECS 或 Amazon EKS 集群来执行您的任务或容器组 (pod)。有关创建 Amazon ECS 集群的更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[创建集群](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create_cluster.html)。有关（使用 Kubernetes 版本 1.1.3.x 或更高版本）创建 Amazon EKS 集群的更多信息，请参阅[创建 Amazon EKS 集群](https://docs.aws.amazon.com/eks/latest/userguide/create_cluster.html)。

1. 配置 Amazon ECS 或 Amazon EKS 集群，然后在 us-east- AWS 区域 1 中启动你创建的亚马逊 ECS 任务定义或亚马逊 EKS 容器。只有在此测试过程中，在产品上线之前，您才必须使用此区域。

1. 当您从 `RegisterUsage` 获得有效的响应时，您可以开始创建您的容器产品。如有问题，请联系 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队。

## RegisterUsage Java 示例
<a name="registerusage-java-example"></a>

以下示例使用 适用于 Java 的 AWS SDK 和 AWS Marketplace 计量服务来调用该`RegisterUsage`操作。签名验证是可选的，但如果您要执行签名验证，则必须包含所需的数字签名验证库。此示例仅用于说明。

```
import com.amazonaws.auth.PEM;
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMetering;
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMeteringClientBuilder;
import com.amazonaws.services.marketplacemetering.model.RegisterUsageRequest;
import com.amazonaws.services.marketplacemetering.model.RegisterUsageResult;
import com.amazonaws.util.json.Jackson;
import com.fasterxml.jackson.databind.JsonNode;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * Class for making calls out to &MKT; Metering Service.
 */
class RegisterUsage {

    private static final String PRODUCT_CODE = ".......";

    private final AWSMarketplaceMetering registerUsageClient;
    private final SignatureVerifier signatureVerifier;
    private final int publicKeyVersion;

    public RegisterUsage(final SignatureVerifier signatureVerifier) {
        this.signatureVerifier = signatureVerifier;
        this.publicKeyVersion = PublicKeyProvider.PUBLIC_KEY_VERSION;
        this.registerUsageClient = AWSMarketplaceMeteringClientBuilder.standard().build();
    }

    /**
     * Shows how to call RegisterUsage client and verify digital signature.
     */
    public void callRegisterUsage() {
        RegisterUsageRequest request = new RegisterUsageRequest()
                .withProductCode(PRODUCT_CODE)
                .withPublicKeyVersion(publicKeyVersion)
                .withNonce(UUID.randomUUID().toString());

        // Execute call to RegisterUsage (only need to call once at container startup)
        RegisterUsageResult result = this.registerUsageClient.registerUsage(request);

        // Verify Digital Signature w/o JWT
        boolean isSignatureValid = this.signatureVerifier.verify(request, result);
        if (!isSignatureValid) {
            throw new RuntimeException("Revoke entitlement, digital signature invalid.");
        }
    }
}

/**
 * Signature verification class with both a JWT-library based verification
 * and a non-library based implementation.
 */
class SignatureVerifier {
    private static BouncyCastleProvider BC = new BouncyCastleProvider();

    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA/PSS";

    private final PublicKey publicKey;

    public SignatureVerifier(PublicKeyProvider publicKeyProvider) {
        this.publicKey = publicKeyProvider.getPublicKey().orElse(null);
        Security.addProvider(BC);
    }

    /**
     * Example signature verification using the NimbusJOSEJWT library to verify the JWT Token.
     *
     * @param request RegisterUsage Request.
     * @param result  RegisterUsage Result.
     * @return true if the token matches.
     */
    public boolean verifyUsingNimbusJOSEJWT(final RegisterUsageRequest request, final RegisterUsageResult result) {
        if (!getPublicKey().isPresent()) {
            return false;
        }

        try {
            JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) getPublicKey().get());
            JWSObject jwsObject = JWSObject.parse(result.getSignature());
            return jwsObject.verify(verifier) && validatePayload(jwsObject.getPayload().toString(), request, result);
        } catch (Exception e) {
            // log error
            return false;
        }
    }

    /**
     * Example signature verification without any JWT library support.
     *
     * @param request RegisterUsage Request.
     * @param result  RegisterUsage Result.
     * @return true if the token matches.
     */
    public boolean verify(final RegisterUsageRequest request, final RegisterUsageResult result) {
        if (!getPublicKey().isPresent()) {
            return false;
        }
        try {
            String[] jwtParts = result.getSignature().split("\\.");
            String header = jwtParts[0];
            String payload = jwtParts[1];
            String payloadSignature = jwtParts[2];

            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, BC);
            signature.initVerify(getPublicKey().get());
            signature.update(String.format("%s.%s", header, payload).getBytes(StandardCharsets.UTF_8));
            boolean verified = signature.verify(Base64.getUrlDecoder()
                    .decode(payloadSignature.getBytes(StandardCharsets.UTF_8)));

            String decodedPayload = new String(Base64.getUrlDecoder().decode(payload));
            return verified && validatePayload(decodedPayload, request, result);
        } catch (Exception e) {
            // log error
            return false;
        }
    }

    /**
     * Validate each value in the returned payload matches values originally
     * supplied in the request to RegisterUsage. TimeToLiveInMillis and
     * PublicKeyExpirationTimestamp will have the values in the payload compared
     * to values in the signature
     */
    private boolean validatePayload(final String payload, final RegisterUsageRequest request,
                                    final RegisterUsageResult result) {
        try {
            JsonNode payloadJson = Jackson.getObjectMapper().readTree(payload);
            boolean matches = payloadJson.get("productCode")
                    .asText()
                    .equals(request.getProductCode());
            matches = matches && payloadJson.get("nonce")
                    .asText()
                    .equals(request.getNonce());
            return matches = matches && payloadJson.get("publicKeyVersion")
                    .asText()
                    .equals(String.valueOf(request.getPublicKeyVersion()));

        } catch (Exception ex) {
            // log error
            return false;
        }
    }

    private Optional<PublicKey> getPublicKey() {
        return Optional.ofNullable(this.publicKey);
    }
}

/**
 * Public key provider taking advantage of the &AWS; PEM Utility.
 */
class PublicKeyProvider {
    // Replace with your public key. Ensure there are new-lines ("\n") in the
    // string after "-----BEGIN PUBLIC KEY-----\n" and before "\n-----END PUBLIC KEY-----".
    private static final String PUBLIC_KEY =
            "-----BEGIN PUBLIC KEY-----\n"
                    + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\n"
                    + "UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\n"
                    + "HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\n"
                    + "o2kQ+X5xK9cipRgEKwIDAQAB\n"
                    + "-----END PUBLIC KEY-----";

    public static final int PUBLIC_KEY_VERSION = 1;

    public Optional<PublicKey> getPublicKey() {
        try {
            return Optional.of(PEM.readPublicKey(new ByteArrayInputStream(
                    PUBLIC_KEY.getBytes(StandardCharsets.UTF_8))));
        } catch (Exception e) {
            // log error
            return Optional.empty();
        }
    }
}
```