

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# AWS Marketplace Metering Service를 사용한 시간당 측정 구성
<a name="container-metering-registerusage"></a>

**참고**  
 Amazon EKS 배포의 경우, 소프트웨어는 [서비스 계정에 대한 IAM 역할(IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)을 사용하여 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html) API 작업에 대한 API 직접 호출에 서명해야 합니다. [EKS Pod Identity](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html), 노드 역할 또는 장기 액세스 키의 사용이 지원되지 않습니다.  
Amazon ECS 배포의 경우, 소프트웨어는 [Amazon ECS 작업 IAM](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) 역할을 사용하여 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html) API 작업에 대한 API 직접 호출에 서명해야 합니다. 노드 역할 또는 장기 액세스 키의 사용은 지원되지 않습니다.

컨테이너 제품이 사용자 지정 측정 요금 차원 대신 시간당 작업별 또는 포드별 요금을 사용하는 경우 사용자 지정 측정 차원을 정의할 필요가 없습니다. AWS Marketplace Metering Service를 사용하여 AWS Marketplace의 컨테이너 제품을 시간당 측정할 수 있습니다. 다음 섹션에서는 AWS Marketplace Metering Service를 사용하여 시간당 측정을 구성하는 방법을 보여줍니다.

`RegisterUsage` API 작업은 Amazon Elastic Container Service(Amazon ECS) 작업별, Amazon Elastic Kubernetes Service(Amazon EKS) 포드별 또는 시간당 소프트웨어 사용량을 측정하며 사용량은 초 단위로 비례 배분합니다. 실행 시간이 짧은 작업 또는 포드에 대해서는 최소 1분의 사용량이 적용됩니다. 소프트웨어 사용을 위한 연속 측정은에서 자동으로 처리됩니다 AWS Marketplace Metering Control Plane. 사용자 소프트웨어는 소프트웨어 사용 측정을 시작할 때 한 번 `RegisterUsage`를 호출하는 것 외에 다른 측정 작업이 필요하지 않습니다.

컨테이너를 시작할 때 `RegisterUsage`를 즉시 호출해야 합니다. 컨테이너 시작 후 처음 6시간 이내에 컨테이너를 등록하지 않으면 AWS Marketplace 측정 서비스는 이전 달에 대한 어떠한 측정도 보장하지 않습니다. 하지만 이번 달 이후에도 컨테이너가 종료될 때까지 측정이 계속됩니다.

