[php]public key(PEM)으로 jwt 검증하는 방법

프로그래밍/서버2020. 4. 24. 22:08

 

 

composer require firebase/php-jwt 명령어를 2번 입력하면.. 설치와 로드?가 됩니다. 구글 소스같은데 좀 마음이 놓이네요.(https://github.com/firebase/php-jwt)

 

차례대로.. 사용한 코드를 하나한 보겠습니다. 애플 아이디로 로그인하기를 구현하면서 진행한 내용입니다.

<?php
require_once '/home/bitnami/vendor/autoload.php';

use CoderCat\JWKToPEM\JWKConverter;
use \Firebase\JWT\JWT;
use \Firebase\JWT\JWK;

$url = 'https://appleid.apple.com/auth/keys';
$jwkSetPure = json_decode(file_get_contents($url), true);//JSON -> Array

$jwkSet = array();
for($i=0; $i<count($jwkSetPure['keys']); $i++){
	$jwkSet[] = $jwkSetPure['keys'][$i];
}

애플에서 제공하는 public keys를 몇 번의 변형을 거쳐야 합니다.

일단 JSON으로 리턴값이 오니 Array로 바꿉니다. 그 다음은 배열에서 ['keys'] 부분을 없애줍니다.

그러면 JWK(Json Web Key) 혹은 JWKS(Json Web Key Set)라고 불리는 것이 완성?됩니다.

 

$jwkConverter = new JWKConverter();
$PEMs = $jwkConverter->multipleToPem($jwkSet);

그리고 JWKS를 PEM형식?의 public key로 변형합니다.

 

print_r($PEMs)로 찍어보면 아래의 값이 나와야 합니다.

Array

(

    [0] => -----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p

GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq

UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg

jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl

8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl

4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL

wQIDAQAB

-----END PUBLIC KEY-----

    [1] => -----BEGIN PUBLIC KEY-----

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4dGQ7bQK8LgILOdLsYzf

ZjkEAoQeVC/aqyc8GC6RX7dq/KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdD

Nq1n52TpxQwI2EqxSk7I9fKPKhRt4F8+2yETlYvye+2s6NeWJim0KBtOVrk0gWvE

Dgd6WOqJl/yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X+Tip84wqwyRpU

lq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll+p/Dg8vAXxJLIJ4SNLcqgFeZe

4OfHLgdzMvxXZJnPp/VgmkcpUdRotazKZumj6dBPcXI/XID4Z4Z3OM1KrZPJNdUh

xwIDAQAB

-----END PUBLIC KEY-----

)

 

list($header, $payload, $signature) = explode (".", $jwt);

$decoded_header = base64_decode($header);
$decoded_header_array = json_decode($decoded_header);

for($i=0; $i<count($jwkSet); $i++){
	$jwk = $jwkSet[$i];
		
	if($jwk['kid'] == $decoded_header_array->kid){
		try{
			$decoded = JWT::decode($jwt, $PEMs[$i], array('RS256'));
			print_r($decoded);
		} catch (Exception $ex) {
			echo 'exception occurred.';
			echo '<br>';
			echo $ex->getMessage();
			echo '<br>';
		}
		
		break;
	}
}

현재 애플에서 제공되는 키가 2개이고.. 애플 로그인시 전달받는 identityToken(jwt)은 이 두 키중 하나로 생성이 됩니다.

일단 jwt가 어떤 key로 생성됐는지는 간단한 몇 가지 과정을 통해 알 수 있습니다.

 

핵심은 JWT는 단순히 base64 encoded 데이터라는 것입니다. 그래서 단순히 base64_decode를 하면.. 원래의 값을 알 수 있습니다.

base64_decode()에 의해 반환되는 값은 단순한 문자열인데 JSON형식입니다. 그래서 json_decode()를 통해.. 친숙한 배열로 변환시켜 줍니다.

 

그리곤 jwkSet을 돌면서.. jwkSet의 kid가.. jwt의 kid와 같은 경우를 발견하면 jwt 검증(verify, validate)를 위해 JWT::decode()함수를 사용합니다.

 

jwt에 문제가 있으면.. 문제에 따라 발생하는 exception이 다릅니다. 간단하게 exception 출력하도록 되어있습니다.

 

 

참고

jwt 디코딩 및 검증 가능한 곳: https://jwt.io/

 

작성자

Posted by 드리머즈

관련 글

댓글 영역