본문 바로가기
spring

[APPLE API] App Store Server API 요청을 위한 JWS 토큰

by moonsiri 2021. 12. 23.
728x90
반응형

Generating Tokens for API Requests

 

JWT(JSON Web Token)는 정보를 안전하게 전송하는 방법을 정의하는 공개 표준 (RFC 7519) 입니다.

App Store Service API는 고객의 인앱  구매에 대한 정보를 요청하고 제공하기 위해 서버에서 호출하는 REST API 인데, API에 대한 각 요청을 승인하기 위해 JWT가 필요합니다.

토큰을 생성하고 App Store Connect에서 다운로드한 개인키로 서명합니다.

 

JWS(JSON Web Signature) : JWT header + JWT payload + Sign

- 서버에서 인증을 증거로 인증 정보를 서버의 private key로 서명한 것을 Token한 것

 

 

JWS 구성

JWT Header

App Store Service API와 통신할 JWT를 생성하려면 header에 아래와 같은 필드와 값을 사용해야합니다.

Header Field Value  
alg ES256 App Store Server API용 모든 JWT는 ES256 암호화 알고리즘으로 서명해야함
kid (예: 2X9R4HXF34) App Store Connect에서 다운로드한 개인 키의 Key ID
typ JWT  

 

{
  "alg": "ES256",
  "kid": "2X9R4HXF34",
  "typ": "JWT"
}

 

JWT Payload

payload 부분에는 토큰에 담을 정보가 들어있습니다. 여기에 담는 정보의 한 조각을 claim이라고 부르고, name-value 한 쌍으로 이뤄져있습니다.

클레임의 종류는 크게 세 분류로 나뉘어져있습니다.

  • registered claim : 등록된 클레임은 서비스에서 필요한 정보가 아닌 토큰에 대한 정보를 담기위하여 이름이 이미 정해진 클레임들입니다.
  • public claim : 공개 클레임은 충돌이 방지된(collision-resistant) 이름을 가지고 있어야합니다. 클레임 이름을 URI 형식으로 짓습니다.
  • private claim : 양 측간에 협의하에 사용되는 클레임 이름들을 사용합니다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때 유의해야합니다.

claim type Payload Field Value  
registered iss (issuer) (예: "57246542-96fe-1a63-e053-0824d011072a") App Store Connect의 키 페이지에 있는 발급자 ID
registered iat (issued at) (예: 1623085200) 발행 시간 - UNIX 시간
registered exp (expiration time) (예: 1623086400) 만료 시간
모든 API 요청에 대해 새 토큰을 생성할 필요는 없이 최대 60분 동안 동일한 서명된 토큰을 재사용 권장
registered aud (audience) appstoreconnect-v1 토큰 대상자
private nonce (unique identifier) (예: "6edffe66-b482-11eb-8529-0242ac130003")  
private bid (bundle id) (예: “com.example.testbundleid2021”)  

 

{
  "iss": "57246542-96fe-1a63e053-0824d011072a",
  "iat": 1623085200,
  "exp": 1623086400,
  "aud": "appstoreconnect-v1",
  "nonce": "6edffe66-b482-11eb-8529-0242ac130003",
  "bid": "com.example.testbundleid2021"
}

 

JWT Sign

header에 지정한 키 ID와 연결된 개인 키를 ES256 암호화를 사용하여 토큰에 서명합니다.

 

ES256 : P-256 및 SHA-256을 사용하는 ECDSA 에 대한 JSON WEB Algorithms (JWA). JWT 에서 비대칭키 알고리즘으로 ES256 방식을 권장.

 

 

 

Request Authorization Header에 JWT 포함

 

curl -v -H 'Authorization: Bearer [signed token]' 
"https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{original_transaction_id}"

 

 

 

 

JWS 생성

 

pom.xml

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.7.0</version>
</dependency>

 

JAVA 코드로 JWS 생성

String jws = Jwts.builder()
        // header
        .setHeaderParam("kid", "2X9R4HXF34")
        // payload
        .setIssuer("57246542-96fe-1a63e053-0824d011072a")
        .setIssuedAt(new Date(Calendar.getInstance().getTimeInMillis()))    // 발행 시간 - UNIX 시간
        .setExpiration(new Date(Calendar.getInstance().getTimeInMillis() + (60 * 60 * 1000)))    // 만료 시간 (발행 시간 + 60분)
        .setAudience("appstoreconnect-v1")
        .claim("bid", "com.lingames.app")
        // sign
        .signWith(SignatureAlgorithm.ES256, KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(strPrivateKey))))
    .compact();
 
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + jws);
 
HttpEntity request = new HttpEntity<>(headers);

String API_URL = "https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/%s";
ResponseEntity<Object> res = restTemplate.exchange(String.format(API_URL, originalTransactionId), HttpMethod.PUT, request, Object.class);
 

 

문자열로된 사설키를 사설키 객체로 변환하는 방법

 
// 문자열로된 사설키
String strPrivateKey = "...";
 
// Base64로 디코딩
byte[] decodeBase64 = Base64.decodeBase64(strPrivateKey);
 
// ASN.1으로 기술되어 있는 개인 키를 표현하기 위한 표준인 PKCS#8가 정의한 PrivateKeyInfo 규칙에 따라 개인 키를 DER로 인코딩
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodeBase64);
 
// 타원 곡선(Elliptic Curve) 키 생성
PrivateKey privateKey = KeyFactory.getInstance("EC").generatePrivate(keySpec);​

 

JWS 토큰 구조

https://jwt.io/

# header
{
  "kid": "2X9R4HXF34",
  "alg": "ES256"
}

# payload
{
  "iss": "57246542-96fe-1a63e053-0824d011072a",
  "iat": 1640220713,
  "exp": 1640224313,
  "aud": "appstoreconnect-v1",
  "bid": "com.lingames.app"
}

# verify signature
ECDSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  Public Key in SPKI, PKCS #1, X.509 Certificate, or JWK string format.,
  Private Key in PKCS #8, PKCS #1, or JWK string format. The key never leaves your browser.
)

 

728x90
반응형

댓글