는 AWS Marketplace Metering Control Plane 고객의 구독 상태에 관계없이 Amazon ECS 작업 및 Amazon EKS 포드 실행에 대한 요금을 고객에게 계속 청구합니다. 따라서 작업 또는 포드가 성공적으로 시작된 후에는 소프트웨어에서 권한 검사를 수행할 필요가 없습니다.

 AWS Marketplace Metering Service API를 시간당 요금으로 컨테이너 제품과 통합하는 방법에 대한 자세한 내용은 *AWS Marketplace 판매자 워크숍*의 [시간당 측정과 통합](https://catalog.workshops.aws/mpseller/en-US/container/integrate-hourly) 실습을 참조하세요.

**Topics**
+ [시간당 측정을 위한 사전 조건](#hourly-metering-prereqs)
+ [`RegisterUsage`에 대한 통합 테스트](#testing-integration-for-registerusage)
+ [`RegisterUsage`의 오류 처리](#hourly-metering-entitlement-error-handling)
+ [를 사용하여 컨테이너 제품을 AWS Marketplace 측정 서비스와 통합 AWS SDK for Java](java-integration-example-registerusage.md)

## 시간당 측정을 위한 사전 조건
<a name="hourly-metering-prereqs"></a>

제품을 게시하기 전에 다음을 수행해야 합니다.

1. 에서 새 컨테이너 제품을 AWS Marketplace Management Portal생성하고 해당 제품 코드를 기록해 둡니다.

   자세한 내용은 [개요: 컨테이너 제품 만들기](container-product-getting-started.md#create-container-product) 단원을 참조하십시오.

1. 를 호출하는 데 필요한 IAM 권한으로 애플리케이션을 실행하는 작업 또는 포드에 AWS Identity and Access Management (IAM) 역할을 사용합니다`RegisterUsage`. IAM 관리형 정책 `AWSMarketplaceMeteringRegisterUsage`에 이러한 권한이 있습니다. 이 정책에 대한 자세한 내용은 *AWS 관리형 정책 참조*의 [AWSMarketplaceMeteringFullAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSMarketplaceMeteringFullAccess.html)를 참조하세요.

1. (선택 사항) 로깅을 보려면 작업 또는 포드 정의에서 AWS CloudTrail 로깅을 활성화하는 것이 좋습니다.

1. 정의하는 모든 요금 차원에 대한 레코드를 사용하여 `RegisterUsage` API 작업 호출을 테스트합니다.

## `RegisterUsage`에 대한 통합 테스트
<a name="testing-integration-for-registerusage"></a>

게시를 위해 이미지를 AWS Marketplace 에 제출하기 전에 `RegisterUsage` API 작업을 사용하여 통합을 테스트합니다.

Amazon ECS 또는 Amazon EKS에서 제품을 실행하여 컨테이너 이미지에서 `RegisterUsage`를 직접 호출합니다. 사용 중인 AWS 계정을 사용하여 제품을 나열합니다 AWS Marketplace. 측정 통합은 하드 코딩 AWS 리전이 아닌 동적으로를 설정해야 합니다. 하지만 테스트할 때는 미국 동부(버지니아 북부) 리전에서 유료 컨테이너가 포함된 Amazon ECS 작업 또는 Amazon EKS 포드를 하나 이상 시작합니다. 이렇게 하면 AWS Marketplace 운영 팀이 해당 리전의 로그로 작업을 확인할 수 있습니다.

**참고**  
제품이 Amazon ECS와 Amazon EKS를 모두 지원하는 경우 판매자는 Amazon EKS에서 시작하기만 하면 됩니다. 그러면 AWS에서 통합을 검증할 수 있습니다.

제품과 함께 필요한 모든 메타데이터 및 요금 정보가 게시될 때까지는 통합에 대한 완전한 테스트가 어렵습니다. 요청된 경우 AWS Marketplace 카탈로그 운영 팀은 측정 레코드의 수신을 확인할 수 있습니다.

## `RegisterUsage`의 오류 처리
<a name="hourly-metering-entitlement-error-handling"></a>

컨테이너 이미지가와 통합 AWS Marketplace Metering Service 되고 컨테이너 시작 `ThrottlingException` 시 이외의 예외가 발생하는 경우 무단 사용을 방지하기 위해 컨테이너를 종료해야 합니다.

`ThrottlingException` 이외의 예외는 초기 `RegisterUsage` API 작업 호출에서만 발생합니다. 하지만 이후에 동일한 Amazon ECS 작업 또는 Amazon EKS 포드에서 호출할 때에는 작업 또는 포드가 여전히 실행 중인 동안 고객이 구독을 해지해도 `CustomerNotSubscribedException`이 throw되지 않습니다. 이러한 고객은 구독을 취소한 후에도 사용량을 추적하여 컨테이너 실행에 대한 요금이 계속 청구됩니다.

다음 표에는 `RegisterUsage` API 작업에서 발생할 수 있는 오류가 설명되어 있습니다. 각 AWS SDK 프로그래밍 언어에는 추가 정보를 위해 참조할 수 있는 오류 처리 지침 세트가 있습니다.


|  **오류**  |  **설명**  | 
| --- | --- | 
|  InternalServiceErrorException  |  RegisterUsage를 사용할 수 없습니다. | 
|  CustomerNotEntitledException  |  고객에게 유효한 제품 구독이 없습니다. | 
|  InvalidProductCodeException  |  요청과 함께 전달된 ProductCode 값이 존재하지 않습니다. | 
|  InvalidPublicKeyException  |  요청과 함께 전달된 PublicKeyVersion 값이 존재하지 않습니다. | 
|  PlatformNotSupportedException  |  AWS Marketplace 는 기본 플랫폼의 측정 사용량을 지원하지 않습니다. Amazon ECS, Amazon EKS 및 만 지원 AWS Fargate 됩니다. | 
|  ThrottlingException  |  RegisterUsage 호출에 병목 현상이 발생했습니다. | 
|  InvalidRegionException  |  RegisterUsage는 Amazon ECS 작업 또는 Amazon EKS 포드가 시작된 AWS 리전 것과 동일한에서 호출되어야 합니다. 그래야만 RegisterUsage 호출 시 컨테이너가 리전(예: withRegion(“us-east-1”))을 선택하지 않습니다. | 

# 를 사용하여 컨테이너 제품을 AWS Marketplace 측정 서비스와 통합 AWS SDK for Java
<a name="java-integration-example-registerusage"></a>

 AWS SDK for Java 를 사용하여 AWS Marketplace 측정 서비스와 통합할 수 있습니다. 소프트웨어 사용을 위한 연속 측정은에서 자동으로 처리됩니다 AWS Marketplace Metering Control Plane. 사용자 소프트웨어는 소프트웨어 사용 측정을 시작할 때 한 번 `RegisterUsage`를 호출하는 것 외에 다른 측정 작업이 필요하지 않습니다. 이 주제에서는를 사용하여 [AWS Marketplace 측정 서비스의](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html) `RegisterUsage` 작업과 통합 AWS SDK for Java 하는 예제 구현을 제공합니다.

컨테이너를 시작할 때 `RegisterUsage`를 즉시 호출해야 합니다. 컨테이너 시작 후 처음 6시간 이내에 컨테이너를 등록하지 않으면 AWS Marketplace 측정 서비스는 이전 달에 대한 어떠한 측정도 보장하지 않습니다. 하지만 이번 달 이후에도 컨테이너가 종료될 때까지 측정이 계속됩니다.

전체 소스 코드는 [RegisterUsage Java 예제](#registerusage-java-example) 단원을 참조하세요. 이러한 단계 중 대부분은 AWS SDK 언어에 관계없이 적용됩니다.



**AWS Marketplace 측정 서비스를 통합하는 단계 예제**

1. [AWS Marketplace Management Portal](https://aws.amazon.com/marketplace/management/tour)에 로그인합니다.

1. [**Assets(자산)**]에서 [**Containers(컨테이너)**]를 선택하여 새 컨테이너 제품을 생성합니다. 제품을 만들면 제품에 대한 제품 코드가 생성되어 컨테이너 이미지와 통합할 수 있습니다. IAM 권한 설정에 대한 자세한 내용은 [AWS Marketplace 측정 및 권한 부여 API 권한](iam-user-policy-for-aws-marketplace-actions.md) 섹션을 참조하세요.

1.  퍼블릭 [AWS Java SDK](https://aws.amazon.com/sdk-for-java/)를 다운로드합니다.
**중요**  
 Amazon EKS에서 측정 APIs를 호출하려면 [지원되는 AWS SDK를 사용하고](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html) Kubernetes 1.13 이상을 실행하는 Amazon EKS 클러스터에서를 실행해야 합니다.

1.  (선택 사항) `RegisterUsage` 작업과 통합하며 디지털 서명 확인을 원한다면 애플리케이션 클래스 경로에서 [BouncyCastle](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on) 서명 확인 라이브러리를 구성해야 합니다.

   또한 JSON 웹 토큰(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 측정 서비스에 연결할 때 일시적인 문제가 발생할 수 있습니다. 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 ECR 또는 Amazon ECR Public처럼 Amazon ECS 또는 Amazon EKS와 호환되는 컨테이너 레지스트리로 푸시합니다. Amazon ECR을 사용하는 경우에는 Amazon ECS 작업 또는 Amazon EKS 포드를 시작하는 계정에 Amazon ECR 리포지토리에 대한 권한이 있는지 확인합니다. 그렇지 않으면 시작되지 않습니다.

1.  다음 코드에 정의된 대로 컨테이너가 `RegisterUsage`를 호출할 수 있는 권한을 부여하는 [IAM](https://aws.amazon.com/iam/) 역할을 생성합니다. Amazon ECS 작업 또는 Amazon EKS 포드 정의의 [작업 역할](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. 와 통합된 컨테이너를 참조 AWS Marketplace 하고 7단계에서 생성한 IAM 역할을 참조하는 Amazon ECS 작업 또는 Amazon EKS 포드 정의를 생성합니다. AWS CloudTrail 로깅을 보려면 작업 정의에서 로깅을 활성화해야 합니다.

1. 작업 또는 포드를 실행할 Amazon ECS 또는 Amazon EKS 클러스터를 생성합니다. 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-1에서 생성한 Amazon ECS 작업 정의 또는 Amazon EKS 포드를 시작합니다 AWS 리전. 제품이 라이브 상태가 되기 전에 이 테스트 프로세스 중에만 이 리전을 사용해야 합니다.

1. `RegisterUsage`에서 유효한 응답을 받은 후에 컨테이너 제품을 생성할 수 있습니다. 궁금한 점은 [AWS Marketplace 판매자 작업](https://aws.amazon.com/marketplace/management/contact-us/) 팀에 문의하세요.

## RegisterUsage Java 예제
<a name="registerusage-java-example"></a>

다음 예제에서는 AWS SDK for Java 및 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();
        }
    }
}
```